]>
Commit | Line | Data |
---|---|---|
18c8241a MM |
1 | /* |
2 | * BIRD Library -- IPv6 Address Manipulation Functions | |
3 | * | |
dce26783 | 4 | * (c) 1999 Martin Mares <mj@ucw.cz> |
18c8241a MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
dce26783 MM |
9 | #include <stdlib.h> |
10 | ||
18c8241a MM |
11 | #include "nest/bird.h" |
12 | #include "lib/ip.h" | |
dce26783 MM |
13 | #include "lib/bitops.h" |
14 | #include "lib/endian.h" | |
221135d6 | 15 | #include "lib/string.h" |
dce26783 MM |
16 | |
17 | /* | |
18 | * See RFC 2373 for explanation of IPv6 addressing issues. | |
19 | */ | |
20 | ||
21 | ip_addr | |
22 | ipv6_mkmask(unsigned n) | |
23 | { | |
24 | ip_addr a; | |
25 | int i; | |
26 | ||
27 | for(i=0; i<4; i++) | |
28 | { | |
29 | if (!n) | |
30 | a.addr[i] = 0; | |
31 | else if (n >= 32) | |
32 | { | |
33 | a.addr[i] = ~0; | |
34 | n -= 32; | |
35 | } | |
36 | else | |
37 | { | |
38 | a.addr[i] = u32_mkmask(n); | |
39 | n = 0; | |
40 | } | |
41 | } | |
42 | return a; | |
43 | } | |
44 | ||
45 | unsigned | |
46 | ipv6_mklen(ip_addr *a) | |
47 | { | |
48 | int i, j, n; | |
49 | ||
50 | for(i=0, n=0; i<4; i++, n+=32) | |
51 | if (a->addr[i] != ~0U) | |
52 | { | |
53 | j = u32_masklen(a->addr[i]); | |
54 | if (j < 0) | |
55 | return j; | |
56 | n += j; | |
57 | while (++i < 4) | |
58 | if (a->addr[i]) | |
59 | return -1; | |
60 | break; | |
61 | } | |
62 | return n; | |
63 | } | |
64 | ||
65 | int | |
66 | ipv6_classify(ip_addr *a) | |
67 | { | |
68 | u32 x = a->addr[0]; | |
69 | ||
dcc71a7f | 70 | if ((x & 0xe0000000) == 0x20000000) /* 2000::/3 Aggregatable Global Unicast Address */ |
dce26783 | 71 | return IADDR_HOST | SCOPE_UNIVERSE; |
dcc71a7f | 72 | if ((x & 0xffc00000) == 0xfe800000) /* fe80::/10 Link-Local Address */ |
dce26783 | 73 | return IADDR_HOST | SCOPE_LINK; |
dcc71a7f | 74 | if ((x & 0xffc00000) == 0xfec00000) /* fec0::/10 Site-Local Address */ |
dce26783 | 75 | return IADDR_HOST | SCOPE_SITE; |
dcc71a7f OZ |
76 | if ((x & 0xfe000000) == 0xfc000000) /* fc00::/7 Unique Local Unicast Address (RFC 4193) */ |
77 | return IADDR_HOST | SCOPE_SITE; | |
78 | if ((x & 0xff000000) == 0xff000000) /* ff00::/8 Multicast Address */ | |
dce26783 MM |
79 | { |
80 | unsigned int scope = (x >> 16) & 0x0f; | |
81 | switch (scope) | |
82 | { | |
83 | case 1: return IADDR_MULTICAST | SCOPE_HOST; | |
84 | case 2: return IADDR_MULTICAST | SCOPE_LINK; | |
85 | case 5: return IADDR_MULTICAST | SCOPE_SITE; | |
86 | case 8: return IADDR_MULTICAST | SCOPE_ORGANIZATION; | |
87 | case 14: return IADDR_MULTICAST | SCOPE_UNIVERSE; | |
bf6d91dc | 88 | default: return IADDR_MULTICAST | SCOPE_UNDEFINED; |
dce26783 MM |
89 | } |
90 | } | |
85a291ff MM |
91 | if (!x && !a->addr[1] && !a->addr[2]) |
92 | { | |
93 | u32 y = a->addr[3]; | |
94 | if (y == 1) | |
95 | return IADDR_HOST | SCOPE_HOST; /* Loopback address */ | |
96 | /* IPv4 compatible addresses */ | |
97 | if (y >= 0x7f000000 && y < 0x80000000) | |
98 | return IADDR_HOST | SCOPE_HOST; | |
99 | if ((y & 0xff000000) == 0x0a000000 || | |
100 | (y & 0xffff0000) == 0xc0a80000 || | |
101 | (y & 0xfff00000) == 0xac100000) | |
102 | return IADDR_HOST | SCOPE_SITE; | |
103 | if (y >= 0x01000000 && y < 0xe0000000) | |
104 | return IADDR_HOST | SCOPE_UNIVERSE; | |
105 | } | |
bf6d91dc | 106 | return IADDR_HOST | SCOPE_UNDEFINED; |
dce26783 MM |
107 | } |
108 | ||
109 | void | |
110 | ipv6_hton(ip_addr *a) | |
111 | { | |
112 | int i; | |
113 | ||
114 | for(i=0; i<4; i++) | |
115 | a->addr[i] = htonl(a->addr[i]); | |
116 | } | |
117 | ||
118 | void | |
119 | ipv6_ntoh(ip_addr *a) | |
120 | { | |
121 | int i; | |
122 | ||
123 | for(i=0; i<4; i++) | |
124 | a->addr[i] = ntohl(a->addr[i]); | |
125 | } | |
126 | ||
127 | int | |
12a9d139 | 128 | ipv6_compare(ip_addr X, ip_addr Y) |
dce26783 MM |
129 | { |
130 | int i; | |
12a9d139 MM |
131 | ip_addr *x = &X; |
132 | ip_addr *y = &Y; | |
dce26783 MM |
133 | |
134 | for(i=0; i<4; i++) | |
135 | if (x->addr[i] > y->addr[i]) | |
136 | return 1; | |
137 | else if (x->addr[i] < y->addr[i]) | |
138 | return -1; | |
139 | return 0; | |
140 | } | |
141 | ||
142 | /* | |
143 | * Conversion of IPv6 address to presentation format and vice versa. | |
144 | * Heavily inspired by routines written by Paul Vixie for the BIND project | |
145 | * and of course by RFC 2373. | |
146 | */ | |
147 | ||
148 | char * | |
149 | ip_ntop(ip_addr a, char *b) | |
150 | { | |
151 | u16 words[8]; | |
152 | int bestpos, bestlen, curpos, curlen, i; | |
dce26783 MM |
153 | |
154 | /* First of all, preprocess the address and find the longest run of zeros */ | |
155 | bestlen = bestpos = curpos = curlen = 0; | |
156 | for(i=0; i<8; i++) | |
157 | { | |
158 | u32 x = a.addr[i/2]; | |
159 | words[i] = ((i%2) ? x : (x >> 16)) & 0xffff; | |
160 | if (words[i]) | |
161 | curlen = 0; | |
162 | else | |
163 | { | |
164 | if (!curlen) | |
165 | curpos = i; | |
166 | curlen++; | |
167 | if (curlen > bestlen) | |
168 | { | |
169 | bestpos = curpos; | |
170 | bestlen = curlen; | |
171 | } | |
172 | } | |
173 | } | |
174 | if (bestlen < 2) | |
175 | bestpos = -1; | |
176 | ||
177 | /* Is it an encapsulated IPv4 address? */ | |
178 | if (!bestpos && | |
179 | (bestlen == 5 && a.addr[2] == 0xffff || | |
180 | bestlen == 6)) | |
181 | { | |
182 | u32 x = a.addr[3]; | |
a37410cb MM |
183 | b += bsprintf(b, "::%s%d.%d.%d.%d", |
184 | a.addr[2] ? "ffff:" : "", | |
185 | ((x >> 24) & 0xff), | |
186 | ((x >> 16) & 0xff), | |
187 | ((x >> 8) & 0xff), | |
188 | (x & 0xff)); | |
dce26783 MM |
189 | return b; |
190 | } | |
191 | ||
192 | /* Normal IPv6 formatting, compress the largest sequence of zeros */ | |
193 | for(i=0; i<8; i++) | |
194 | { | |
195 | if (i == bestpos) | |
196 | { | |
197 | i += bestlen - 1; | |
198 | *b++ = ':'; | |
199 | if (i == 7) | |
200 | *b++ = ':'; | |
201 | } | |
202 | else | |
203 | { | |
204 | if (i) | |
205 | *b++ = ':'; | |
a37410cb | 206 | b += bsprintf(b, "%x", words[i]); |
dce26783 MM |
207 | } |
208 | } | |
209 | *b = 0; | |
210 | return b; | |
211 | } | |
212 | ||
213 | char * | |
214 | ip_ntox(ip_addr a, char *b) | |
215 | { | |
216 | int i; | |
217 | ||
218 | for(i=0; i<4; i++) | |
219 | { | |
220 | if (i) | |
221 | *b++ = '.'; | |
a37410cb | 222 | b += bsprintf(b, "%08x", a.addr[i]); |
dce26783 MM |
223 | } |
224 | return b; | |
225 | } | |
226 | ||
227 | int | |
228 | ipv4_pton_u32(char *a, u32 *o) | |
229 | { | |
2a149b18 | 230 | int i; |
dce26783 MM |
231 | unsigned long int l; |
232 | u32 ia = 0; | |
233 | ||
234 | i=4; | |
235 | while (i--) | |
236 | { | |
237 | char *d, *c = strchr(a, '.'); | |
238 | if (!c != !i) | |
239 | return 0; | |
240 | l = strtoul(a, &d, 10); | |
241 | if (d != c && *d || l > 255) | |
242 | return 0; | |
243 | ia = (ia << 8) | l; | |
244 | if (c) | |
245 | c++; | |
246 | a = c; | |
247 | } | |
248 | *o = ia; | |
249 | return 1; | |
250 | } | |
251 | ||
252 | int | |
253 | ip_pton(char *a, ip_addr *o) | |
254 | { | |
255 | u16 words[8]; | |
256 | int i, j, k, l, hfil; | |
257 | char *start; | |
258 | ||
259 | if (a[0] == ':') /* Leading :: */ | |
260 | { | |
261 | if (a[1] != ':') | |
262 | return 0; | |
263 | a++; | |
264 | } | |
265 | hfil = -1; | |
266 | i = 0; | |
267 | while (*a) | |
268 | { | |
269 | if (*a == ':') /* :: */ | |
270 | { | |
271 | if (hfil >= 0) | |
272 | return 0; | |
273 | hfil = i; | |
274 | a++; | |
275 | continue; | |
276 | } | |
277 | j = 0; | |
278 | l = 0; | |
279 | start = a; | |
280 | for(;;) | |
281 | { | |
282 | if (*a >= '0' && *a <= '9') | |
283 | k = *a++ - '0'; | |
284 | else if (*a >= 'A' && *a <= 'F') | |
285 | k = *a++ - 'A' + 10; | |
286 | else if (*a >= 'a' && *a <= 'f') | |
287 | k = *a++ - 'a' + 10; | |
288 | else | |
289 | break; | |
290 | j = (j << 4) + k; | |
291 | if (j >= 0x10000 || ++l > 4) | |
292 | return 0; | |
293 | } | |
294 | if (*a == ':' && a[1]) | |
295 | a++; | |
296 | else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0)) | |
297 | { /* Embedded IPv4 address */ | |
298 | u32 x; | |
299 | if (!ipv4_pton_u32(start, &x)) | |
300 | return 0; | |
301 | words[i++] = x >> 16; | |
302 | words[i++] = x; | |
303 | break; | |
304 | } | |
305 | else if (*a) | |
306 | return 0; | |
307 | if (i >= 8) | |
308 | return 0; | |
309 | words[i++] = j; | |
310 | } | |
311 | ||
312 | /* Replace :: with an appropriate number of zeros */ | |
313 | if (hfil >= 0) | |
314 | { | |
315 | j = 8 - i; | |
316 | for(i=7; i-j >= hfil; i--) | |
317 | words[i] = words[i-j]; | |
318 | for(; i>=hfil; i--) | |
319 | words[i] = 0; | |
320 | } | |
321 | ||
322 | /* Convert the address to ip_addr format */ | |
323 | for(i=0; i<4; i++) | |
324 | o->addr[i] = (words[2*i] << 16) | words[2*i+1]; | |
325 | return 1; | |
326 | } | |
327 | ||
cfa6ab05 MM |
328 | void ipv6_absolutize(ip_addr *a, ip_addr *ifa) |
329 | { | |
330 | if ((a->addr[0] & 0xffc00000) == 0xfe800000 && /* a is link-scope */ | |
331 | ((ifa->addr[0] & 0xe0000000) == 0x20000000 | /* ifa is AGU ... */ | |
332 | (ifa->addr[0] & 0xffc00000) == 0xfec00000)) /* ... or site-scope */ | |
333 | { | |
334 | a->addr[0] = ifa->addr[0]; /* Copy the prefix, leave interface ID */ | |
335 | a->addr[1] = ifa->addr[1]; | |
336 | } | |
337 | } | |
338 | ||
dce26783 MM |
339 | #ifdef TEST |
340 | ||
341 | #include "bitops.c" | |
342 | ||
343 | static void test(char *x) | |
344 | { | |
345 | ip_addr a; | |
346 | char c[STD_ADDRESS_P_LENGTH+1]; | |
347 | ||
348 | printf("%-40s ", x); | |
349 | if (!ip_pton(x, &a)) | |
350 | { | |
351 | puts("BAD"); | |
352 | return; | |
353 | } | |
354 | ip_ntop(a, c); | |
355 | printf("%-40s %04x\n", c, ipv6_classify(&a)); | |
356 | } | |
357 | ||
358 | int main(void) | |
359 | { | |
360 | puts("Positive tests:"); | |
361 | test("1:2:3:4:5:6:7:8"); | |
362 | test("dead:beef:DEAD:BEEF::f00d"); | |
363 | test("::"); | |
364 | test("::1"); | |
365 | test("1::"); | |
366 | test("::1.234.5.6"); | |
367 | test("::ffff:1.234.5.6"); | |
368 | test("::fffe:1.234.5.6"); | |
369 | test("1:2:3:4:5:6:7::8"); | |
370 | test("2080::8:800:200c:417a"); | |
371 | test("ff01::101"); | |
372 | ||
373 | puts("Negative tests:"); | |
374 | test(":::"); | |
375 | test("1:2:3:4:5:6:7:8:"); | |
376 | test("1::2::3"); | |
377 | test("::12345"); | |
378 | test("::1.2.3.4:5"); | |
379 | test(":1:2:3:4:5:6:7:8"); | |
380 | test("g:1:2:3:4:5:6:7"); | |
381 | return 0; | |
382 | } | |
18c8241a | 383 | |
dce26783 | 384 | #endif |