2 * mapcalc - MAP parameter calculation
4 * Author: Steven Barth <cyrus@openwrt.org>
5 * Copyright (c) 2014-2015 cisco Systems, Inc.
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
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.
19 #include <arpa/inet.h>
22 #include <libubox/utils.h>
25 struct blob_attr *dump = NULL;
32 static const struct blobmsg_policy dump_attrs[DUMP_ATTR_MAX] = {
33 [DUMP_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY },
43 static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
44 [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
45 [IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY },
55 static const struct blobmsg_policy prefix_attrs[PREFIX_ATTR_MAX] = {
56 [PREFIX_ATTR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
57 [PREFIX_ATTR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_INT32 },
60 static int bmemcmp(const void *av, const void *bv, size_t bits)
62 const uint8_t *a = av, *b = bv;
63 size_t bytes = bits / 8;
66 int res = memcmp(a, b, bytes);
67 if (res == 0 && bits > 0)
68 res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits));
73 static void bmemcpy(void *av, const void *bv, size_t bits)
76 const uint8_t *b = bv;
78 size_t bytes = bits / 8;
83 uint8_t mask = (1 << (8 - bits)) - 1;
84 a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]);
88 static void bmemcpys64(void *av, const void *bv, size_t frombits, size_t nbits)
91 const uint8_t *b = bv;
92 size_t frombyte = frombits / 8, tobyte = (frombits + nbits) / 8;
94 memcpy(&buf, &b[frombyte], tobyte - frombyte + 1);
95 buf = cpu_to_be64(be64_to_cpu(buf) << (frombits % 8));
97 bmemcpy(av, &buf, nbits);
100 static void handle_dump(struct ubus_request *req __attribute__((unused)),
101 int type __attribute__((unused)), struct blob_attr *msg)
103 struct blob_attr *tb[DUMP_ATTR_INTERFACE];
104 blobmsg_parse(dump_attrs, DUMP_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
106 if (!tb[DUMP_ATTR_INTERFACE])
109 dump = blob_memdup(tb[DUMP_ATTR_INTERFACE]);
128 static char *const token[] = {
131 [OPT_EALEN] = "ealen",
132 [OPT_PREFIX4LEN] = "prefix4len",
133 [OPT_PREFIX6LEN] = "prefix6len",
134 [OPT_IPV6PREFIX] = "ipv6prefix",
135 [OPT_IPV4PREFIX] = "ipv4prefix",
136 [OPT_OFFSET] = "offset",
137 [OPT_PSIDLEN] = "psidlen",
145 int main(int argc, char *argv[])
148 const char *iface = argv[1];
150 const char *legacy_env = getenv("LEGACY");
151 bool legacy = legacy_env && atoi(legacy_env);
155 fprintf(stderr, "Usage: %s <interface|*> <rule1> [rule2] [...]\n", argv[0]);
159 uint32_t network_interface;
160 struct ubus_context *ubus = ubus_connect(NULL);
162 ubus_lookup_id(ubus, "network.interface", &network_interface);
163 ubus_invoke(ubus, network_interface, "dump", NULL, handle_dump, NULL, 5000);
167 for (int i = 2; i < argc; ++i) {
175 struct in_addr ipv4prefix = {INADDR_ANY};
176 struct in_addr ipv4addr = {INADDR_ANY};
177 struct in6_addr ipv6addr = IN6ADDR_ANY_INIT;
178 struct in6_addr ipv6prefix = IN6ADDR_ANY_INIT;
179 struct in6_addr pd = IN6ADDR_ANY_INIT;
184 const char *dmr = NULL;
185 const char *br = NULL;
187 for (char *rule = strdup(argv[i]); *rule; ) {
190 int idx = getsubopt(&rule, token, &value);
193 if (idx == OPT_TYPE) {
194 lw4o6 = (value && !strcmp(value, "lw4o6"));
195 } else if (idx == OPT_FMR) {
197 } else if (idx == OPT_EALEN && (intval = strtoul(value, NULL, 0)) <= 48 && !errno) {
199 } else if (idx == OPT_PREFIX4LEN && (intval = strtoul(value, NULL, 0)) <= 32 && !errno) {
201 } else if (idx == OPT_PREFIX6LEN && (intval = strtoul(value, NULL, 0)) <= 112 && !errno) {
203 } else if (idx == OPT_IPV4PREFIX && inet_pton(AF_INET, value, &ipv4prefix) == 1) {
205 } else if (idx == OPT_IPV6PREFIX && inet_pton(AF_INET6, value, &ipv6prefix) == 1) {
207 } else if (idx == OPT_OFFSET && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
209 } else if (idx == OPT_PSIDLEN && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
211 } else if (idx == OPT_PSID && (intval = strtoul(value, NULL, 0)) <= 65535 && !errno) {
213 } else if (idx == OPT_DMR) {
215 } else if (idx == OPT_BR) {
218 if (idx == -1 || idx >= OPT_MAX)
219 fprintf(stderr, "Skipped invalid option: %s\n", value);
221 fprintf(stderr, "Skipped invalid value %s for option %s\n",
227 offset = (lw4o6) ? 0 : (legacy) ? 4 : 6;
230 struct blob_attr *c, *cur;
232 blobmsg_for_each_attr(c, dump, rem) {
233 struct blob_attr *tb[IFACE_ATTR_MAX];
234 blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c));
236 if (!tb[IFACE_ATTR_INTERFACE] || (strcmp(argv[1], "*") && strcmp(argv[1],
237 blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]))))
240 if ((cur = tb[IFACE_ATTR_PREFIX])) {
241 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, NULL))
246 blobmsg_for_each_attr(d, cur, drem) {
247 struct blob_attr *ptb[PREFIX_ATTR_MAX];
248 blobmsg_parse(prefix_attrs, PREFIX_ATTR_MAX, ptb,
249 blobmsg_data(d), blobmsg_data_len(d));
251 if (!ptb[PREFIX_ATTR_ADDRESS] || !ptb[PREFIX_ATTR_MASK])
254 struct in6_addr prefix = IN6ADDR_ANY_INIT;
255 int mask = blobmsg_get_u32(ptb[PREFIX_ATTR_MASK]);
256 inet_pton(AF_INET6, blobmsg_get_string(ptb[PREFIX_ATTR_ADDRESS]), &prefix);
258 if (mask >= prefix6len && !bmemcmp(&prefix, &ipv6prefix, prefix6len)) {
261 iface = blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]);
271 if (ealen < 0 && pdlen >= 0)
272 ealen = pdlen - prefix6len;
275 psidlen = ealen - (32 - prefix4len);
279 if (psid < 0 && psidlen <= 16 && psidlen >= 0 && pdlen >= 0 && ealen >= psidlen) {
280 bmemcpys64(&psid16, &pd, prefix6len + ealen - psidlen, psidlen);
281 psid = be16_to_cpu(psid16);
284 psid16 = cpu_to_be16(psid >> (16 - psidlen));
286 if ((pdlen >= 0 || ealen == psidlen) && ealen >= psidlen) {
287 bmemcpys64(&ipv4addr, &pd, prefix6len, ealen - psidlen);
288 ipv4addr.s_addr = htonl(ntohl(ipv4addr.s_addr) >> prefix4len);
289 bmemcpy(&ipv4addr, &ipv4prefix, prefix4len);
291 if (prefix4len + ealen < 32)
292 addr4len = prefix4len + ealen;
295 if (prefix4len < 0 || prefix6len < 0 || ealen < 0 || ealen < psidlen) {
296 fprintf(stderr, "Skipping invalid or incomplete rule: %s\n", argv[i]);
301 if (pdlen < 0 && !fmr) {
302 fprintf(stderr, "Skipping non-FMR without matching PD: %s\n", argv[i]);
305 } else if (pdlen >= 0) {
306 size_t v4offset = (legacy) ? 9 : 10;
307 memcpy(&ipv6addr.s6_addr[v4offset], &ipv4addr, 4);
308 memcpy(&ipv6addr.s6_addr[v4offset + 4], &psid16, 2);
309 bmemcpy(&ipv6addr, &pd, pdlen);
313 char ipv4addrbuf[INET_ADDRSTRLEN];
314 char ipv4prefixbuf[INET_ADDRSTRLEN];
315 char ipv6prefixbuf[INET6_ADDRSTRLEN];
316 char ipv6addrbuf[INET6_ADDRSTRLEN];
317 char pdbuf[INET6_ADDRSTRLEN];
319 inet_ntop(AF_INET, &ipv4addr, ipv4addrbuf, sizeof(ipv4addrbuf));
320 inet_ntop(AF_INET, &ipv4prefix, ipv4prefixbuf, sizeof(ipv4prefixbuf));
321 inet_ntop(AF_INET6, &ipv6prefix, ipv6prefixbuf, sizeof(ipv6prefixbuf));
322 inet_ntop(AF_INET6, &ipv6addr, ipv6addrbuf, sizeof(ipv6addrbuf));
323 inet_ntop(AF_INET6, &pd, pdbuf, sizeof(pdbuf));
325 printf("RULE_%d_FMR=%d\n", rulecnt, fmr);
326 printf("RULE_%d_EALEN=%d\n", rulecnt, ealen);
327 printf("RULE_%d_PSIDLEN=%d\n", rulecnt, psidlen);
328 printf("RULE_%d_OFFSET=%d\n", rulecnt, offset);
329 printf("RULE_%d_PREFIX4LEN=%d\n", rulecnt, prefix4len);
330 printf("RULE_%d_PREFIX6LEN=%d\n", rulecnt, prefix6len);
331 printf("RULE_%d_IPV4PREFIX=%s\n", rulecnt, ipv4prefixbuf);
332 printf("RULE_%d_IPV6PREFIX=%s\n", rulecnt, ipv6prefixbuf);
335 printf("RULE_%d_IPV6PD=%s\n", rulecnt, pdbuf);
336 printf("RULE_%d_PD6LEN=%d\n", rulecnt, pdlen);
337 printf("RULE_%d_PD6IFACE=%s\n", rulecnt, iface);
338 printf("RULE_%d_IPV6ADDR=%s\n", rulecnt, ipv6addrbuf);
339 printf("RULE_BMR=%d\n", rulecnt);
342 if (ipv4addr.s_addr) {
343 printf("RULE_%d_IPV4ADDR=%s\n", rulecnt, ipv4addrbuf);
344 printf("RULE_%d_ADDR4LEN=%d\n", rulecnt, addr4len);
348 if (psidlen > 0 && psid >= 0) {
349 printf("RULE_%d_PORTSETS='", rulecnt);
350 for (int k = (offset) ? 1 : 0; k < (1 << offset); ++k) {
351 int start = (k << (16 - offset)) | (psid >> offset);
352 int end = start + (1 << (16 - offset - psidlen)) - 1;
358 printf("%d-%d ", start, end);
364 printf("RULE_%d_DMR=%s\n", rulecnt, dmr);
367 printf("RULE_%d_BR=%s\n", rulecnt, br);
370 printf("RULE_COUNT=%d\n", rulecnt);