/* * Check format of network prefix */ #include #include #include #include #include #include #include #include typedef struct { uint8_t family; uint8_t bytelen; unsigned int plen; uint32_t data[4]; } inet_prefix; static void err(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } static void usage(void) { fprintf(stderr, "Usage: check-prefix-boundary [-4|-6] address/prefix\n"); exit(1); } static void get_addr_1(inet_prefix *addr, const char *name, int family) { memset(addr, 0, sizeof(*addr)); if (strchr(name, ':')) { addr->family = AF_INET6; addr->bytelen = 16; if (family != AF_UNSPEC && family != AF_INET6) err("IPV6 address not allowed\n"); if (inet_pton(AF_INET6, name, addr->data) <= 0) err("Invalid IPV6 address: %s\n", name); return; } addr->family = AF_INET; addr->bytelen = 4; if (family != AF_UNSPEC && family != AF_INET) err("IPV4 address not allowed\n"); if (inet_pton(AF_INET, name, addr->data) <= 0) err("Invalid IPV4 address: %s\n", name); return; } static void get_prefix_1(inet_prefix *dst, char *arg, int family) { char *slash, *endp; memset(dst, 0, sizeof(*dst)); slash = strchr(arg, '/'); if (!slash || slash[1] == '\0') err("Missing prefix length\n"); *slash = 0; get_addr_1(dst, arg, family); dst->plen = strtoul(slash+1, &endp, 0); if (*endp != '\0') err("Invalid character in prefix length\n"); if (dst->plen > 8 * dst->bytelen) err("Prefix length is too large\n"); *slash = '/'; } static void get_netmask(inet_prefix *msk, const inet_prefix *dst) { int i, plen = dst->plen; memset(msk, 0, sizeof(*msk)); msk->family = dst->family; msk->bytelen = dst->bytelen; for (i = 0; plen > 0 && i < dst->bytelen / sizeof(uint32_t); i++) { uint32_t m = (plen > 32) ? ~0 : htonl(~0 << (32 - plen)); msk->data[i] = dst->data[i] & m; plen -= 32; } } int main(int argc, char **argv) { int family = AF_UNSPEC; while (--argc) { char *arg = *++argv; inet_prefix dst, msk; if (arg[0] == '-') switch(arg[1]) { case '4': family = AF_INET; break; case '6': family = AF_INET6; break; default: usage(); } get_prefix_1(&dst, arg, family); get_netmask(&msk, &dst); if (memcmp(msk.data, dst.data, dst.bytelen) != 0) { char buf[INET_ADDRSTRLEN]; err("Prefix not on a natural network boundary." "Did you mean %s?\n", inet_ntop(msk.family, msk.data, buf, sizeof buf)); } } return 0; }