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
},
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
},
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
},
62 static int bmemcmp(const void *av
, const void *bv
, size_t bits
)
64 const uint8_t *a
= av
, *b
= bv
;
65 size_t bytes
= bits
/ 8;
68 int res
= memcmp(a
, b
, bytes
);
69 if (res
== 0 && bits
> 0)
70 res
= (a
[bytes
] >> (8 - bits
)) - (b
[bytes
] >> (8 - bits
));
75 static void bmemcpy(void *av
, const void *bv
, size_t bits
)
78 const uint8_t *b
= bv
;
80 size_t bytes
= bits
/ 8;
85 uint8_t mask
= (1 << (8 - bits
)) - 1;
86 a
[bytes
] = (a
[bytes
] & mask
) | ((~mask
) & b
[bytes
]);
90 static void bmemcpys64(void *av
, const void *bv
, size_t frombits
, size_t nbits
)
93 const uint8_t *b
= bv
;
94 size_t frombyte
= frombits
/ 8, tobyte
= (frombits
+ nbits
) / 8;
96 memcpy(&buf
, &b
[frombyte
], tobyte
- frombyte
+ 1);
97 buf
= cpu_to_be64(be64_to_cpu(buf
) << (frombits
% 8));
99 bmemcpy(av
, &buf
, nbits
);
102 static void handle_dump(struct ubus_request
*req
__attribute__((unused
)),
103 int type
__attribute__((unused
)), struct blob_attr
*msg
)
105 struct blob_attr
*tb
[DUMP_ATTR_INTERFACE
];
106 blobmsg_parse(dump_attrs
, DUMP_ATTR_MAX
, tb
, blob_data(msg
), blob_len(msg
));
108 if (!tb
[DUMP_ATTR_INTERFACE
])
111 dump
= blob_memdup(tb
[DUMP_ATTR_INTERFACE
]);
114 static void match_prefix(int *pdlen
, struct in6_addr
*pd
, struct blob_attr
*cur
,
115 const struct in6_addr
*ipv6prefix
, int prefix6len
)
120 if (!cur
|| blobmsg_type(cur
) != BLOBMSG_TYPE_ARRAY
|| !blobmsg_check_attr(cur
, NULL
))
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
));
128 if (!ptb
[PREFIX_ATTR_ADDRESS
] || !ptb
[PREFIX_ATTR_MASK
])
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
);
135 // lw4over6 /128-address-as-PD matching madness workaround
139 if (*pdlen
< mask
&& mask
>= prefix6len
&&
140 !bmemcmp(&prefix
, ipv6prefix
, prefix6len
)) {
141 bmemcpy(pd
, &prefix
, mask
);
165 static char *const token
[] = {
168 [OPT_EALEN
] = "ealen",
169 [OPT_PREFIX4LEN
] = "prefix4len",
170 [OPT_PREFIX6LEN
] = "prefix6len",
171 [OPT_IPV6PREFIX
] = "ipv6prefix",
172 [OPT_IPV4PREFIX
] = "ipv4prefix",
173 [OPT_OFFSET
] = "offset",
174 [OPT_PSIDLEN
] = "psidlen",
179 [OPT_PDLEN
] = "pdlen",
184 int main(int argc
, char *argv
[])
187 const char *iface
= argv
[1];
189 const char *legacy_env
= getenv("LEGACY");
190 bool legacy
= legacy_env
&& atoi(legacy_env
);
194 fprintf(stderr
, "Usage: %s <interface|*> <rule1> [rule2] [...]\n", argv
[0]);
198 uint32_t network_interface
;
199 struct ubus_context
*ubus
= ubus_connect(NULL
);
201 ubus_lookup_id(ubus
, "network.interface", &network_interface
);
202 ubus_invoke(ubus
, network_interface
, "dump", NULL
, handle_dump
, NULL
, 5000);
206 for (int i
= 2; i
< argc
; ++i
) {
214 struct in_addr ipv4prefix
= {INADDR_ANY
};
215 struct in_addr ipv4addr
= {INADDR_ANY
};
216 struct in6_addr ipv6addr
= IN6ADDR_ANY_INIT
;
217 struct in6_addr ipv6prefix
= IN6ADDR_ANY_INIT
;
218 struct in6_addr pd
= IN6ADDR_ANY_INIT
;
223 const char *dmr
= NULL
;
224 const char *br
= NULL
;
226 for (char *rule
= strdup(argv
[i
]); *rule
; ) {
229 int idx
= getsubopt(&rule
, token
, &value
);
232 if (idx
== OPT_TYPE
) {
233 lw4o6
= (value
&& !strcmp(value
, "lw4o6"));
234 } else if (idx
== OPT_FMR
) {
236 } else if (idx
== OPT_EALEN
&& (intval
= strtoul(value
, NULL
, 0)) <= 48 && !errno
) {
238 } else if (idx
== OPT_PREFIX4LEN
&& (intval
= strtoul(value
, NULL
, 0)) <= 32 && !errno
) {
240 } else if (idx
== OPT_PREFIX6LEN
&& (intval
= strtoul(value
, NULL
, 0)) <= 112 && !errno
) {
242 } else if (idx
== OPT_IPV4PREFIX
&& inet_pton(AF_INET
, value
, &ipv4prefix
) == 1) {
244 } else if (idx
== OPT_IPV6PREFIX
&& inet_pton(AF_INET6
, value
, &ipv6prefix
) == 1) {
246 } else if (idx
== OPT_PD
&& inet_pton(AF_INET6
, value
, &pd
) == 1) {
248 } else if (idx
== OPT_OFFSET
&& (intval
= strtoul(value
, NULL
, 0)) <= 16 && !errno
) {
250 } else if (idx
== OPT_PSIDLEN
&& (intval
= strtoul(value
, NULL
, 0)) <= 16 && !errno
) {
252 } else if (idx
== OPT_PDLEN
&& (intval
= strtoul(value
, NULL
, 0)) <= 128 && !errno
) {
254 } else if (idx
== OPT_PSID
&& (intval
= strtoul(value
, NULL
, 0)) <= 65535 && !errno
) {
256 } else if (idx
== OPT_DMR
) {
258 } else if (idx
== OPT_BR
) {
261 if (idx
== -1 || idx
>= OPT_MAX
)
262 fprintf(stderr
, "Skipped invalid option: %s\n", value
);
264 fprintf(stderr
, "Skipped invalid value %s for option %s\n",
270 offset
= (lw4o6
) ? 0 : (legacy
) ? 4 : 6;
272 // LW4over6 doesn't have an EALEN and has no psid-autodetect
284 blobmsg_for_each_attr(c
, dump
, rem
) {
285 struct blob_attr
*tb
[IFACE_ATTR_MAX
];
286 blobmsg_parse(iface_attrs
, IFACE_ATTR_MAX
, tb
, blobmsg_data(c
), blobmsg_data_len(c
));
288 if (!tb
[IFACE_ATTR_INTERFACE
] || (strcmp(argv
[1], "*") && strcmp(argv
[1],
289 blobmsg_get_string(tb
[IFACE_ATTR_INTERFACE
]))))
292 match_prefix(&pdlen
, &pd
, tb
[IFACE_ATTR_PREFIX
], &ipv6prefix
, prefix6len
);
295 match_prefix(&pdlen
, &pd
, tb
[IFACE_ATTR_ADDRESS
], &ipv6prefix
, prefix6len
);
298 iface
= blobmsg_get_string(tb
[IFACE_ATTR_INTERFACE
]);
304 if (ealen
< 0 && pdlen
>= 0)
305 ealen
= pdlen
- prefix6len
;
308 psidlen
= ealen
- (32 - prefix4len
);
312 if (psid
< 0 && psidlen
<= 16 && psidlen
>= 0 && pdlen
>= 0 && ealen
>= psidlen
) {
313 bmemcpys64(&psid16
, &pd
, prefix6len
+ ealen
- psidlen
, psidlen
);
314 psid
= be16_to_cpu(psid16
);
317 psid16
= cpu_to_be16(psid
>> (16 - psidlen
));
319 if (prefix4len
< 0 || prefix6len
< 0 || ealen
< 0 || ealen
< psidlen
) {
320 fprintf(stderr
, "Skipping invalid or incomplete rule: %s\n", argv
[i
]);
325 if ((pdlen
>= 0 || ealen
== psidlen
) && ealen
>= psidlen
) {
326 bmemcpys64(&ipv4addr
, &pd
, prefix6len
, ealen
- psidlen
);
327 ipv4addr
.s_addr
= htonl(ntohl(ipv4addr
.s_addr
) >> prefix4len
);
328 bmemcpy(&ipv4addr
, &ipv4prefix
, prefix4len
);
330 if (prefix4len
+ ealen
< 32)
331 addr4len
= prefix4len
+ ealen
;
334 if (pdlen
< 0 && !fmr
) {
335 fprintf(stderr
, "Skipping non-FMR without matching PD: %s\n", argv
[i
]);
338 } else if (pdlen
>= 0) {
339 size_t v4offset
= (legacy
) ? 9 : 10;
340 memcpy(&ipv6addr
.s6_addr
[v4offset
], &ipv4addr
, 4);
341 memcpy(&ipv6addr
.s6_addr
[v4offset
+ 4], &psid16
, 2);
342 bmemcpy(&ipv6addr
, &pd
, pdlen
);
346 char ipv4addrbuf
[INET_ADDRSTRLEN
];
347 char ipv4prefixbuf
[INET_ADDRSTRLEN
];
348 char ipv6prefixbuf
[INET6_ADDRSTRLEN
];
349 char ipv6addrbuf
[INET6_ADDRSTRLEN
];
350 char pdbuf
[INET6_ADDRSTRLEN
];
352 inet_ntop(AF_INET
, &ipv4addr
, ipv4addrbuf
, sizeof(ipv4addrbuf
));
353 inet_ntop(AF_INET
, &ipv4prefix
, ipv4prefixbuf
, sizeof(ipv4prefixbuf
));
354 inet_ntop(AF_INET6
, &ipv6prefix
, ipv6prefixbuf
, sizeof(ipv6prefixbuf
));
355 inet_ntop(AF_INET6
, &ipv6addr
, ipv6addrbuf
, sizeof(ipv6addrbuf
));
356 inet_ntop(AF_INET6
, &pd
, pdbuf
, sizeof(pdbuf
));
358 printf("RULE_%d_FMR=%d\n", rulecnt
, fmr
);
359 printf("RULE_%d_EALEN=%d\n", rulecnt
, ealen
);
360 printf("RULE_%d_PSIDLEN=%d\n", rulecnt
, psidlen
);
361 printf("RULE_%d_OFFSET=%d\n", rulecnt
, offset
);
362 printf("RULE_%d_PREFIX4LEN=%d\n", rulecnt
, prefix4len
);
363 printf("RULE_%d_PREFIX6LEN=%d\n", rulecnt
, prefix6len
);
364 printf("RULE_%d_IPV4PREFIX=%s\n", rulecnt
, ipv4prefixbuf
);
365 printf("RULE_%d_IPV6PREFIX=%s\n", rulecnt
, ipv6prefixbuf
);
368 printf("RULE_%d_IPV6PD=%s\n", rulecnt
, pdbuf
);
369 printf("RULE_%d_PD6LEN=%d\n", rulecnt
, pdlen
);
370 printf("RULE_%d_PD6IFACE=%s\n", rulecnt
, iface
);
371 printf("RULE_%d_IPV6ADDR=%s\n", rulecnt
, ipv6addrbuf
);
372 printf("RULE_BMR=%d\n", rulecnt
);
375 if (ipv4addr
.s_addr
) {
376 printf("RULE_%d_IPV4ADDR=%s\n", rulecnt
, ipv4addrbuf
);
377 printf("RULE_%d_ADDR4LEN=%d\n", rulecnt
, addr4len
);
381 if (psidlen
> 0 && psid
>= 0) {
382 printf("RULE_%d_PORTSETS='", rulecnt
);
383 for (int k
= (offset
) ? 1 : 0; k
< (1 << offset
); ++k
) {
384 int start
= (k
<< (16 - offset
)) | (psid
>> offset
);
385 int end
= start
+ (1 << (16 - offset
- psidlen
)) - 1;
391 printf("%d-%d ", start
, end
);
397 printf("RULE_%d_DMR=%s\n", rulecnt
, dmr
);
400 printf("RULE_%d_BR=%s\n", rulecnt
, br
);
403 printf("RULE_COUNT=%d\n", rulecnt
);