]>
Commit | Line | Data |
---|---|---|
089fb364 TL |
1 | /* inet.c |
2 | ||
3 | Subroutines to manipulate internet addresses in a safely portable | |
4 | way... */ | |
5 | ||
6 | /* | |
98311e4b DH |
7 | * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC") |
8 | * Copyright (c) 1995-2003 by Internet Software Consortium | |
089fb364 | 9 | * |
98311e4b DH |
10 | * Permission to use, copy, modify, and distribute this software for any |
11 | * purpose with or without fee is hereby granted, provided that the above | |
12 | * copyright notice and this permission notice appear in all copies. | |
089fb364 | 13 | * |
98311e4b DH |
14 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
20 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
089fb364 | 21 | * |
98311e4b DH |
22 | * Internet Systems Consortium, Inc. |
23 | * 950 Charter Street | |
24 | * Redwood City, CA 94063 | |
25 | * <info@isc.org> | |
26 | * http://www.isc.org/ | |
49733f31 | 27 | * |
98311e4b | 28 | * This software has been written for Internet Systems Consortium |
49733f31 | 29 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. |
98311e4b | 30 | * To learn more about Internet Systems Consortium, see |
49733f31 TL |
31 | * ``http://www.isc.org/''. To learn more about Vixie Enterprises, |
32 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see | |
33 | * ``http://www.nominum.com''. | |
089fb364 TL |
34 | */ |
35 | ||
341b9d40 TL |
36 | #ifndef lint |
37 | static char copyright[] = | |
98bd7ca0 | 38 | "$Id: inet.c,v 1.12 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium. All rights reserved.\n"; |
341b9d40 TL |
39 | #endif /* not lint */ |
40 | ||
089fb364 TL |
41 | #include "dhcpd.h" |
42 | ||
43 | /* Return just the network number of an internet address... */ | |
44 | ||
45 | struct iaddr subnet_number (addr, mask) | |
46 | struct iaddr addr; | |
47 | struct iaddr mask; | |
48 | { | |
49 | int i; | |
50 | struct iaddr rv; | |
51 | ||
98311e4b DH |
52 | if (addr.len > sizeof(addr.iabuf)) |
53 | log_fatal("subnet_number():%s:%d: Invalid addr length.", MDL); | |
54 | if (addr.len != mask.len) | |
55 | log_fatal("subnet_number():%s:%d: Addr/mask length mismatch.", | |
56 | MDL); | |
57 | ||
089fb364 TL |
58 | rv.len = 0; |
59 | ||
60 | /* Both addresses must have the same length... */ | |
61 | if (addr.len != mask.len) | |
62 | return rv; | |
63 | ||
64 | rv.len = addr.len; | |
65 | for (i = 0; i < rv.len; i++) | |
66 | rv.iabuf [i] = addr.iabuf [i] & mask.iabuf [i]; | |
67 | return rv; | |
68 | } | |
69 | ||
70 | /* Combine a network number and a integer to produce an internet address. | |
71 | This won't work for subnets with more than 32 bits of host address, but | |
72 | maybe this isn't a problem. */ | |
73 | ||
74 | struct iaddr ip_addr (subnet, mask, host_address) | |
75 | struct iaddr subnet; | |
76 | struct iaddr mask; | |
73fd5718 | 77 | u_int32_t host_address; |
089fb364 TL |
78 | { |
79 | int i, j, k; | |
73fd5718 | 80 | u_int32_t swaddr; |
089fb364 TL |
81 | struct iaddr rv; |
82 | unsigned char habuf [sizeof swaddr]; | |
83 | ||
98311e4b DH |
84 | if (subnet.len > sizeof(subnet.iabuf)) |
85 | log_fatal("ip_addr():%s:%d: Invalid addr length.", MDL); | |
86 | if (subnet.len != mask.len) | |
87 | log_fatal("ip_addr():%s:%d: Addr/mask length mismatch.", | |
88 | MDL); | |
89 | ||
089fb364 TL |
90 | swaddr = htonl (host_address); |
91 | memcpy (habuf, &swaddr, sizeof swaddr); | |
92 | ||
93 | /* Combine the subnet address and the host address. If | |
94 | the host address is bigger than can fit in the subnet, | |
95 | return a zero-length iaddr structure. */ | |
96 | rv = subnet; | |
97 | j = rv.len - sizeof habuf; | |
98 | for (i = sizeof habuf - 1; i >= 0; i--) { | |
99 | if (mask.iabuf [i + j]) { | |
97ca1699 | 100 | if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) { |
089fb364 TL |
101 | rv.len = 0; |
102 | return rv; | |
103 | } | |
104 | for (k = i - 1; k >= 0; k--) { | |
105 | if (habuf [k]) { | |
106 | rv.len = 0; | |
107 | return rv; | |
108 | } | |
109 | } | |
97ca1699 | 110 | rv.iabuf [i + j] |= habuf [i]; |
089fb364 | 111 | break; |
97ca1699 TL |
112 | } else |
113 | rv.iabuf [i + j] = habuf [i]; | |
089fb364 TL |
114 | } |
115 | ||
116 | return rv; | |
117 | } | |
118 | ||
a3b267e9 TL |
119 | /* Given a subnet number and netmask, return the address on that subnet |
120 | for which the host portion of the address is all ones (the standard | |
121 | broadcast address). */ | |
122 | ||
123 | struct iaddr broadcast_addr (subnet, mask) | |
124 | struct iaddr subnet; | |
125 | struct iaddr mask; | |
126 | { | |
127 | int i, j, k; | |
128 | struct iaddr rv; | |
129 | ||
98311e4b DH |
130 | if (subnet.len > sizeof(subnet.iabuf)) |
131 | log_fatal("broadcast_addr():%s:%d: Invalid addr length.", MDL); | |
132 | if (subnet.len != mask.len) | |
133 | log_fatal("broadcast_addr():%s:%d: Addr/mask length mismatch.", | |
134 | MDL); | |
135 | ||
a3b267e9 TL |
136 | if (subnet.len != mask.len) { |
137 | rv.len = 0; | |
138 | return rv; | |
139 | } | |
140 | ||
141 | for (i = 0; i < subnet.len; i++) { | |
142 | rv.iabuf [i] = subnet.iabuf [i] | (~mask.iabuf [i] & 255); | |
143 | } | |
144 | rv.len = subnet.len; | |
145 | ||
146 | return rv; | |
147 | } | |
148 | ||
73fd5718 | 149 | u_int32_t host_addr (addr, mask) |
089fb364 TL |
150 | struct iaddr addr; |
151 | struct iaddr mask; | |
152 | { | |
153 | int i; | |
73fd5718 | 154 | u_int32_t swaddr; |
089fb364 TL |
155 | struct iaddr rv; |
156 | ||
98311e4b DH |
157 | if (addr.len > sizeof(addr.iabuf)) |
158 | log_fatal("host_addr():%s:%d: Invalid addr length.", MDL); | |
159 | if (addr.len != mask.len) | |
160 | log_fatal("host_addr():%s:%d: Addr/mask length mismatch.", | |
161 | MDL); | |
162 | ||
089fb364 TL |
163 | rv.len = 0; |
164 | ||
165 | /* Mask out the network bits... */ | |
166 | rv.len = addr.len; | |
167 | for (i = 0; i < rv.len; i++) | |
168 | rv.iabuf [i] = addr.iabuf [i] & ~mask.iabuf [i]; | |
169 | ||
170 | /* Copy out up to 32 bits... */ | |
171 | memcpy (&swaddr, &rv.iabuf [rv.len - sizeof swaddr], sizeof swaddr); | |
172 | ||
173 | /* Swap it and return it. */ | |
174 | return ntohl (swaddr); | |
175 | } | |
176 | ||
177 | int addr_eq (addr1, addr2) | |
178 | struct iaddr addr1, addr2; | |
179 | { | |
98311e4b DH |
180 | if (addr1.len > sizeof(addr1.iabuf)) |
181 | log_fatal("addr_eq():%s:%d: Invalid addr length.", MDL); | |
182 | ||
089fb364 TL |
183 | if (addr1.len != addr2.len) |
184 | return 0; | |
185 | return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0; | |
186 | } | |
187 | ||
febbd402 DH |
188 | /* addr_match |
189 | * | |
190 | * compares an IP address against a network/mask combination | |
191 | * by ANDing the IP with the mask and seeing whether the result | |
192 | * matches the masked network value. | |
193 | */ | |
194 | int | |
195 | addr_match(addr, match) | |
196 | struct iaddr *addr; | |
197 | struct iaddrmatch *match; | |
198 | { | |
199 | int i; | |
200 | ||
201 | if (addr->len != match->addr.len) | |
202 | return 0; | |
203 | ||
204 | i = 0; | |
205 | for (i = 0 ; i < addr->len ; i++) { | |
206 | if ((addr->iabuf[i] & match->mask.iabuf[i]) != | |
207 | match->addr.iabuf[i]) | |
208 | return 0; | |
209 | } | |
210 | return 1; | |
211 | } | |
212 | ||
98bd7ca0 DH |
213 | /* |
214 | * Compares the addresses a1 and a2. | |
215 | * | |
216 | * If a1 < a2, returns -1. | |
217 | * If a1 == a2, returns 0. | |
218 | * If a1 > a2, returns 1. | |
219 | * | |
220 | * WARNING: if a1 and a2 differ in length, returns 0. | |
221 | */ | |
222 | int | |
223 | addr_cmp(const struct iaddr *a1, const struct iaddr *a2) { | |
089fb364 TL |
224 | int i; |
225 | ||
98bd7ca0 DH |
226 | if (a1->len != a2->len) { |
227 | return 0; | |
228 | } | |
229 | ||
230 | for (i=0; i<a1->len; i++) { | |
231 | if (a1->iabuf[i] < a2->iabuf[i]) { | |
232 | return -1; | |
233 | } | |
234 | if (a1->iabuf[i] > a2->iabuf[i]) { | |
235 | return 1; | |
236 | } | |
237 | } | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | /* | |
243 | * Performs a bitwise-OR of two addresses. | |
244 | * | |
245 | * Returns 1 if the result is non-zero, or 0 otherwise. | |
246 | * | |
247 | * WARNING: if a1 and a2 differ in length, returns 0. | |
248 | */ | |
249 | int | |
250 | addr_or(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) { | |
251 | int i; | |
252 | int all_zero; | |
253 | ||
254 | if (a1->len != a2->len) { | |
255 | return 0; | |
256 | } | |
257 | ||
258 | all_zero = 1; | |
259 | ||
260 | result->len = a1->len; | |
261 | for (i=0; i<a1->len; i++) { | |
262 | result->iabuf[i] = a1->iabuf[i] | a2->iabuf[i]; | |
263 | if (result->iabuf[i] != 0) { | |
264 | all_zero = 0; | |
265 | } | |
266 | } | |
267 | ||
268 | return !all_zero; | |
269 | } | |
270 | ||
271 | /* | |
272 | * Performs a bitwise-AND of two addresses. | |
273 | * | |
274 | * Returns 1 if the result is non-zero, or 0 otherwise. | |
275 | * | |
276 | * WARNING: if a1 and a2 differ in length, returns 0. | |
277 | */ | |
278 | int | |
279 | addr_and(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) { | |
280 | int i; | |
281 | int all_zero; | |
282 | ||
283 | if (a1->len != a2->len) { | |
284 | return 0; | |
285 | } | |
286 | ||
287 | all_zero = 1; | |
288 | ||
289 | result->len = a1->len; | |
290 | for (i=0; i<a1->len; i++) { | |
291 | result->iabuf[i] = a1->iabuf[i] & a2->iabuf[i]; | |
292 | if (result->iabuf[i] != 0) { | |
293 | all_zero = 0; | |
294 | } | |
295 | } | |
296 | ||
297 | return !all_zero; | |
298 | } | |
299 | ||
300 | /* | |
301 | * range2cidr | |
302 | * | |
303 | * Converts a range of IP addresses to a set of CIDR networks. | |
304 | * | |
305 | * Examples: | |
306 | * 192.168.0.0 - 192.168.0.255 = 192.168.0.0/24 | |
307 | * 10.0.0.0 - 10.0.1.127 = 10.0.0.0/24, 10.0.1.0/25 | |
308 | * 255.255.255.32 - 255.255.255.255 = 255.255.255.32/27, 255.255.255.64/26, | |
309 | * 255.255.255.128/25 | |
310 | */ | |
311 | isc_result_t | |
312 | range2cidr(struct iaddrcidrnetlist **result, | |
313 | const struct iaddr *lo, const struct iaddr *hi) { | |
314 | struct iaddr addr; | |
315 | struct iaddr mask; | |
316 | int bit; | |
317 | struct iaddr end_addr; | |
318 | struct iaddr dummy; | |
319 | int ofs, val; | |
320 | struct iaddrcidrnetlist *net; | |
321 | int tmp; | |
322 | ||
323 | if (result == NULL) { | |
324 | return ISC_R_INVALIDARG; | |
325 | } | |
326 | if (*result != NULL) { | |
327 | return ISC_R_INVALIDARG; | |
328 | } | |
329 | if ((lo == NULL) || (hi == NULL) || (lo->len != hi->len)) { | |
330 | return ISC_R_INVALIDARG; | |
331 | } | |
332 | ||
333 | /* | |
334 | * Put our start and end in the right order, if reversed. | |
335 | */ | |
336 | if (addr_cmp(lo, hi) > 0) { | |
337 | const struct iaddr *tmp; | |
338 | tmp = lo; | |
339 | lo = hi; | |
340 | hi = tmp; | |
341 | } | |
342 | ||
343 | /* | |
344 | * Theory of operation: | |
345 | * | |
346 | * ------------------- | |
347 | * Start at the low end, and keep trying larger networks | |
348 | * until we get one that is too big (explained below). | |
349 | * | |
350 | * We keep a "mask", which is the ones-complement of a | |
351 | * normal netmask. So, a /23 has a netmask of 255.255.254.0, | |
352 | * and a mask of 0.0.1.255. | |
353 | * | |
354 | * We know when a network is too big when we bitwise-AND the | |
355 | * mask with the starting address and we get a non-zero | |
356 | * result, like this: | |
357 | * | |
358 | * addr: 192.168.1.0, mask: 0.0.1.255 | |
359 | * bitwise-AND: 0.0.1.0 | |
360 | * | |
361 | * A network is also too big if the bitwise-OR of the mask | |
362 | * with the starting address is larger than the end address, | |
363 | * like this: | |
364 | * | |
365 | * start: 192.168.1.0, mask: 0.0.1.255, end: 192.168.0.255 | |
366 | * bitwise-OR: 192.168.1.255 | |
367 | * | |
368 | * ------------------- | |
369 | * Once we have found a network that is too big, we add the | |
370 | * appropriate CIDR network to our list of found networks. | |
371 | * | |
372 | * We then use the next IP address as our low address, and | |
373 | * begin the process of searching for a network that is | |
374 | * too big again, starting with an empty mask. | |
375 | */ | |
376 | addr = *lo; | |
377 | bit = 0; | |
378 | memset(&mask, 0, sizeof(mask)); | |
379 | mask.len = addr.len; | |
380 | while (addr_cmp(&addr, hi) <= 0) { | |
381 | /* | |
382 | * Bitwise-OR mask with (1 << bit) | |
383 | */ | |
384 | ofs = addr.len - (bit / 8) - 1; | |
385 | val = 1 << (bit % 8); | |
386 | if (ofs >= 0) { | |
387 | mask.iabuf[ofs] |= val; | |
388 | } | |
389 | ||
390 | /* | |
391 | * See if we're too big, and save this network if so. | |
392 | */ | |
393 | addr_or(&end_addr, &addr, &mask); | |
394 | if ((ofs < 0) || | |
395 | (addr_cmp(&end_addr, hi) > 0) || | |
396 | addr_and(&dummy, &addr, &mask)) { | |
397 | /* | |
398 | * Add a new prefix to our list. | |
399 | */ | |
400 | net = dmalloc(sizeof(*net), MDL); | |
401 | if (net == NULL) { | |
402 | while (*result != NULL) { | |
403 | net = (*result)->next; | |
404 | dfree(*result, MDL); | |
405 | *result = net; | |
406 | } | |
407 | return ISC_R_NOMEMORY; | |
408 | } | |
409 | net->cidrnet.lo_addr = addr; | |
410 | net->cidrnet.bits = (addr.len * 8) - bit; | |
411 | net->next = *result; | |
412 | *result = net; | |
413 | ||
414 | /* | |
415 | * Figure out our new starting address, | |
416 | * by adding (1 << bit) to our previous | |
417 | * starting address. | |
418 | */ | |
419 | tmp = addr.iabuf[ofs] + val; | |
420 | while ((ofs >= 0) && (tmp > 255)) { | |
421 | addr.iabuf[ofs] = tmp - 256; | |
422 | ofs--; | |
423 | tmp = addr.iabuf[ofs] + 1; | |
424 | } | |
425 | if (ofs < 0) { | |
426 | /* Gone past last address, we're done. */ | |
427 | break; | |
428 | } | |
429 | addr.iabuf[ofs] = tmp; | |
430 | ||
431 | /* | |
432 | * Reset our bit and mask. | |
433 | */ | |
434 | bit = 0; | |
435 | memset(mask.iabuf, 0, sizeof(mask.iabuf)); | |
436 | memset(end_addr.iabuf, 0, sizeof(end_addr.iabuf)); | |
437 | } else { | |
438 | /* | |
439 | * If we're not too big, increase our network size. | |
440 | */ | |
441 | bit++; | |
442 | } | |
443 | } | |
444 | ||
445 | /* | |
446 | * We're done. | |
447 | */ | |
448 | return ISC_R_SUCCESS; | |
449 | } | |
450 | ||
451 | /* | |
452 | * Free a list of CIDR networks, such as returned from range2cidr(). | |
453 | */ | |
454 | isc_result_t | |
455 | free_iaddrcidrnetlist(struct iaddrcidrnetlist **result) { | |
456 | struct iaddrcidrnetlist *p; | |
457 | ||
458 | if (result == NULL) { | |
459 | return ISC_R_INVALIDARG; | |
460 | } | |
461 | if (*result == NULL) { | |
462 | return ISC_R_INVALIDARG; | |
463 | } | |
464 | ||
465 | while (*result != NULL) { | |
466 | p = *result; | |
467 | *result = p->next; | |
468 | dfree(p, MDL); | |
469 | } | |
470 | ||
471 | return ISC_R_SUCCESS; | |
472 | } | |
473 | ||
474 | /* piaddr() turns an iaddr structure into a printable address. */ | |
475 | /* XXX: should use a const pointer rather than passing the structure */ | |
476 | const char * | |
477 | piaddr(const struct iaddr addr) { | |
478 | static char | |
479 | pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; | |
480 | /* "255.255.255.255" */ | |
481 | ||
482 | /* INSIST((addr.len == 0) || (addr.len == 4) || (addr.len == 16)); */ | |
d758ad8c TL |
483 | |
484 | if (addr.len == 0) { | |
98bd7ca0 | 485 | return "<null address>"; |
d758ad8c | 486 | } |
98bd7ca0 DH |
487 | if (addr.len == 4) { |
488 | return inet_ntop(AF_INET, addr.iabuf, pbuf, sizeof(pbuf)); | |
489 | } | |
490 | if (addr.len == 16) { | |
491 | return inet_ntop(AF_INET6, addr.iabuf, pbuf, sizeof(pbuf)); | |
d758ad8c | 492 | } |
98bd7ca0 DH |
493 | |
494 | log_fatal("piaddr():%s:%d: Invalid address length %d.", MDL, | |
495 | addr.len); | |
496 | /* quell compiler warnings */ | |
497 | return NULL; | |
d758ad8c TL |
498 | } |
499 | ||
98bd7ca0 DH |
500 | /* piaddrmask takes an iaddr structure mask, determines the bitlength of |
501 | * the mask, and then returns the printable CIDR notation of the two. | |
502 | */ | |
503 | char * | |
504 | piaddrmask(struct iaddr *addr, struct iaddr *mask) { | |
98311e4b | 505 | int mw; |
98bd7ca0 | 506 | unsigned int oct, bit; |
98311e4b | 507 | |
98bd7ca0 | 508 | if ((addr->len != 4) && (addr->len != 16)) |
98311e4b | 509 | log_fatal("piaddrmask():%s:%d: Address length %d invalid", |
98bd7ca0 DH |
510 | MDL, addr->len); |
511 | if (addr->len != mask->len) | |
98311e4b DH |
512 | log_fatal("piaddrmask():%s:%d: Address and mask size mismatch", |
513 | MDL); | |
514 | ||
515 | /* Determine netmask width in bits. */ | |
98bd7ca0 | 516 | for (mw = (mask->len * 8) ; mw > 0 ; ) { |
98311e4b DH |
517 | oct = (mw - 1) / 8; |
518 | bit = 0x80 >> ((mw - 1) % 8); | |
98bd7ca0 | 519 | if (!mask->iabuf[oct]) |
98311e4b | 520 | mw -= 8; |
98bd7ca0 | 521 | else if (mask->iabuf[oct] & bit) |
d758ad8c | 522 | break; |
d758ad8c | 523 | else |
98311e4b | 524 | mw--; |
d758ad8c | 525 | } |
98311e4b | 526 | |
98bd7ca0 DH |
527 | if (mw < 0) |
528 | log_fatal("Impossible condition at %s:%d.", MDL); | |
529 | ||
530 | return piaddrcidr(addr, mw); | |
531 | } | |
532 | ||
533 | /* Format an address and mask-length into printable CIDR notation. */ | |
534 | char * | |
535 | piaddrcidr(const struct iaddr *addr, unsigned int bits) { | |
536 | static char | |
537 | ret[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128")]; | |
538 | /* "255.255.255.255/32" */ | |
539 | ||
540 | /* INSIST(addr != NULL); */ | |
541 | /* INSIST((addr->len == 4) || (addr->len == 16)); */ | |
542 | /* INSIST(bits <= (addr->len * 8)); */ | |
543 | ||
544 | if (bits > (addr->len * 8)) | |
545 | return NULL; | |
546 | ||
547 | sprintf(ret, "%s/%d", piaddr(*addr), bits); | |
98311e4b | 548 | |
98bd7ca0 | 549 | return ret; |
d758ad8c TL |
550 | } |
551 |