blob: c6922036b1833a899ac01e08d30a1fbeadabd9e0 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/*
2 * Prefix related functions.
3 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
4 *
5 * This file is part of GNU Zebra.
6 *
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Zebra; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include <zebra.h>
24
25#include "prefix.h"
26#include "vty.h"
27#include "sockunion.h"
28#include "memory.h"
29#include "log.h"
30
31/* Maskbit. */
32static u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
33 0xf8, 0xfc, 0xfe, 0xff};
34
35/* Number of bits in prefix type. */
36#ifndef PNBBY
37#define PNBBY 8
38#endif /* PNBBY */
39
40#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
41
42/* Address Famiy Identifier to Address Family converter. */
43int
44afi2family (int afi)
45{
46 if (afi == AFI_IP)
47 return AF_INET;
48#ifdef HAVE_IPV6
49 else if (afi == AFI_IP6)
50 return AF_INET6;
51#endif /* HAVE_IPV6 */
52 return 0;
53}
54
55int
56family2afi (int family)
57{
58 if (family == AF_INET)
59 return AFI_IP;
60#ifdef HAVE_IPV6
61 else if (family == AF_INET6)
62 return AFI_IP6;
63#endif /* HAVE_IPV6 */
64 return 0;
65}
66
67/* If n includes p prefix then return 1 else return 0. */
68int
hassob04c6992004-10-04 19:10:31 +000069prefix_match (const struct prefix *n, const struct prefix *p)
paul718e3742002-12-13 20:15:29 +000070{
71 int offset;
72 int shift;
73
74 /* Set both prefix's head pointer. */
paul8cc41982005-05-06 21:25:49 +000075 const u_char *np = (const u_char *)&n->u.prefix;
76 const u_char *pp = (const u_char *)&p->u.prefix;
paul718e3742002-12-13 20:15:29 +000077
78 /* If n's prefix is longer than p's one return 0. */
79 if (n->prefixlen > p->prefixlen)
80 return 0;
81
82 offset = n->prefixlen / PNBBY;
83 shift = n->prefixlen % PNBBY;
84
85 if (shift)
86 if (maskbit[shift] & (np[offset] ^ pp[offset]))
87 return 0;
88
89 while (offset--)
90 if (np[offset] != pp[offset])
91 return 0;
92 return 1;
93}
94
95/* Copy prefix from src to dest. */
96void
hassob04c6992004-10-04 19:10:31 +000097prefix_copy (struct prefix *dest, const struct prefix *src)
paul718e3742002-12-13 20:15:29 +000098{
99 dest->family = src->family;
100 dest->prefixlen = src->prefixlen;
101
102 if (src->family == AF_INET)
103 dest->u.prefix4 = src->u.prefix4;
104#ifdef HAVE_IPV6
105 else if (src->family == AF_INET6)
106 dest->u.prefix6 = src->u.prefix6;
107#endif /* HAVE_IPV6 */
108 else if (src->family == AF_UNSPEC)
109 {
110 dest->u.lp.id = src->u.lp.id;
111 dest->u.lp.adv_router = src->u.lp.adv_router;
112 }
113 else
114 {
ajsb9e70282004-12-08 17:14:45 +0000115 zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d",
paul718e3742002-12-13 20:15:29 +0000116 src->family);
117 assert (0);
118 }
119}
120
gdt9d24baa2004-01-13 14:55:40 +0000121/*
122 * Return 1 if the address/netmask contained in the prefix structure
123 * is the same, and else return 0. For this routine, 'same' requires
124 * that not only the prefix length and the network part be the same,
125 * but also the host part. Thus, 10.0.0.1/8 and 10.0.0.2/8 are not
126 * the same. Note that this routine has the same return value sense
127 * as '==' (which is different from prefix_cmp).
128 */
paul718e3742002-12-13 20:15:29 +0000129int
hassob04c6992004-10-04 19:10:31 +0000130prefix_same (const struct prefix *p1, const struct prefix *p2)
paul718e3742002-12-13 20:15:29 +0000131{
132 if (p1->family == p2->family && p1->prefixlen == p2->prefixlen)
133 {
134 if (p1->family == AF_INET)
135 if (IPV4_ADDR_SAME (&p1->u.prefix, &p2->u.prefix))
136 return 1;
137#ifdef HAVE_IPV6
138 if (p1->family == AF_INET6 )
139 if (IPV6_ADDR_SAME (&p1->u.prefix, &p2->u.prefix))
140 return 1;
141#endif /* HAVE_IPV6 */
142 }
143 return 0;
144}
145
gdt9d24baa2004-01-13 14:55:40 +0000146/*
147 * Return 0 if the network prefixes represented by the struct prefix
148 * arguments are the same prefix, and 1 otherwise. Network prefixes
149 * are considered the same if the prefix lengths are equal and the
150 * network parts are the same. Host bits (which are considered masked
151 * by the prefix length) are not significant. Thus, 10.0.0.1/8 and
152 * 10.0.0.2/8 are considered equivalent by this routine. Note that
153 * this routine has the same return sense as strcmp (which is different
154 * from prefix_same).
155 */
paul718e3742002-12-13 20:15:29 +0000156int
hassob04c6992004-10-04 19:10:31 +0000157prefix_cmp (const struct prefix *p1, const struct prefix *p2)
paul718e3742002-12-13 20:15:29 +0000158{
159 int offset;
160 int shift;
161
162 /* Set both prefix's head pointer. */
paul8cc41982005-05-06 21:25:49 +0000163 const u_char *pp1 = (const u_char *)&p1->u.prefix;
164 const u_char *pp2 = (const u_char *)&p2->u.prefix;
paul718e3742002-12-13 20:15:29 +0000165
166 if (p1->family != p2->family || p1->prefixlen != p2->prefixlen)
167 return 1;
168
169 offset = p1->prefixlen / 8;
170 shift = p1->prefixlen % 8;
171
172 if (shift)
173 if (maskbit[shift] & (pp1[offset] ^ pp2[offset]))
174 return 1;
175
176 while (offset--)
177 if (pp1[offset] != pp2[offset])
178 return 1;
179
180 return 0;
181}
182
183/* Return prefix family type string. */
hassob04c6992004-10-04 19:10:31 +0000184const char *
185prefix_family_str (const struct prefix *p)
paul718e3742002-12-13 20:15:29 +0000186{
187 if (p->family == AF_INET)
188 return "inet";
189#ifdef HAVE_IPV6
190 if (p->family == AF_INET6)
191 return "inet6";
192#endif /* HAVE_IPV6 */
193 return "unspec";
194}
195
196/* Allocate new prefix_ipv4 structure. */
197struct prefix_ipv4 *
198prefix_ipv4_new ()
199{
200 struct prefix_ipv4 *p;
201
ajs7907c6c2005-07-26 19:55:31 +0000202 /* Call prefix_new to allocate a full-size struct prefix to avoid problems
203 where the struct prefix_ipv4 is cast to struct prefix and unallocated
204 bytes were being referenced (e.g. in structure assignments). */
205 p = (struct prefix_ipv4 *)prefix_new();
paul718e3742002-12-13 20:15:29 +0000206 p->family = AF_INET;
207 return p;
208}
209
210/* Free prefix_ipv4 structure. */
211void
212prefix_ipv4_free (struct prefix_ipv4 *p)
213{
ajs7907c6c2005-07-26 19:55:31 +0000214 prefix_free((struct prefix *)p);
paul718e3742002-12-13 20:15:29 +0000215}
216
217/* When string format is invalid return 0. */
218int
hassob04c6992004-10-04 19:10:31 +0000219str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
paul718e3742002-12-13 20:15:29 +0000220{
221 int ret;
222 int plen;
223 char *pnt;
224 char *cp;
225
226 /* Find slash inside string. */
227 pnt = strchr (str, '/');
228
229 /* String doesn't contail slash. */
230 if (pnt == NULL)
231 {
232 /* Convert string to prefix. */
233 ret = inet_aton (str, &p->prefix);
234 if (ret == 0)
235 return 0;
236
237 /* If address doesn't contain slash we assume it host address. */
238 p->family = AF_INET;
239 p->prefixlen = IPV4_MAX_BITLEN;
240
241 return ret;
242 }
243 else
244 {
245 cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
246 strncpy (cp, str, pnt - str);
247 *(cp + (pnt - str)) = '\0';
248 ret = inet_aton (cp, &p->prefix);
249 XFREE (MTYPE_TMP, cp);
250
251 /* Get prefix length. */
252 plen = (u_char) atoi (++pnt);
hasso3fb9cd62004-10-19 19:44:43 +0000253 if (plen > IPV4_MAX_PREFIXLEN)
paul718e3742002-12-13 20:15:29 +0000254 return 0;
255
256 p->family = AF_INET;
257 p->prefixlen = plen;
258 }
259
260 return ret;
261}
262
263/* Convert masklen into IP address's netmask. */
264void
265masklen2ip (int masklen, struct in_addr *netmask)
266{
267 u_char *pnt;
268 int bit;
269 int offset;
270
271 memset (netmask, 0, sizeof (struct in_addr));
272 pnt = (unsigned char *) netmask;
273
274 offset = masklen / 8;
275 bit = masklen % 8;
276
277 while (offset--)
278 *pnt++ = 0xff;
279
280 if (bit)
281 *pnt = maskbit[bit];
282}
283
284/* Convert IP address's netmask into integer. We assume netmask is
285 sequential one. Argument netmask should be network byte order. */
286u_char
287ip_masklen (struct in_addr netmask)
288{
289 u_char len;
290 u_char *pnt;
291 u_char *end;
292 u_char val;
293
294 len = 0;
295 pnt = (u_char *) &netmask;
296 end = pnt + 4;
297
ajs330009f2005-07-26 14:35:37 +0000298 while ((pnt < end) && (*pnt == 0xff))
paul718e3742002-12-13 20:15:29 +0000299 {
300 len+= 8;
301 pnt++;
302 }
303
304 if (pnt < end)
305 {
306 val = *pnt;
307 while (val)
308 {
309 len++;
310 val <<= 1;
311 }
312 }
313 return len;
314}
315
316/* Apply mask to IPv4 prefix. */
317void
318apply_mask_ipv4 (struct prefix_ipv4 *p)
319{
320 u_char *pnt;
321 int index;
322 int offset;
323
324 index = p->prefixlen / 8;
325
326 if (index < 4)
327 {
328 pnt = (u_char *) &p->prefix;
329 offset = p->prefixlen % 8;
330
331 pnt[index] &= maskbit[offset];
332 index++;
333
334 while (index < 4)
335 pnt[index++] = 0;
336 }
337}
338
339/* If prefix is 0.0.0.0/0 then return 1 else return 0. */
340int
hassob04c6992004-10-04 19:10:31 +0000341prefix_ipv4_any (const struct prefix_ipv4 *p)
paul718e3742002-12-13 20:15:29 +0000342{
343 return (p->prefix.s_addr == 0 && p->prefixlen == 0);
344}
345
346#ifdef HAVE_IPV6
347
348/* Allocate a new ip version 6 route */
349struct prefix_ipv6 *
paul8cc41982005-05-06 21:25:49 +0000350prefix_ipv6_new (void)
paul718e3742002-12-13 20:15:29 +0000351{
352 struct prefix_ipv6 *p;
353
ajs7907c6c2005-07-26 19:55:31 +0000354 /* Allocate a full-size struct prefix to avoid problems with structure
355 size mismatches. */
356 p = (struct prefix_ipv6 *)prefix_new();
paul718e3742002-12-13 20:15:29 +0000357 p->family = AF_INET6;
358 return p;
359}
360
361/* Free prefix for IPv6. */
362void
363prefix_ipv6_free (struct prefix_ipv6 *p)
364{
ajs7907c6c2005-07-26 19:55:31 +0000365 prefix_free((struct prefix *)p);
paul718e3742002-12-13 20:15:29 +0000366}
367
368/* If given string is valid return pin6 else return NULL */
369int
hassob04c6992004-10-04 19:10:31 +0000370str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
paul718e3742002-12-13 20:15:29 +0000371{
372 char *pnt;
373 char *cp;
374 int ret;
375
376 pnt = strchr (str, '/');
377
378 /* If string doesn't contain `/' treat it as host route. */
379 if (pnt == NULL)
380 {
381 ret = inet_pton (AF_INET6, str, &p->prefix);
382 if (ret != 1)
383 return 0;
384 p->prefixlen = IPV6_MAX_BITLEN;
385 }
386 else
387 {
388 int plen;
389
390 cp = XMALLOC (0, (pnt - str) + 1);
391 strncpy (cp, str, pnt - str);
392 *(cp + (pnt - str)) = '\0';
393 ret = inet_pton (AF_INET6, cp, &p->prefix);
394 free (cp);
395 if (ret != 1)
396 return 0;
397 plen = (u_char) atoi (++pnt);
398 if (plen > 128)
399 return 0;
400 p->prefixlen = plen;
401 }
402 p->family = AF_INET6;
403
404 return ret;
405}
406
hassob04c6992004-10-04 19:10:31 +0000407/* Convert struct in6_addr netmask into integer.
408 * FIXME return u_char as ip_maskleni() does. */
paul718e3742002-12-13 20:15:29 +0000409int
410ip6_masklen (struct in6_addr netmask)
411{
412 int len = 0;
413 unsigned char val;
414 unsigned char *pnt;
415
416 pnt = (unsigned char *) & netmask;
417
418 while ((*pnt == 0xff) && len < 128)
419 {
420 len += 8;
421 pnt++;
422 }
423
424 if (len < 128)
425 {
426 val = *pnt;
427 while (val)
428 {
429 len++;
430 val <<= 1;
431 }
432 }
433 return len;
434}
435
436void
437masklen2ip6 (int masklen, struct in6_addr *netmask)
438{
439 unsigned char *pnt;
440 int bit;
441 int offset;
442
443 memset (netmask, 0, sizeof (struct in6_addr));
444 pnt = (unsigned char *) netmask;
445
446 offset = masklen / 8;
447 bit = masklen % 8;
448
449 while (offset--)
450 *pnt++ = 0xff;
451
452 if (bit)
453 *pnt = maskbit[bit];
454}
455
456void
457apply_mask_ipv6 (struct prefix_ipv6 *p)
458{
459 u_char *pnt;
460 int index;
461 int offset;
462
463 index = p->prefixlen / 8;
464
465 if (index < 16)
466 {
467 pnt = (u_char *) &p->prefix;
468 offset = p->prefixlen % 8;
469
470 pnt[index] &= maskbit[offset];
471 index++;
472
473 while (index < 16)
474 pnt[index++] = 0;
475 }
476}
477
478void
hassob04c6992004-10-04 19:10:31 +0000479str2in6_addr (const char *str, struct in6_addr *addr)
paul718e3742002-12-13 20:15:29 +0000480{
481 int i;
482 unsigned int x;
483
484 /* %x must point to unsinged int */
485 for (i = 0; i < 16; i++)
486 {
487 sscanf (str + (i * 2), "%02x", &x);
488 addr->s6_addr[i] = x & 0xff;
489 }
490}
491#endif /* HAVE_IPV6 */
492
493void
494apply_mask (struct prefix *p)
495{
496 switch (p->family)
497 {
498 case AF_INET:
499 apply_mask_ipv4 ((struct prefix_ipv4 *)p);
500 break;
501#ifdef HAVE_IPV6
502 case AF_INET6:
503 apply_mask_ipv6 ((struct prefix_ipv6 *)p);
504 break;
505#endif /* HAVE_IPV6 */
506 default:
507 break;
508 }
509 return;
510}
511
hassob04c6992004-10-04 19:10:31 +0000512/* Utility function of convert between struct prefix <=> union sockunion.
513 * FIXME This function isn't used anywhere. */
paul718e3742002-12-13 20:15:29 +0000514struct prefix *
hassob04c6992004-10-04 19:10:31 +0000515sockunion2prefix (const union sockunion *dest,
516 const union sockunion *mask)
paul718e3742002-12-13 20:15:29 +0000517{
518 if (dest->sa.sa_family == AF_INET)
519 {
520 struct prefix_ipv4 *p;
521
522 p = prefix_ipv4_new ();
523 p->family = AF_INET;
524 p->prefix = dest->sin.sin_addr;
525 p->prefixlen = ip_masklen (mask->sin.sin_addr);
526 return (struct prefix *) p;
527 }
528#ifdef HAVE_IPV6
529 if (dest->sa.sa_family == AF_INET6)
530 {
531 struct prefix_ipv6 *p;
532
533 p = prefix_ipv6_new ();
534 p->family = AF_INET6;
535 p->prefixlen = ip6_masklen (mask->sin6.sin6_addr);
536 memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr));
537 return (struct prefix *) p;
538 }
539#endif /* HAVE_IPV6 */
540 return NULL;
541}
542
hassob04c6992004-10-04 19:10:31 +0000543/* Utility function of convert between struct prefix <=> union sockunion. */
paul718e3742002-12-13 20:15:29 +0000544struct prefix *
hassob04c6992004-10-04 19:10:31 +0000545sockunion2hostprefix (const union sockunion *su)
paul718e3742002-12-13 20:15:29 +0000546{
547 if (su->sa.sa_family == AF_INET)
548 {
549 struct prefix_ipv4 *p;
550
551 p = prefix_ipv4_new ();
552 p->family = AF_INET;
553 p->prefix = su->sin.sin_addr;
554 p->prefixlen = IPV4_MAX_BITLEN;
555 return (struct prefix *) p;
556 }
557#ifdef HAVE_IPV6
558 if (su->sa.sa_family == AF_INET6)
559 {
560 struct prefix_ipv6 *p;
561
562 p = prefix_ipv6_new ();
563 p->family = AF_INET6;
564 p->prefixlen = IPV6_MAX_BITLEN;
565 memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr));
566 return (struct prefix *) p;
567 }
568#endif /* HAVE_IPV6 */
569 return NULL;
570}
571
572int
hassob04c6992004-10-04 19:10:31 +0000573prefix_blen (const struct prefix *p)
paul718e3742002-12-13 20:15:29 +0000574{
575 switch (p->family)
576 {
577 case AF_INET:
578 return IPV4_MAX_BYTELEN;
579 break;
580#ifdef HAVE_IPV6
581 case AF_INET6:
582 return IPV6_MAX_BYTELEN;
583 break;
584#endif /* HAVE_IPV6 */
585 }
586 return 0;
587}
588
589/* Generic function for conversion string to struct prefix. */
590int
hassob04c6992004-10-04 19:10:31 +0000591str2prefix (const char *str, struct prefix *p)
paul718e3742002-12-13 20:15:29 +0000592{
593 int ret;
594
595 /* First we try to convert string to struct prefix_ipv4. */
596 ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p);
597 if (ret)
598 return ret;
599
600#ifdef HAVE_IPV6
601 /* Next we try to convert string to struct prefix_ipv6. */
602 ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
603 if (ret)
604 return ret;
605#endif /* HAVE_IPV6 */
606
607 return 0;
608}
609
610int
hassob04c6992004-10-04 19:10:31 +0000611prefix2str (const struct prefix *p, char *str, int size)
paul718e3742002-12-13 20:15:29 +0000612{
613 char buf[BUFSIZ];
614
615 inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ);
616 snprintf (str, size, "%s/%d", buf, p->prefixlen);
617 return 0;
618}
619
620struct prefix *
621prefix_new ()
622{
623 struct prefix *p;
624
625 p = XCALLOC (MTYPE_PREFIX, sizeof *p);
626 return p;
627}
628
629/* Free prefix structure. */
630void
631prefix_free (struct prefix *p)
632{
633 XFREE (MTYPE_PREFIX, p);
634}
635
636/* Utility function. Check the string only contains digit
hassob04c6992004-10-04 19:10:31 +0000637 * character.
638 * FIXME str.[c|h] would be better place for this function. */
paul718e3742002-12-13 20:15:29 +0000639int
hassob04c6992004-10-04 19:10:31 +0000640all_digit (const char *str)
paul718e3742002-12-13 20:15:29 +0000641{
642 for (; *str != '\0'; str++)
643 if (!isdigit ((int) *str))
644 return 0;
645 return 1;
646}
647
648/* Utility function to convert ipv4 prefixes to Classful prefixes */
649void apply_classful_mask_ipv4 (struct prefix_ipv4 *p)
650{
651
652 u_int32_t destination;
653
654 destination = ntohl (p->prefix.s_addr);
655
hasso3fb9cd62004-10-19 19:44:43 +0000656 if (p->prefixlen == IPV4_MAX_PREFIXLEN);
paul718e3742002-12-13 20:15:29 +0000657 /* do nothing for host routes */
658 else if (IN_CLASSC (destination))
659 {
660 p->prefixlen=24;
661 apply_mask_ipv4(p);
662 }
663 else if (IN_CLASSB(destination))
664 {
665 p->prefixlen=16;
666 apply_mask_ipv4(p);
667 }
668 else
669 {
670 p->prefixlen=8;
671 apply_mask_ipv4(p);
672 }
673}
674
hasso3fb9cd62004-10-19 19:44:43 +0000675in_addr_t
676ipv4_network_addr (in_addr_t hostaddr, int masklen)
677{
678 struct in_addr mask;
679
680 masklen2ip (masklen, &mask);
681 return hostaddr & mask.s_addr;
682}
683
684in_addr_t
685ipv4_broadcast_addr (in_addr_t hostaddr, int masklen)
686{
687 struct in_addr mask;
688
689 masklen2ip (masklen, &mask);
690 return (masklen != IPV4_MAX_PREFIXLEN-1) ?
691 /* normal case */
692 (hostaddr | ~mask.s_addr) :
693 /* special case for /31 */
694 (hostaddr ^ ~mask.s_addr);
695}
696
paul718e3742002-12-13 20:15:29 +0000697/* Utility function to convert ipv4 netmask to prefixes
698 ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
699 ex.) "1.0.0.0" NULL => "1.0.0.0/8" */
700int
hassob04c6992004-10-04 19:10:31 +0000701netmask_str2prefix_str (const char *net_str, const char *mask_str,
702 char *prefix_str)
paul718e3742002-12-13 20:15:29 +0000703{
704 struct in_addr network;
705 struct in_addr mask;
706 u_char prefixlen;
707 u_int32_t destination;
708 int ret;
709
710 ret = inet_aton (net_str, &network);
711 if (! ret)
712 return 0;
713
714 if (mask_str)
715 {
716 ret = inet_aton (mask_str, &mask);
717 if (! ret)
718 return 0;
719
720 prefixlen = ip_masklen (mask);
721 }
722 else
723 {
724 destination = ntohl (network.s_addr);
725
726 if (network.s_addr == 0)
727 prefixlen = 0;
728 else if (IN_CLASSC (destination))
729 prefixlen = 24;
730 else if (IN_CLASSB (destination))
731 prefixlen = 16;
732 else if (IN_CLASSA (destination))
733 prefixlen = 8;
734 else
735 return 0;
736 }
737
738 sprintf (prefix_str, "%s/%d", net_str, prefixlen);
739
740 return 1;
741}
742
hasso59209902005-04-05 14:36:49 +0000743#ifdef HAVE_IPV6
744/* Utility function for making IPv6 address string. */
745const char *
hasso3a2ce6a2005-04-08 01:30:51 +0000746inet6_ntoa (struct in6_addr addr)
hasso59209902005-04-05 14:36:49 +0000747{
748 static char buf[INET6_ADDRSTRLEN];
749
hasso3a2ce6a2005-04-08 01:30:51 +0000750 inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN);
hasso59209902005-04-05 14:36:49 +0000751 return buf;
752}
753#endif /* HAVE_IPV6 */