xref: /freebsd/usr.sbin/ppp/ncpaddr.c (revision 26dceef0a6e1522935bb209e842fa98a534729e1)
1 /*-
2  * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <net/if_types.h>
32 #include <net/route.h>
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/ip.h>
36 #include <arpa/inet.h>
37 #include <sys/un.h>
38 
39 #include <limits.h>
40 #include <netdb.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <termios.h>
45 
46 #include "log.h"
47 #include "ncpaddr.h"
48 #include "timer.h"
49 #include "fsm.h"
50 #include "defs.h"
51 #include "slcompress.h"
52 #include "iplist.h"
53 #include "throughput.h"
54 #include "mbuf.h"
55 #include "ip.h"
56 #include "ipcp.h"
57 #include "filter.h"
58 #include "descriptor.h"
59 #include "route.h"
60 #include "layer.h"
61 #include "lqr.h"
62 #include "hdlc.h"
63 #include "lcp.h"
64 #include "ccp.h"
65 #include "link.h"
66 #include "mp.h"
67 #ifndef NORADIUS
68 #include "radius.h"
69 #endif
70 #include "ipv6cp.h"
71 #include "ncp.h"
72 #include "bundle.h"
73 
74 
75 #define ncprange_ip4addr	u.ip4.ipaddr
76 #define ncprange_ip4mask	u.ip4.mask
77 #define ncprange_ip4width	u.ip4.width
78 #define ncpaddr_ip4addr		u.ip4addr
79 #ifndef NOINET6
80 #define ncprange_ip6addr	u.ip6.ipaddr
81 #define ncprange_ip6width	u.ip6.width
82 #define ncpaddr_ip6addr		u.ip6addr
83 #endif
84 
85 #define	NCP_ASCIIBUFFERSIZE	52
86 
87 static struct in_addr
88 bits2mask4(int bits)
89 {
90   struct in_addr result;
91   u_int32_t bit = 0x80000000;
92 
93   result.s_addr = 0;
94 
95   while (bits) {
96     result.s_addr |= bit;
97     bit >>= 1;
98     bits--;
99   }
100 
101   result.s_addr = htonl(result.s_addr);
102   return result;
103 }
104 
105 static int
106 mask42bits(struct in_addr mask)
107 {
108   u_int32_t msk = ntohl(mask.s_addr);
109   u_int32_t tst;
110   int ret;
111 
112   for (ret = 32, tst = 1; tst; ret--, tst <<= 1)
113     if (msk & tst)
114       break;
115 
116   for (tst <<= 1; tst; tst <<= 1)
117     if (!(msk & tst))
118       break;
119 
120   return tst ? -1 : ret;
121 }
122 
123 #ifndef NOINET6
124 static struct in6_addr
125 bits2mask6(int bits)
126 {
127   struct in6_addr result;
128   u_int32_t bit = 0x80;
129   u_char *c = result.s6_addr;
130 
131   memset(&result, '\0', sizeof result);
132 
133   while (bits) {
134     if (bit == 0) {
135       bit = 0x80;
136       c++;
137     }
138     *c |= bit;
139     bit >>= 1;
140     bits--;
141   }
142 
143   return result;
144 }
145 
146 static int
147 mask62bits(const struct in6_addr *mask)
148 {
149   const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
150   const u_char *c, *p, *end;
151   int masklen;
152 
153   p = (const u_char *)mask;
154   for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
155     masklen += 8;
156 
157   if (p < end) {
158     for (c = masks; c < masks + sizeof masks; c++)
159       if (*c == *p) {
160         masklen += c - masks;
161         break;
162       }
163   }
164 
165   return masklen;
166 }
167 
168 static void
169 adjust_linklocal(struct sockaddr_in6 *sin6)
170 {
171     /* XXX: ?????!?!?!!!!!  This is horrible ! */
172     if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
173         IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) {
174       sin6->sin6_scope_id =
175         ntohs(*(u_short *)&sin6->sin6_addr.s6_addr[2]);
176       *(u_short *)&sin6->sin6_addr.s6_addr[2] = 0;
177     }
178 }
179 #endif
180 
181 void
182 ncpaddr_init(struct ncpaddr *addr)
183 {
184   addr->ncpaddr_family = AF_UNSPEC;
185 }
186 
187 int
188 ncpaddr_isset(const struct ncpaddr *addr)
189 {
190   return addr->ncpaddr_family != AF_UNSPEC;
191 }
192 
193 int
194 ncpaddr_isdefault(const struct ncpaddr *addr)
195 {
196   switch (addr->ncpaddr_family) {
197   case AF_INET:
198     if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
199       return 1;
200     break;
201 
202 #ifndef NOINET6
203   case AF_INET6:
204     if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
205       return 1;
206     break;
207 #endif
208   }
209 
210   return 0;
211 }
212 
213 int
214 ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
215 {
216   if (addr->ncpaddr_family != cmp->ncpaddr_family)
217     return 0;
218 
219   switch (addr->ncpaddr_family) {
220   case AF_INET:
221     return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
222 
223 #ifndef NOINET6
224   case AF_INET6:
225     return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
226                    sizeof addr->ncpaddr_ip6addr);
227 #endif
228 
229   case AF_UNSPEC:
230     return 1;
231   }
232 
233   return 0;
234 }
235 
236 void
237 ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
238 {
239   switch (from->ncpaddr_family) {
240   case AF_INET:
241     addr->ncpaddr_family = AF_INET;
242     addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
243     break;
244 #ifndef NOINET6
245   case AF_INET6:
246     addr->ncpaddr_family = AF_INET6;
247     addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
248     break;
249 #endif
250   default:
251     addr->ncpaddr_family = AF_UNSPEC;
252   }
253 }
254 
255 void
256 ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
257 {
258   addr->ncpaddr_family = AF_INET;
259   addr->ncpaddr_ip4addr.s_addr = ip;
260 }
261 
262 int
263 ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
264 {
265   if (addr->ncpaddr_family != AF_INET)
266     return 0;
267   *ip = addr->ncpaddr_ip4addr.s_addr;
268   return 1;
269 }
270 
271 void
272 ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
273 {
274   addr->ncpaddr_family = AF_INET;
275   addr->ncpaddr_ip4addr = ip;
276 }
277 
278 int
279 ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
280 {
281   if (addr->ncpaddr_family != AF_INET)
282     return 0;
283   *ip = addr->ncpaddr_ip4addr;
284   return 1;
285 }
286 
287 #ifndef NOINET6
288 void
289 ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
290 {
291   addr->ncpaddr_family = AF_INET6;
292   addr->ncpaddr_ip6addr = *ip6;
293 }
294 
295 int
296 ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
297 {
298   if (addr->ncpaddr_family != AF_INET6)
299     return 0;
300   *ip6 = addr->ncpaddr_ip6addr;
301   return 1;
302 }
303 #endif
304 
305 void
306 ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
307 {
308   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
309 #ifndef NOINET6
310   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
311 #endif
312 
313   memset(host, '\0', sizeof(*host));
314 
315   switch (addr->ncpaddr_family) {
316   case AF_INET:
317     host4->sin_family = AF_INET;
318     host4->sin_len = sizeof(*host4);
319     host4->sin_addr = addr->ncpaddr_ip4addr;
320     break;
321 
322 #ifndef NOINET6
323   case AF_INET6:
324     host6->sin6_family = AF_INET6;
325     host6->sin6_len = sizeof(*host6);
326     host6->sin6_addr = addr->ncpaddr_ip6addr;
327     break;
328 #endif
329 
330   default:
331     host->ss_family = AF_UNSPEC;
332     break;
333   }
334 }
335 
336 void
337 ncpaddr_setsa(struct ncpaddr *addr, const struct sockaddr *host)
338 {
339   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
340 #ifndef NOINET6
341   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
342 #endif
343 
344   switch (host->sa_family) {
345   case AF_INET:
346     addr->ncpaddr_family = AF_INET;
347     addr->ncpaddr_ip4addr = host4->sin_addr;
348     break;
349 
350 #ifndef NOINET6
351   case AF_INET6:
352     if (IN6_IS_ADDR_V4MAPPED(&host6->sin6_addr)) {
353       addr->ncpaddr_family = AF_INET;
354       addr->ncpaddr_ip4addr.s_addr =
355         *(const u_int32_t *)(host6->sin6_addr.s6_addr + 12);
356     } else {
357       addr->ncpaddr_family = AF_INET6;
358       addr->ncpaddr_ip6addr = host6->sin6_addr;
359     }
360     break;
361 #endif
362 
363   default:
364     addr->ncpaddr_family = AF_UNSPEC;
365   }
366 }
367 
368 static char *
369 ncpaddr_ntowa(const struct ncpaddr *addr)
370 {
371   static char res[NCP_ASCIIBUFFERSIZE];
372 #ifndef NOINET6
373   struct sockaddr_in6 sin6;
374 #endif
375 
376   switch (addr->ncpaddr_family) {
377   case AF_INET:
378     snprintf(res, sizeof res, "%s", inet_ntoa(addr->ncpaddr_ip4addr));
379     return res;
380 
381 #ifndef NOINET6
382   case AF_INET6:
383     memset(&sin6, '\0', sizeof(sin6));
384     sin6.sin6_len = sizeof(sin6);
385     sin6.sin6_family = AF_INET6;
386     sin6.sin6_addr = addr->ncpaddr_ip6addr;
387     adjust_linklocal(&sin6);
388     if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
389                     NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
390       break;
391 
392     return res;
393 #endif
394   }
395 
396   snprintf(res, sizeof res, "<AF_UNSPEC>");
397   return res;
398 }
399 
400 const char *
401 ncpaddr_ntoa(const struct ncpaddr *addr)
402 {
403   return ncpaddr_ntowa(addr);
404 }
405 
406 
407 int
408 ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
409 {
410   struct ncprange range;
411 
412   if (!ncprange_aton(&range, ncp, data))
413     return 0;
414 
415   if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32) {
416     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
417     return 0;
418   }
419 
420 #ifndef NOINET6
421   if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128) {
422     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
423     return 0;
424   }
425 #endif
426 
427   switch (range.ncprange_family) {
428   case AF_INET:
429     addr->ncpaddr_family = range.ncprange_family;
430     addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
431     return 1;
432 
433 #ifndef NOINET6
434   case AF_INET6:
435     addr->ncpaddr_family = range.ncprange_family;
436     addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
437     return 1;
438 #endif
439   }
440 
441   return 0;
442 }
443 
444 void
445 ncprange_init(struct ncprange *range)
446 {
447   range->ncprange_family = AF_UNSPEC;
448 }
449 
450 int
451 ncprange_isset(const struct ncprange *range)
452 {
453   return range->ncprange_family != AF_UNSPEC;
454 }
455 
456 int
457 ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
458 {
459   if (range->ncprange_family != cmp->ncprange_family)
460     return 0;
461 
462   switch (range->ncprange_family) {
463   case AF_INET:
464     if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
465       return 0;
466     return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
467 
468 #ifndef NOINET6
469   case AF_INET6:
470     if (range->ncprange_ip6width != cmp->ncprange_ip6width)
471       return 0;
472     return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
473                    sizeof range->ncprange_ip6addr);
474 #endif
475 
476   case AF_UNSPEC:
477     return 1;
478   }
479 
480   return 0;
481 }
482 
483 int
484 ncprange_isdefault(const struct ncprange *range)
485 {
486   switch (range->ncprange_family) {
487   case AF_INET:
488     if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
489       return 1;
490     break;
491 
492 #ifndef NOINET6
493   case AF_INET6:
494     if (range->ncprange_ip6width == 0 &&
495         IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
496       return 1;
497     break;
498 #endif
499   }
500 
501   return 0;
502 }
503 
504 void
505 ncprange_setdefault(struct ncprange *range, int af)
506 {
507   memset(range, '\0', sizeof *range);
508   range->ncprange_family = af;
509 }
510 
511 int
512 ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
513 {
514 #ifndef NOINET6
515   const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
516   const u_char *addrp, *rangep;
517   int bits;
518 #endif
519 
520   if (range->ncprange_family != addr->ncpaddr_family)
521     return 0;
522 
523   switch (range->ncprange_family) {
524   case AF_INET:
525     return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
526              range->ncprange_ip4mask.s_addr);
527 
528 #ifndef NOINET6
529   case AF_INET6:
530     rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
531     addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
532 
533     for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
534       if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
535         return 0;
536 
537     return 1;
538 #endif
539   }
540 
541   return 0;
542 }
543 
544 int
545 ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
546 {
547   switch (range->ncprange_family) {
548   case AF_INET:
549     return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
550              range->ncprange_ip4mask.s_addr);
551   }
552 
553   return 0;
554 }
555 
556 void
557 ncprange_copy(struct ncprange *range, const struct ncprange *from)
558 {
559   switch (from->ncprange_family) {
560   case AF_INET:
561     range->ncprange_family = AF_INET;
562     range->ncprange_ip4addr = from->ncprange_ip4addr;
563     range->ncprange_ip4mask = from->ncprange_ip4mask;
564     range->ncprange_ip4width = from->ncprange_ip4width;
565     break;
566 
567 #ifndef NOINET6
568   case AF_INET6:
569     range->ncprange_family = AF_INET6;
570     range->ncprange_ip6addr = from->ncprange_ip6addr;
571     range->ncprange_ip6width = from->ncprange_ip6width;
572     break;
573 #endif
574 
575   default:
576     range->ncprange_family = AF_UNSPEC;
577   }
578 }
579 
580 void
581 ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
582 {
583   ncprange_sethost(range, addr);
584   ncprange_setwidth(range, width);
585 }
586 
587 void
588 ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
589 {
590   switch (from->ncpaddr_family) {
591   case AF_INET:
592     range->ncprange_family = AF_INET;
593     range->ncprange_ip4addr = from->ncpaddr_ip4addr;
594     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
595     range->ncprange_ip4width = 32;
596     break;
597 
598 #ifndef NOINET6
599   case AF_INET6:
600     range->ncprange_family = AF_INET6;
601     range->ncprange_ip6addr = from->ncpaddr_ip6addr;
602     range->ncprange_ip6width = 128;
603     break;
604 #endif
605 
606   default:
607     range->ncprange_family = AF_UNSPEC;
608   }
609 }
610 
611 int
612 ncprange_ishost(const struct ncprange *range)
613 {
614   switch (range->ncprange_family) {
615   case AF_INET:
616     return range->ncprange_ip4width == 32;
617 #ifndef NOINET6
618   case AF_INET6:
619     return range->ncprange_ip6width == 128;
620 #endif
621   }
622 
623   return (0);
624 }
625 
626 int
627 ncprange_setwidth(struct ncprange *range, int width)
628 {
629   switch (range->ncprange_family) {
630   case AF_INET:
631     if (width < 0 || width > 32)
632       break;
633     range->ncprange_ip4width = width;
634     range->ncprange_ip4mask = bits2mask4(width);
635     break;
636 
637 #ifndef NOINET6
638   case AF_INET6:
639     if (width < 0 || width > 128)
640       break;
641     range->ncprange_ip6width = width;
642     break;
643 #endif
644 
645   case AF_UNSPEC:
646     return 1;
647   }
648 
649   return 0;
650 }
651 
652 void
653 ncprange_setip4host(struct ncprange *range, struct in_addr from)
654 {
655   range->ncprange_family = AF_INET;
656   range->ncprange_ip4addr = from;
657   range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
658   range->ncprange_ip4width = 32;
659 }
660 
661 void
662 ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
663 {
664   range->ncprange_family = AF_INET;
665   range->ncprange_ip4addr = from;
666   range->ncprange_ip4mask = msk;
667   range->ncprange_ip4width = mask42bits(msk);
668 }
669 
670 
671 int
672 ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
673 {
674   if (range->ncprange_family != AF_INET)
675     return 0;
676   range->ncprange_ip4mask = mask;
677   range->ncprange_ip4width = mask42bits(mask);
678   return 1;
679 }
680 
681 void
682 ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
683                const struct sockaddr *mask)
684 {
685   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
686   const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
687 #ifndef NOINET6
688   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
689   const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
690 #endif
691 
692   switch (host->sa_family) {
693   case AF_INET:
694     range->ncprange_family = AF_INET;
695     range->ncprange_ip4addr = host4->sin_addr;
696     if (mask4) {
697       range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
698       range->ncprange_ip4width = mask42bits(mask4->sin_addr);
699     } else {
700       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
701       range->ncprange_ip4width = 32;
702     }
703     break;
704 
705 #ifndef NOINET6
706   case AF_INET6:
707     range->ncprange_family = AF_INET6;
708     range->ncprange_ip6addr = host6->sin6_addr;
709     range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
710     break;
711 #endif
712 
713   default:
714     range->ncprange_family = AF_UNSPEC;
715   }
716 }
717 
718 void
719 ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
720                struct sockaddr_storage *mask)
721 {
722   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
723   struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
724 #ifndef NOINET6
725   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
726   struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
727 #endif
728 
729   memset(host, '\0', sizeof(*host));
730   if (mask)
731     memset(mask, '\0', sizeof(*mask));
732 
733   switch (range->ncprange_family) {
734   case AF_INET:
735     host4->sin_family = AF_INET;
736     host4->sin_len = sizeof(*host4);
737     host4->sin_addr = range->ncprange_ip4addr;
738     if (mask4) {
739       mask4->sin_family = AF_INET;
740       mask4->sin_len = sizeof(*host4);
741       mask4->sin_addr = range->ncprange_ip4mask;
742     }
743     break;
744 
745 #ifndef NOINET6
746   case AF_INET6:
747     host6->sin6_family = AF_INET6;
748     host6->sin6_len = sizeof(*host6);
749     host6->sin6_addr = range->ncprange_ip6addr;
750     if (mask6) {
751       mask6->sin6_family = AF_INET6;
752       mask6->sin6_len = sizeof(*host6);
753       mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
754     }
755     break;
756 #endif
757 
758   default:
759     host->ss_family = AF_UNSPEC;
760     if (mask)
761       mask->ss_family = AF_UNSPEC;
762     break;
763   }
764 }
765 
766 int
767 ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
768 {
769   switch (range->ncprange_family) {
770   case AF_INET:
771     addr->ncpaddr_family = AF_INET;
772     addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
773     return 1;
774 #ifndef NOINET6
775   case AF_INET6:
776     addr->ncpaddr_family = AF_INET6;
777     addr->ncpaddr_ip6addr =  range->ncprange_ip6addr;
778     return 1;
779 #endif
780   }
781 
782   return 0;
783 }
784 
785 int
786 ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
787 {
788   if (range->ncprange_family != AF_INET)
789     return 0;
790 
791   *addr = range->ncprange_ip4addr;
792   return 1;
793 }
794 
795 int
796 ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
797 {
798   switch (range->ncprange_family) {
799   case AF_INET:
800     *mask = range->ncprange_ip4mask;
801     return 1;
802   }
803 
804   return 0;
805 }
806 
807 int
808 ncprange_getwidth(const struct ncprange *range, int *width)
809 {
810   switch (range->ncprange_family) {
811   case AF_INET:
812     *width = range->ncprange_ip4width;
813     return 1;
814 #ifndef NOINET6
815   case AF_INET6:
816     *width = range->ncprange_ip6width;
817     return 1;
818 #endif
819   }
820 
821   return 0;
822 }
823 
824 const char *
825 ncprange_ntoa(const struct ncprange *range)
826 {
827   char *res;
828   struct ncpaddr addr;
829   int len;
830 
831   if (!ncprange_getaddr(range, &addr))
832     return "<AF_UNSPEC>";
833 
834   res = ncpaddr_ntowa(&addr);
835   len = strlen(res);
836   if (len >= NCP_ASCIIBUFFERSIZE - 1)
837     return res;
838 
839   switch (range->ncprange_family) {
840   case AF_INET:
841     if (range->ncprange_ip4width == -1) {
842       /* A non-contiguous mask */
843       for (; len >= 3; res[len -= 2] = '\0')
844         if (strcmp(res + len - 2, ".0"))
845           break;
846       snprintf(res + len, sizeof res - len, "&0x%08lx",
847                (unsigned long)ntohl(range->ncprange_ip4mask.s_addr));
848     } else if (range->ncprange_ip4width < 32)
849       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
850 
851     return res;
852 
853 #ifndef NOINET6
854   case AF_INET6:
855     if (range->ncprange_ip6width != 128)
856       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
857 
858     return res;
859 #endif
860   }
861 
862   return "<AF_UNSPEC>";
863 }
864 
865 #ifndef NOINET6
866 int
867 ncprange_scopeid(const struct ncprange *range)
868 {
869   const struct in6_addr *sin6;
870   int scopeid = -1;
871 
872   if (range->ncprange_family == AF_INET6) {
873     sin6 = &range->ncprange_ip6addr;
874     if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
875       if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
876         scopeid = -1;
877   }
878 
879   return scopeid;
880 }
881 #endif
882 
883 int
884 ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
885 {
886   int bits, len;
887   char *wp;
888   const char *cp;
889   char *s;
890 
891   len = strcspn(data, "/");
892 
893   if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
894     range->ncprange_family = AF_INET;
895     range->ncprange_ip4addr = ncp->ipcp.peer_ip;
896     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
897     range->ncprange_ip4width = 32;
898     return 1;
899 #ifndef NOINET6
900   } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
901     ncprange_sethost(range, &ncp->ipv6cp.hisaddr);
902     return 1;
903 #endif
904   } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
905     range->ncprange_family = AF_INET;
906     range->ncprange_ip4addr = ncp->ipcp.my_ip;
907     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
908     range->ncprange_ip4width = 32;
909     return 1;
910 #ifndef NOINET6
911   } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
912     ncprange_sethost(range, &ncp->ipv6cp.myaddr);
913     return 1;
914 #endif
915   } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
916     range->ncprange_family = AF_INET;
917     range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
918     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
919     range->ncprange_ip4width = 32;
920     return 1;
921   } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
922     range->ncprange_family = AF_INET;
923     range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
924     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
925     range->ncprange_ip4width = 32;
926     return 1;
927   }
928 
929   s = (char *)alloca(len + 1);
930   strncpy(s, data, len);
931   s[len] = '\0';
932   bits = -1;
933 
934   if (data[len] != '\0') {
935     bits = strtol(data + len + 1, &wp, 0);
936     if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
937       log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
938       return 0;
939     }
940   }
941 
942   if ((cp = strchr(data, ':')) == NULL) {
943     range->ncprange_family = AF_INET;
944 
945     range->ncprange_ip4addr = GetIpAddr(s);
946 
947     if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
948       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
949       return 0;
950     }
951 
952     if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
953       range->ncprange_ip4mask.s_addr = INADDR_ANY;
954       range->ncprange_ip4width = 0;
955     } else if (bits == -1) {
956       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
957       range->ncprange_ip4width = 32;
958     } else if (bits > 32) {
959       log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
960       return 0;
961     } else {
962       range->ncprange_ip4mask = bits2mask4(bits);
963       range->ncprange_ip4width = bits;
964     }
965 
966     return 1;
967 #ifndef NOINET6
968   } else if (strchr(cp + 1, ':') != NULL) {
969     range->ncprange_family = AF_INET6;
970 
971     if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
972       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
973       return 0;
974     }
975 
976     if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
977       range->ncprange_ip6width = 0;
978     else
979       range->ncprange_ip6width = (bits == -1) ? 128 : bits;
980     return 1;
981 #endif
982   }
983 
984   return 0;
985 }
986