/* * BIRD Library -- IPv6 Address Manipulation Functions * * (c) 1999 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ #include <stdlib.h> #include "nest/bird.h" #include "lib/ip.h" #include "lib/bitops.h" #include "lib/endian.h" #include "lib/string.h" /* * See RFC 2373 for explanation of IPv6 addressing issues. */ ip_addr ipv6_mkmask(unsigned n) { ip_addr a; int i; for(i=0; i<4; i++) { if (!n) a.addr[i] = 0; else if (n >= 32) { a.addr[i] = ~0; n -= 32; } else { a.addr[i] = u32_mkmask(n); n = 0; } } return a; } unsigned ipv6_mklen(ip_addr *a) { int i, j, n; for(i=0, n=0; i<4; i++, n+=32) if (a->addr[i] != ~0U) { j = u32_masklen(a->addr[i]); if (j < 0) return j; n += j; while (++i < 4) if (a->addr[i]) return -1; break; } return n; } int ipv6_classify(ip_addr *a) { u32 x = a->addr[0]; if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */ return IADDR_HOST | SCOPE_UNIVERSE; if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */ return IADDR_HOST | SCOPE_LINK; if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */ return IADDR_HOST | SCOPE_SITE; if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */ return IADDR_HOST | SCOPE_SITE; if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */ { unsigned int scope = (x >> 16) & 0x0f; switch (scope) { case 1: return IADDR_MULTICAST | SCOPE_HOST; case 2: return IADDR_MULTICAST | SCOPE_LINK; case 5: return IADDR_MULTICAST | SCOPE_SITE; case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION; case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE; default: return IADDR_MULTICAST | SCOPE_UNDEFINED; } } if (!x && !a->addr[1] && !a->addr[2]) { u32 y = a->addr[3]; if (y == 1) return IADDR_HOST | SCOPE_HOST; /* Loopback address */ /* IPv4 compatible addresses */ if (y >= 0x7f000000 && y < 0x80000000) return IADDR_HOST | SCOPE_HOST; if ((y & 0xff000000) == 0x0a000000 || (y & 0xffff0000) == 0xc0a80000 || (y & 0xfff00000) == 0xac100000) return IADDR_HOST | SCOPE_SITE; if (y >= 0x01000000 && y < 0xe0000000) return IADDR_HOST | SCOPE_UNIVERSE; } return IADDR_HOST | SCOPE_UNDEFINED; } void ipv6_hton(ip_addr *a) { int i; for(i=0; i<4; i++) a->addr[i] = htonl(a->addr[i]); } void ipv6_ntoh(ip_addr *a) { int i; for(i=0; i<4; i++) a->addr[i] = ntohl(a->addr[i]); } int ipv6_compare(ip_addr X, ip_addr Y) { int i; ip_addr *x = &X; ip_addr *y = &Y; for(i=0; i<4; i++) if (x->addr[i] > y->addr[i]) return 1; else if (x->addr[i] < y->addr[i]) return -1; return 0; } /* * Conversion of IPv6 address to presentation format and vice versa. * Heavily inspired by routines written by Paul Vixie for the BIND project * and of course by RFC 2373. */ char * ip_ntop(ip_addr a, char *b) { u16 words[8]; int bestpos, bestlen, curpos, curlen, i; /* First of all, preprocess the address and find the longest run of zeros */ bestlen = bestpos = curpos = curlen = 0; for(i=0; i<8; i++) { u32 x = a.addr[i/2]; words[i] = ((i%2) ? x : (x >> 16)) & 0xffff; if (words[i]) curlen = 0; else { if (!curlen) curpos = i; curlen++; if (curlen > bestlen) { bestpos = curpos; bestlen = curlen; } } } if (bestlen < 2) bestpos = -1; /* Is it an encapsulated IPv4 address? */ if (!bestpos && (bestlen == 5 && a.addr[2] == 0xffff || bestlen == 6)) { u32 x = a.addr[3]; b += bsprintf(b, "::%s%d.%d.%d.%d", a.addr[2] ? "ffff:" : "", ((x >> 24) & 0xff), ((x >> 16) & 0xff), ((x >> 8) & 0xff), (x & 0xff)); return b; } /* Normal IPv6 formatting, compress the largest sequence of zeros */ for(i=0; i<8; i++) { if (i == bestpos) { i += bestlen - 1; *b++ = ':'; if (i == 7) *b++ = ':'; } else { if (i) *b++ = ':'; b += bsprintf(b, "%x", words[i]); } } *b = 0; return b; } char * ip_ntox(ip_addr a, char *b) { int i; for(i=0; i<4; i++) { if (i) *b++ = '.'; b += bsprintf(b, "%08x", a.addr[i]); } return b; } int ipv4_pton_u32(char *a, u32 *o) { int i; unsigned long int l; u32 ia = 0; i=4; while (i--) { char *d, *c = strchr(a, '.'); if (!c != !i) return 0; l = strtoul(a, &d, 10); if (d != c && *d || l > 255) return 0; ia = (ia << 8) | l; if (c) c++; a = c; } *o = ia; return 1; } int ip_pton(char *a, ip_addr *o) { u16 words[8]; int i, j, k, l, hfil; char *start; if (a[0] == ':') /* Leading :: */ { if (a[1] != ':') return 0; a++; } hfil = -1; i = 0; while (*a) { if (*a == ':') /* :: */ { if (hfil >= 0) return 0; hfil = i; a++; continue; } j = 0; l = 0; start = a; for(;;) { if (*a >= '0' && *a <= '9') k = *a++ - '0'; else if (*a >= 'A' && *a <= 'F') k = *a++ - 'A' + 10; else if (*a >= 'a' && *a <= 'f') k = *a++ - 'a' + 10; else break; j = (j << 4) + k; if (j >= 0x10000 || ++l > 4) return 0; } if (*a == ':' && a[1]) a++; else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0)) { /* Embedded IPv4 address */ u32 x; if (!ipv4_pton_u32(start, &x)) return 0; words[i++] = x >> 16; words[i++] = x; break; } else if (*a) return 0; if (i >= 8) return 0; words[i++] = j; } /* Replace :: with an appropriate number of zeros */ if (hfil >= 0) { j = 8 - i; for(i=7; i-j >= hfil; i--) words[i] = words[i-j]; for(; i>=hfil; i--) words[i] = 0; } /* Convert the address to ip_addr format */ for(i=0; i<4; i++) o->addr[i] = (words[2*i] << 16) | words[2*i+1]; return 1; } void ipv6_absolutize(ip_addr *a, ip_addr *ifa) { if ((a->addr[0] & 0xffc00000) == 0xfe800000 && /* a is link-scope */ ((ifa->addr[0] & 0xe0000000) == 0x20000000 | /* ifa is AGU ... */ (ifa->addr[0] & 0xffc00000) == 0xfec00000)) /* ... or site-scope */ { a->addr[0] = ifa->addr[0]; /* Copy the prefix, leave interface ID */ a->addr[1] = ifa->addr[1]; } } #ifdef TEST #include "bitops.c" static void test(char *x) { ip_addr a; char c[STD_ADDRESS_P_LENGTH+1]; printf("%-40s ", x); if (!ip_pton(x, &a)) { puts("BAD"); return; } ip_ntop(a, c); printf("%-40s %04x\n", c, ipv6_classify(&a)); } int main(void) { puts("Positive tests:"); test("1:2:3:4:5:6:7:8"); test("dead:beef:DEAD:BEEF::f00d"); test("::"); test("::1"); test("1::"); test("::1.234.5.6"); test("::ffff:1.234.5.6"); test("::fffe:1.234.5.6"); test("1:2:3:4:5:6:7::8"); test("2080::8:800:200c:417a"); test("ff01::101"); puts("Negative tests:"); test(":::"); test("1:2:3:4:5:6:7:8:"); test("1::2::3"); test("::12345"); test("::1.2.3.4:5"); test(":1:2:3:4:5:6:7:8"); test("g:1:2:3:4:5:6:7"); return 0; } #endif