123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- /*
- * mapcalc - MAP parameter calculation
- *
- * Author: Steven Barth <cyrus@openwrt.org>
- * Copyright (c) 2014-2015 cisco Systems, Inc.
- * Copyright (c) 2015 Steven Barth <cyrus@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <errno.h>
- #include <libubus.h>
- #include <libubox/utils.h>
- struct blob_attr *dump = NULL;
- enum {
- DUMP_ATTR_INTERFACE,
- DUMP_ATTR_MAX
- };
- static const struct blobmsg_policy dump_attrs[DUMP_ATTR_MAX] = {
- [DUMP_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY },
- };
- enum {
- IFACE_ATTR_INTERFACE,
- IFACE_ATTR_PREFIX,
- IFACE_ATTR_ADDRESS,
- IFACE_ATTR_MAX,
- };
- static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
- [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
- [IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY },
- [IFACE_ATTR_ADDRESS] = { .name = "ipv6-address", .type = BLOBMSG_TYPE_ARRAY },
- };
- enum {
- PREFIX_ATTR_ADDRESS,
- PREFIX_ATTR_MASK,
- PREFIX_ATTR_MAX,
- };
- static const struct blobmsg_policy prefix_attrs[PREFIX_ATTR_MAX] = {
- [PREFIX_ATTR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
- [PREFIX_ATTR_MASK] = { .name = "mask", .type = BLOBMSG_TYPE_INT32 },
- };
- static int bmemcmp(const void *av, const void *bv, size_t bits)
- {
- const uint8_t *a = av, *b = bv;
- size_t bytes = bits / 8;
- bits %= 8;
- int res = memcmp(a, b, bytes);
- if (res == 0 && bits > 0)
- res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits));
- return res;
- }
- static void bmemcpy(void *av, const void *bv, size_t bits)
- {
- uint8_t *a = av;
- const uint8_t *b = bv;
- size_t bytes = bits / 8;
- bits %= 8;
- memcpy(a, b, bytes);
- if (bits > 0) {
- uint8_t mask = (1 << (8 - bits)) - 1;
- a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]);
- }
- }
- static void bmemcpys64(void *av, const void *bv, size_t frombits, size_t nbits)
- {
- uint64_t buf = 0;
- const uint8_t *b = bv;
- size_t frombyte = frombits / 8, tobyte = (frombits + nbits) / 8;
- memcpy(&buf, &b[frombyte], tobyte - frombyte + 1);
- buf = cpu_to_be64(be64_to_cpu(buf) << (frombits % 8));
- bmemcpy(av, &buf, nbits);
- }
- static void handle_dump(struct ubus_request *req __attribute__((unused)),
- int type __attribute__((unused)), struct blob_attr *msg)
- {
- struct blob_attr *tb[DUMP_ATTR_INTERFACE];
- blobmsg_parse(dump_attrs, DUMP_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
- if (!tb[DUMP_ATTR_INTERFACE])
- return;
- dump = blob_memdup(tb[DUMP_ATTR_INTERFACE]);
- }
- static void match_prefix(int *pdlen, struct in6_addr *pd, struct blob_attr *cur,
- const struct in6_addr *ipv6prefix, int prefix6len, bool lw4o6)
- {
- struct blob_attr *d;
- unsigned drem;
- if (!cur || blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, NULL))
- return;
- blobmsg_for_each_attr(d, cur, drem) {
- struct blob_attr *ptb[PREFIX_ATTR_MAX];
- blobmsg_parse(prefix_attrs, PREFIX_ATTR_MAX, ptb,
- blobmsg_data(d), blobmsg_data_len(d));
- if (!ptb[PREFIX_ATTR_ADDRESS] || !ptb[PREFIX_ATTR_MASK])
- continue;
- struct in6_addr prefix = IN6ADDR_ANY_INIT;
- int mask = blobmsg_get_u32(ptb[PREFIX_ATTR_MASK]);
- inet_pton(AF_INET6, blobmsg_get_string(ptb[PREFIX_ATTR_ADDRESS]), &prefix);
- // lw4over6 /128-address-as-PD matching madness workaround
- if (lw4o6 && mask == 128)
- mask = 64;
- if (*pdlen < mask && mask >= prefix6len &&
- !bmemcmp(&prefix, ipv6prefix, prefix6len)) {
- bmemcpy(pd, &prefix, mask);
- *pdlen = mask;
- } else if (lw4o6 && *pdlen < prefix6len && mask < prefix6len &&
- !bmemcmp(&prefix, ipv6prefix, mask)) {
- bmemcpy(pd, ipv6prefix, prefix6len);
- *pdlen = prefix6len;
- }
- }
- }
- enum {
- OPT_TYPE,
- OPT_FMR,
- OPT_EALEN,
- OPT_PREFIX4LEN,
- OPT_PREFIX6LEN,
- OPT_IPV6PREFIX,
- OPT_IPV4PREFIX,
- OPT_OFFSET,
- OPT_PSIDLEN,
- OPT_PSID,
- OPT_BR,
- OPT_DMR,
- OPT_PD,
- OPT_PDLEN,
- OPT_MAX
- };
- static char *const token[] = {
- [OPT_TYPE] = "type",
- [OPT_FMR] = "fmr",
- [OPT_EALEN] = "ealen",
- [OPT_PREFIX4LEN] = "prefix4len",
- [OPT_PREFIX6LEN] = "prefix6len",
- [OPT_IPV6PREFIX] = "ipv6prefix",
- [OPT_IPV4PREFIX] = "ipv4prefix",
- [OPT_OFFSET] = "offset",
- [OPT_PSIDLEN] = "psidlen",
- [OPT_PSID] = "psid",
- [OPT_BR] = "br",
- [OPT_DMR] = "dmr",
- [OPT_PD] = "pd",
- [OPT_PDLEN] = "pdlen",
- [OPT_MAX] = NULL
- };
- int main(int argc, char *argv[])
- {
- int status = 0;
- const char *iface = argv[1];
- const char *legacy_env = getenv("LEGACY");
- bool legacy = legacy_env && atoi(legacy_env);
- if (argc < 3) {
- fprintf(stderr, "Usage: %s <interface|*> <rule1> [rule2] [...]\n", argv[0]);
- return 1;
- }
- uint32_t network_interface;
- struct ubus_context *ubus = ubus_connect(NULL);
- if (ubus) {
- ubus_lookup_id(ubus, "network.interface", &network_interface);
- ubus_invoke(ubus, network_interface, "dump", NULL, handle_dump, NULL, 5000);
- }
- int rulecnt = 0;
- for (int i = 2; i < argc; ++i) {
- bool lw4o6 = false;
- bool fmr = false;
- int ealen = -1;
- int addr4len = 32;
- int prefix4len = 32;
- int prefix6len = -1;
- int pdlen = -1;
- struct in_addr ipv4prefix = {INADDR_ANY};
- struct in_addr ipv4addr = {INADDR_ANY};
- struct in6_addr ipv6addr = IN6ADDR_ANY_INIT;
- struct in6_addr ipv6prefix = IN6ADDR_ANY_INIT;
- struct in6_addr pd = IN6ADDR_ANY_INIT;
- int offset = -1;
- int psidlen = -1;
- int psid = -1;
- uint16_t psid16 = 0;
- const char *dmr = NULL;
- const char *br = NULL;
- for (char *rule = strdup(argv[i]); *rule; ) {
- char *value;
- int intval;
- int idx = getsubopt(&rule, token, &value);
- errno = 0;
- if (idx == OPT_TYPE) {
- lw4o6 = (value && !strcmp(value, "lw4o6"));
- } else if (idx == OPT_FMR) {
- fmr = true;
- } else if (idx == OPT_EALEN && (intval = strtoul(value, NULL, 0)) <= 48 && !errno) {
- ealen = intval;
- } else if (idx == OPT_PREFIX4LEN && (intval = strtoul(value, NULL, 0)) <= 32 && !errno) {
- prefix4len = intval;
- } else if (idx == OPT_PREFIX6LEN && (intval = strtoul(value, NULL, 0)) <= 128 && !errno) {
- prefix6len = intval;
- } else if (idx == OPT_IPV4PREFIX && inet_pton(AF_INET, value, &ipv4prefix) == 1) {
- // dummy
- } else if (idx == OPT_IPV6PREFIX && inet_pton(AF_INET6, value, &ipv6prefix) == 1) {
- // dummy
- } else if (idx == OPT_PD && inet_pton(AF_INET6, value, &pd) == 1) {
- // dummy
- } else if (idx == OPT_OFFSET && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
- offset = intval;
- } else if (idx == OPT_PSIDLEN && (intval = strtoul(value, NULL, 0)) <= 16 && !errno) {
- psidlen = intval;
- } else if (idx == OPT_PDLEN && (intval = strtoul(value, NULL, 0)) <= 128 && !errno) {
- pdlen = intval;
- } else if (idx == OPT_PSID && (intval = strtoul(value, NULL, 0)) <= 65535 && !errno) {
- psid = intval;
- } else if (idx == OPT_DMR) {
- dmr = value;
- } else if (idx == OPT_BR) {
- br = value;
- } else {
- if (idx == -1 || idx >= OPT_MAX)
- fprintf(stderr, "Skipped invalid option: %s\n", value);
- else
- fprintf(stderr, "Skipped invalid value %s for option %s\n",
- value, token[idx]);
- }
- }
- if (offset < 0)
- offset = (lw4o6) ? 0 : (legacy) ? 4 : 6;
- // LW4over6 doesn't have an EALEN and has no psid-autodetect
- if (lw4o6) {
- if (psidlen < 0)
- psidlen = 0;
- ealen = psidlen;
- }
- // Find PD
- if (pdlen < 0) {
- struct blob_attr *c;
- unsigned rem;
- blobmsg_for_each_attr(c, dump, rem) {
- struct blob_attr *tb[IFACE_ATTR_MAX];
- blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c));
- if (!tb[IFACE_ATTR_INTERFACE] || (strcmp(argv[1], "*") && strcmp(argv[1],
- blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]))))
- continue;
- match_prefix(&pdlen, &pd, tb[IFACE_ATTR_PREFIX], &ipv6prefix, prefix6len, lw4o6);
- if (lw4o6)
- match_prefix(&pdlen, &pd, tb[IFACE_ATTR_ADDRESS], &ipv6prefix, prefix6len, lw4o6);
- if (pdlen >= 0) {
- iface = blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]);
- break;
- }
- }
- }
- if (ealen < 0 && pdlen >= 0)
- ealen = pdlen - prefix6len;
- if (psidlen <= 0) {
- psidlen = ealen - (32 - prefix4len);
- psid = -1;
- }
- if (psid < 0 && psidlen <= 16 && psidlen >= 0 && pdlen >= 0 && ealen >= psidlen) {
- bmemcpys64(&psid16, &pd, prefix6len + ealen - psidlen, psidlen);
- psid = be16_to_cpu(psid16);
- }
- psid = psid >> (16 - psidlen);
- psid16 = cpu_to_be16(psid);
- psid = psid << (16 - psidlen);
- if (prefix4len < 0 || prefix6len < 0 || ealen < 0 || ealen < psidlen) {
- fprintf(stderr, "Skipping invalid or incomplete rule: %s\n", argv[i]);
- status = 1;
- continue;
- }
- if ((pdlen >= 0 || ealen == psidlen) && ealen >= psidlen) {
- bmemcpys64(&ipv4addr, &pd, prefix6len, ealen - psidlen);
- ipv4addr.s_addr = htonl(ntohl(ipv4addr.s_addr) >> prefix4len);
- bmemcpy(&ipv4addr, &ipv4prefix, prefix4len);
- if (prefix4len + ealen < 32)
- addr4len = prefix4len + ealen;
- }
- if (pdlen < 0 && !fmr) {
- fprintf(stderr, "Skipping non-FMR without matching PD: %s\n", argv[i]);
- status = 1;
- continue;
- } else if (pdlen >= 0) {
- size_t v4offset = (legacy) ? 9 : 10;
- memcpy(&ipv6addr.s6_addr[v4offset], &ipv4addr, 4);
- memcpy(&ipv6addr.s6_addr[v4offset + 4], &psid16, 2);
- bmemcpy(&ipv6addr, &pd, pdlen);
- }
- ++rulecnt;
- char ipv4addrbuf[INET_ADDRSTRLEN];
- char ipv4prefixbuf[INET_ADDRSTRLEN];
- char ipv6prefixbuf[INET6_ADDRSTRLEN];
- char ipv6addrbuf[INET6_ADDRSTRLEN];
- char pdbuf[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET, &ipv4addr, ipv4addrbuf, sizeof(ipv4addrbuf));
- inet_ntop(AF_INET, &ipv4prefix, ipv4prefixbuf, sizeof(ipv4prefixbuf));
- inet_ntop(AF_INET6, &ipv6prefix, ipv6prefixbuf, sizeof(ipv6prefixbuf));
- inet_ntop(AF_INET6, &ipv6addr, ipv6addrbuf, sizeof(ipv6addrbuf));
- inet_ntop(AF_INET6, &pd, pdbuf, sizeof(pdbuf));
- printf("RULE_%d_FMR=%d\n", rulecnt, fmr);
- printf("RULE_%d_EALEN=%d\n", rulecnt, ealen);
- printf("RULE_%d_PSIDLEN=%d\n", rulecnt, psidlen);
- printf("RULE_%d_OFFSET=%d\n", rulecnt, offset);
- printf("RULE_%d_PREFIX4LEN=%d\n", rulecnt, prefix4len);
- printf("RULE_%d_PREFIX6LEN=%d\n", rulecnt, prefix6len);
- printf("RULE_%d_IPV4PREFIX=%s\n", rulecnt, ipv4prefixbuf);
- printf("RULE_%d_IPV6PREFIX=%s\n", rulecnt, ipv6prefixbuf);
- if (pdlen >= 0) {
- printf("RULE_%d_IPV6PD=%s\n", rulecnt, pdbuf);
- printf("RULE_%d_PD6LEN=%d\n", rulecnt, pdlen);
- printf("RULE_%d_PD6IFACE=%s\n", rulecnt, iface);
- printf("RULE_%d_IPV6ADDR=%s\n", rulecnt, ipv6addrbuf);
- printf("RULE_BMR=%d\n", rulecnt);
- }
- if (ipv4addr.s_addr) {
- printf("RULE_%d_IPV4ADDR=%s\n", rulecnt, ipv4addrbuf);
- printf("RULE_%d_ADDR4LEN=%d\n", rulecnt, addr4len);
- }
- if (psidlen > 0 && psid >= 0) {
- printf("RULE_%d_PORTSETS='", rulecnt);
- for (int k = (offset) ? 1 : 0; k < (1 << offset); ++k) {
- int start = (k << (16 - offset)) | (psid >> offset);
- int end = start + (1 << (16 - offset - psidlen)) - 1;
- if (start == 0)
- start = 1;
- if (start <= end)
- printf("%d-%d ", start, end);
- }
- printf("'\n");
- }
- if (dmr)
- printf("RULE_%d_DMR=%s\n", rulecnt, dmr);
- if (br)
- printf("RULE_%d_BR=%s\n", rulecnt, br);
- }
- printf("RULE_COUNT=%d\n", rulecnt);
- return status;
- }
|