map: add support for lw4o6 address matching, minor optimizations
[openwrt/openwrt.git] / package / network / ipv6 / map / src / mapcalc.c
1 /*
2 * mapcalc - MAP parameter calculation
3 *
4 * Author: Steven Barth <cyrus@openwrt.org>
5 * Copyright (c) 2014-2015 cisco Systems, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <arpa/inet.h>
20 #include <errno.h>
21 #include <libubus.h>
22 #include <libubox/utils.h>
23
24
25 struct blob_attr *dump = NULL;
26
27 enum {
28 DUMP_ATTR_INTERFACE,
29 DUMP_ATTR_MAX
30 };
31
32 static const struct blobmsg_policy dump_attrs[DUMP_ATTR_MAX] = {
33 [DUMP_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY },
34 };
35
36
37 enum {
38 IFACE_ATTR_INTERFACE,
39 IFACE_ATTR_PREFIX,
40 IFACE_ATTR_ADDRESS,
41 IFACE_ATTR_MAX,
42 };
43
44 static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
45 [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
46 [IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY },
47 [IFACE_ATTR_ADDRESS] = { .name = "ipv6-address", .type = BLOBMSG_TYPE_ARRAY },
48 };
49
50
51 enum {
52 PREFIX_ATTR_ADDRESS,
53 PREFIX_ATTR_MASK,
54 PREFIX_ATTR_MAX,
55 };
56
57 static const struct blobmsg_policy prefix_attrs[PREFIX_ATTR_MAX] = {
58 [PREFIX_ATTR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
59 [PREFIX_ATTR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_INT32 },
60 };
61
62 static int bmemcmp(const void *av, const void *bv, size_t bits)
63 {
64 const uint8_t *a = av, *b = bv;
65 size_t bytes = bits / 8;
66 bits %= 8;
67
68 int res = memcmp(a, b, bytes);
69 if (res == 0 && bits > 0)
70 res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits));
71
72 return res;
73 }
74
75 static void bmemcpy(void *av, const void *bv, size_t bits)
76 {
77 uint8_t *a = av;
78 const uint8_t *b = bv;
79
80 size_t bytes = bits / 8;
81 bits %= 8;
82 memcpy(a, b, bytes);
83
84 if (bits > 0) {
85 uint8_t mask = (1 << (8 - bits)) - 1;
86 a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]);
87 }
88 }
89
90 static void bmemcpys64(void *av, const void *bv, size_t frombits, size_t nbits)
91 {
92 uint64_t buf = 0;
93 const uint8_t *b = bv;
94 size_t frombyte = frombits / 8, tobyte = (frombits + nbits) / 8;
95
96 memcpy(&buf, &b[frombyte], tobyte - frombyte + 1);
97 buf = cpu_to_be64(be64_to_cpu(buf) << (frombits % 8));
98
99 bmemcpy(av, &buf, nbits);
100 }
101
102 static void handle_dump(struct ubus_request *req __attribute__((unused)),
103 int type __attribute__((unused)), struct blob_attr *msg)
104 {
105 struct blob_attr *tb[DUMP_ATTR_INTERFACE];
106 blobmsg_parse(dump_attrs, DUMP_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
107
108 if (!tb[DUMP_ATTR_INTERFACE])
109 return;
110
111 dump = blob_memdup(tb[DUMP_ATTR_INTERFACE]);
112 }
113
114 static void match_prefix(int *pdlen, struct in6_addr *pd, struct blob_attr *cur,
115 const struct in6_addr *ipv6prefix, int prefix6len)
116 {
117 struct blob_attr *d;
118 unsigned drem;
119
120 if (!cur || blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, NULL))
121 return;
122
123 blobmsg_for_each_attr(d, cur, drem) {
124 struct blob_attr *ptb[PREFIX_ATTR_MAX];
125 blobmsg_parse(prefix_attrs, PREFIX_ATTR_MAX, ptb,
126 blobmsg_data(d), blobmsg_data_len(d));
127
128 if (!ptb[PREFIX_ATTR_ADDRESS] || !ptb[PREFIX_ATTR_MASK])
129 continue;
130
131 struct in6_addr prefix = IN6ADDR_ANY_INIT;
132 int mask = blobmsg_get_u32(ptb[PREFIX_ATTR_MASK]);
133 inet_pton(AF_INET6, blobmsg_get_string(ptb[PREFIX_ATTR_ADDRESS]), &prefix);
134
135 // lw4over6 /128-address-as-PD matching madness workaround
136 if (mask == 128)
137 mask = 64;
138
139 if (*pdlen < mask && mask >= prefix6len &&
140 !bmemcmp(&prefix, ipv6prefix, prefix6len)) {
141 bmemcpy(pd, &prefix, mask);
142 *pdlen = mask;
143 }
144 }
145 }
146
147 enum {
148 OPT_TYPE,
149 OPT_FMR,
150 OPT_EALEN,
151 OPT_PREFIX4LEN,
152 OPT_PREFIX6LEN,
153 OPT_IPV6PREFIX,
154 OPT_IPV4PREFIX,
155 OPT_OFFSET,
156 OPT_PSIDLEN,
157 OPT_PSID,
158 OPT_BR,
159 OPT_DMR,
160 OPT_MAX
161 };
162
163 static char *const token[] = {
164 [OPT_TYPE] = "type",
165 [OPT_FMR] = "fmr",
166 [OPT_EALEN] = "ealen",
167 [OPT_PREFIX4LEN] = "prefix4len",
168 [OPT_PREFIX6LEN] = "prefix6len",
169 [OPT_IPV6PREFIX] = "ipv6prefix",
170 [OPT_IPV4PREFIX] = "ipv4prefix",
171 [OPT_OFFSET] = "offset",
172 [OPT_PSIDLEN] = "psidlen",
173 [OPT_PSID] = "psid",
174 [OPT_BR] = "br",
175 [OPT_DMR] = "dmr",
176 [OPT_MAX] = NULL
177 };
178
179
180 int main(int argc, char *argv[])
181 {
182 int status = 0;
183 const char *iface = argv[1];
184
185 const char *legacy_env = getenv("LEGACY");
186 bool legacy = legacy_env && atoi(legacy_env);
187
188
189 if (argc < 3) {
190 fprintf(stderr, "Usage: %s <interface|*> <rule1> [rule2] [...]\n", argv[0]);
191 return 1;
192 }
193
194 uint32_t network_interface;
195 struct ubus_context *ubus = ubus_connect(NULL);
196 if (ubus) {
197 ubus_lookup_id(ubus, "network.interface", &network_interface);
198 ubus_invoke(ubus, network_interface, "dump", NULL, handle_dump, NULL, 5000);
199 }
200
201 int rulecnt = 0;
202 for (int i = 2; i < argc; ++i) {
203 bool lw4o6 = false;
204 bool fmr = false;
205 int ealen = -1;
206 int addr4len = 32;
207 int prefix4len = 32;
208 int prefix6len = -1;
209 int pdlen = -1;
210 struct in_addr ipv4prefix = {INADDR_ANY};
211 struct in_addr ipv4addr = {INADDR_ANY};
212 struct in6_addr ipv6addr = IN6ADDR_ANY_INIT;
213 struct in6_addr ipv6prefix = IN6ADDR_ANY_INIT;
214 struct in6_addr pd = IN6ADDR_ANY_INIT;
215 int offset = -1;
216 int psidlen = -1;
217 int psid = -1;
218 uint16_t psid16 = 0;
219 const char *dmr = NULL;
220 const char *br = NULL;
221
222 for (char *rule = strdup(argv[i]); *rule; ) {
223 char *value;
224 int intval;
225 int idx = getsubopt(&rule, token, &value);
226 errno = 0;
227
228 if (idx == OPT_TYPE) {
229 lw4o6 = (value && !strcmp(value, "lw4o6"));
230 } else if (idx == OPT_FMR) {
231 fmr = true;
232 } else if (idx == OPT_EALEN && (intval = strtoul(value, NULL, 0)) <= 48 && !errno) {
233 ealen = intval;
234 } else if (idx == OPT_PREFIX4LEN && (intval = strtoul(value, NULL, 0)) <= 32 && !errno) {
235 prefix4len = intval;
236 } else if (idx == OPT_PREFIX6LEN && (intval = strtoul(value, NULL, 0)) <= 112 && !errno) {
237 prefix6len = intval;
238 } else if (idx == OPT_IPV4PREFIX && inet_pton(AF_INET, value, &ipv4prefix) == 1) {
239 // dummy
240 } else if (idx == OPT_IPV6PREFIX && inet_pton(AF_INET6, value, &ipv6prefix) == 1) {
241 // dummy
242 } else if (idx == OPT_OFFSET && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
243 offset = intval;
244 } else if (idx == OPT_PSIDLEN && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
245 psidlen = intval;
246 } else if (idx == OPT_PSID && (intval = strtoul(value, NULL, 0)) <= 65535 && !errno) {
247 psid = intval;
248 } else if (idx == OPT_DMR) {
249 dmr = value;
250 } else if (idx == OPT_BR) {
251 br = value;
252 } else {
253 if (idx == -1 || idx >= OPT_MAX)
254 fprintf(stderr, "Skipped invalid option: %s\n", value);
255 else
256 fprintf(stderr, "Skipped invalid value %s for option %s\n",
257 value, token[idx]);
258 }
259 }
260
261 if (offset < 0)
262 offset = (lw4o6) ? 0 : (legacy) ? 4 : 6;
263
264 // LW4over6 doesn't have an EALEN and has no psid-autodetect
265 if (lw4o6) {
266 if (psidlen < 0)
267 psidlen = 0;
268
269 ealen = psidlen;
270 }
271
272 // Find PD
273 struct blob_attr *c;
274 unsigned rem;
275 blobmsg_for_each_attr(c, dump, rem) {
276 struct blob_attr *tb[IFACE_ATTR_MAX];
277 blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c));
278
279 if (!tb[IFACE_ATTR_INTERFACE] || (strcmp(argv[1], "*") && strcmp(argv[1],
280 blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]))))
281 continue;
282
283 match_prefix(&pdlen, &pd, tb[IFACE_ATTR_PREFIX], &ipv6prefix, prefix6len);
284
285 if (lw4o6)
286 match_prefix(&pdlen, &pd, tb[IFACE_ATTR_ADDRESS], &ipv6prefix, prefix6len);
287
288 if (pdlen >= 0) {
289 iface = blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]);
290 break;
291 }
292 }
293
294 if (ealen < 0 && pdlen >= 0)
295 ealen = pdlen - prefix6len;
296
297 if (psidlen <= 0) {
298 psidlen = ealen - (32 - prefix4len);
299 psid = -1;
300 }
301
302 if (psid < 0 && psidlen <= 16 && psidlen >= 0 && pdlen >= 0 && ealen >= psidlen) {
303 bmemcpys64(&psid16, &pd, prefix6len + ealen - psidlen, psidlen);
304 psid = be16_to_cpu(psid16);
305 }
306
307 psid16 = cpu_to_be16(psid >> (16 - psidlen));
308
309 if (prefix4len < 0 || prefix6len < 0 || ealen < 0 || ealen < psidlen) {
310 fprintf(stderr, "Skipping invalid or incomplete rule: %s\n", argv[i]);
311 status = 1;
312 continue;
313 }
314
315 if ((pdlen >= 0 || ealen == psidlen) && ealen >= psidlen) {
316 bmemcpys64(&ipv4addr, &pd, prefix6len, ealen - psidlen);
317 ipv4addr.s_addr = htonl(ntohl(ipv4addr.s_addr) >> prefix4len);
318 bmemcpy(&ipv4addr, &ipv4prefix, prefix4len);
319
320 if (prefix4len + ealen < 32)
321 addr4len = prefix4len + ealen;
322 }
323
324 if (pdlen < 0 && !fmr) {
325 fprintf(stderr, "Skipping non-FMR without matching PD: %s\n", argv[i]);
326 status = 1;
327 continue;
328 } else if (pdlen >= 0) {
329 size_t v4offset = (legacy) ? 9 : 10;
330 memcpy(&ipv6addr.s6_addr[v4offset], &ipv4addr, 4);
331 memcpy(&ipv6addr.s6_addr[v4offset + 4], &psid16, 2);
332 bmemcpy(&ipv6addr, &pd, pdlen);
333 }
334
335 ++rulecnt;
336 char ipv4addrbuf[INET_ADDRSTRLEN];
337 char ipv4prefixbuf[INET_ADDRSTRLEN];
338 char ipv6prefixbuf[INET6_ADDRSTRLEN];
339 char ipv6addrbuf[INET6_ADDRSTRLEN];
340 char pdbuf[INET6_ADDRSTRLEN];
341
342 inet_ntop(AF_INET, &ipv4addr, ipv4addrbuf, sizeof(ipv4addrbuf));
343 inet_ntop(AF_INET, &ipv4prefix, ipv4prefixbuf, sizeof(ipv4prefixbuf));
344 inet_ntop(AF_INET6, &ipv6prefix, ipv6prefixbuf, sizeof(ipv6prefixbuf));
345 inet_ntop(AF_INET6, &ipv6addr, ipv6addrbuf, sizeof(ipv6addrbuf));
346 inet_ntop(AF_INET6, &pd, pdbuf, sizeof(pdbuf));
347
348 printf("RULE_%d_FMR=%d\n", rulecnt, fmr);
349 printf("RULE_%d_EALEN=%d\n", rulecnt, ealen);
350 printf("RULE_%d_PSIDLEN=%d\n", rulecnt, psidlen);
351 printf("RULE_%d_OFFSET=%d\n", rulecnt, offset);
352 printf("RULE_%d_PREFIX4LEN=%d\n", rulecnt, prefix4len);
353 printf("RULE_%d_PREFIX6LEN=%d\n", rulecnt, prefix6len);
354 printf("RULE_%d_IPV4PREFIX=%s\n", rulecnt, ipv4prefixbuf);
355 printf("RULE_%d_IPV6PREFIX=%s\n", rulecnt, ipv6prefixbuf);
356
357 if (pdlen >= 0) {
358 printf("RULE_%d_IPV6PD=%s\n", rulecnt, pdbuf);
359 printf("RULE_%d_PD6LEN=%d\n", rulecnt, pdlen);
360 printf("RULE_%d_PD6IFACE=%s\n", rulecnt, iface);
361 printf("RULE_%d_IPV6ADDR=%s\n", rulecnt, ipv6addrbuf);
362 printf("RULE_BMR=%d\n", rulecnt);
363 }
364
365 if (ipv4addr.s_addr) {
366 printf("RULE_%d_IPV4ADDR=%s\n", rulecnt, ipv4addrbuf);
367 printf("RULE_%d_ADDR4LEN=%d\n", rulecnt, addr4len);
368 }
369
370
371 if (psidlen > 0 && psid >= 0) {
372 printf("RULE_%d_PORTSETS='", rulecnt);
373 for (int k = (offset) ? 1 : 0; k < (1 << offset); ++k) {
374 int start = (k << (16 - offset)) | (psid >> offset);
375 int end = start + (1 << (16 - offset - psidlen)) - 1;
376
377 if (start == 0)
378 start = 1;
379
380 if (start <= end)
381 printf("%d-%d ", start, end);
382 }
383 printf("'\n");
384 }
385
386 if (dmr)
387 printf("RULE_%d_DMR=%s\n", rulecnt, dmr);
388
389 if (br)
390 printf("RULE_%d_BR=%s\n", rulecnt, br);
391 }
392
393 printf("RULE_COUNT=%d\n", rulecnt);
394 return status;
395 }