xref: /freebsd/usr.sbin/ppp/ncpaddr.c (revision 30949fd4b52d91188c23cb2ca0d130d89bb1678c)
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_isdefault(const struct ncpaddr *addr)
189 {
190   switch (addr->ncpaddr_family) {
191   case AF_INET:
192     if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
193       return 1;
194     break;
195 
196 #ifndef NOINET6
197   case AF_INET6:
198     if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
199       return 1;
200     break;
201 #endif
202   }
203 
204   return 0;
205 }
206 
207 int
208 ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
209 {
210   if (addr->ncpaddr_family != cmp->ncpaddr_family)
211     return 0;
212 
213   switch (addr->ncpaddr_family) {
214   case AF_INET:
215     return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
216 
217 #ifndef NOINET6
218   case AF_INET6:
219     return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
220                    sizeof addr->ncpaddr_ip6addr);
221 #endif
222 
223   case AF_UNSPEC:
224     return 1;
225   }
226 
227   return 0;
228 }
229 
230 void
231 ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
232 {
233   switch (from->ncpaddr_family) {
234   case AF_INET:
235     addr->ncpaddr_family = AF_INET;
236     addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
237     break;
238 #ifndef NOINET6
239   case AF_INET6:
240     addr->ncpaddr_family = AF_INET6;
241     addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
242     break;
243 #endif
244   default:
245     addr->ncpaddr_family = AF_UNSPEC;
246   }
247 }
248 
249 void
250 ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
251 {
252   addr->ncpaddr_family = AF_INET;
253   addr->ncpaddr_ip4addr.s_addr = ip;
254 }
255 
256 int
257 ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
258 {
259   if (addr->ncpaddr_family != AF_INET)
260     return 0;
261   *ip = addr->ncpaddr_ip4addr.s_addr;
262   return 1;
263 }
264 
265 void
266 ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
267 {
268   addr->ncpaddr_family = AF_INET;
269   addr->ncpaddr_ip4addr = ip;
270 }
271 
272 int
273 ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
274 {
275   if (addr->ncpaddr_family != AF_INET)
276     return 0;
277   *ip = addr->ncpaddr_ip4addr;
278   return 1;
279 }
280 
281 #ifndef NOINET6
282 void
283 ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
284 {
285   addr->ncpaddr_family = AF_INET6;
286   addr->ncpaddr_ip6addr = *ip6;
287 }
288 
289 int
290 ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
291 {
292   if (addr->ncpaddr_family != AF_INET6)
293     return 0;
294   *ip6 = addr->ncpaddr_ip6addr;
295   return 1;
296 }
297 #endif
298 
299 void
300 ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
301 {
302   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
303 #ifndef NOINET6
304   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
305 #endif
306 
307   memset(host, '\0', sizeof(*host));
308 
309   switch (addr->ncpaddr_family) {
310   case AF_INET:
311     host4->sin_family = AF_INET;
312     host4->sin_len = sizeof(*host4);
313     host4->sin_addr = addr->ncpaddr_ip4addr;
314     break;
315 
316 #ifndef NOINET6
317   case AF_INET6:
318     host6->sin6_family = AF_INET6;
319     host6->sin6_len = sizeof(*host6);
320     host6->sin6_addr = addr->ncpaddr_ip6addr;
321     adjust_linklocal(host6);
322     break;
323 #endif
324 
325   default:
326     host->ss_family = AF_UNSPEC;
327     break;
328   }
329 }
330 
331 void
332 ncpaddr_setsa(struct ncpaddr *addr, const struct sockaddr *host)
333 {
334   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
335 #ifndef NOINET6
336   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
337 #endif
338 
339   switch (host->sa_family) {
340   case AF_INET:
341     addr->ncpaddr_family = AF_INET;
342     addr->ncpaddr_ip4addr = host4->sin_addr;
343     break;
344 
345 #ifndef NOINET6
346   case AF_INET6:
347     if (IN6_IS_ADDR_V4MAPPED(&host6->sin6_addr)) {
348       addr->ncpaddr_family = AF_INET;
349       addr->ncpaddr_ip4addr.s_addr =
350         *(const u_int32_t *)(host6->sin6_addr.s6_addr + 12);
351     } else {
352       addr->ncpaddr_family = AF_INET6;
353       addr->ncpaddr_ip6addr = host6->sin6_addr;
354     }
355     break;
356 #endif
357 
358   default:
359     addr->ncpaddr_family = AF_UNSPEC;
360   }
361 }
362 
363 static char *
364 ncpaddr_ntowa(const struct ncpaddr *addr)
365 {
366   static char res[NCP_ASCIIBUFFERSIZE];
367 #ifndef NOINET6
368   struct sockaddr_in6 sin6;
369 #endif
370 
371   switch (addr->ncpaddr_family) {
372   case AF_INET:
373     snprintf(res, sizeof res, "%s", inet_ntoa(addr->ncpaddr_ip4addr));
374     return res;
375 
376 #ifndef NOINET6
377   case AF_INET6:
378     memset(&sin6, '\0', sizeof(sin6));
379     sin6.sin6_len = sizeof(sin6);
380     sin6.sin6_family = AF_INET6;
381     sin6.sin6_addr = addr->ncpaddr_ip6addr;
382     adjust_linklocal(&sin6);
383     if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
384                     NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
385       break;
386 
387     return res;
388 #endif
389   }
390 
391   snprintf(res, sizeof res, "<AF_UNSPEC>");
392   return res;
393 }
394 
395 const char *
396 ncpaddr_ntoa(const struct ncpaddr *addr)
397 {
398   return ncpaddr_ntowa(addr);
399 }
400 
401 
402 int
403 ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
404 {
405   struct ncprange range;
406 
407   if (!ncprange_aton(&range, ncp, data))
408     return 0;
409 
410   if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32) {
411     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
412     return 0;
413   }
414 
415 #ifndef NOINET6
416   if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128) {
417     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
418     return 0;
419   }
420 #endif
421 
422   switch (range.ncprange_family) {
423   case AF_INET:
424     addr->ncpaddr_family = range.ncprange_family;
425     addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
426     return 1;
427 
428 #ifndef NOINET6
429   case AF_INET6:
430     addr->ncpaddr_family = range.ncprange_family;
431     addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
432     return 1;
433 #endif
434   }
435 
436   return 0;
437 }
438 
439 void
440 ncprange_init(struct ncprange *range)
441 {
442   range->ncprange_family = AF_UNSPEC;
443 }
444 
445 int
446 ncprange_isset(const struct ncprange *range)
447 {
448   return range->ncprange_family != AF_UNSPEC;
449 }
450 
451 int
452 ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
453 {
454   if (range->ncprange_family != cmp->ncprange_family)
455     return 0;
456 
457   switch (range->ncprange_family) {
458   case AF_INET:
459     if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
460       return 0;
461     return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
462 
463 #ifndef NOINET6
464   case AF_INET6:
465     if (range->ncprange_ip6width != cmp->ncprange_ip6width)
466       return 0;
467     return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
468                    sizeof range->ncprange_ip6addr);
469 #endif
470 
471   case AF_UNSPEC:
472     return 1;
473   }
474 
475   return 0;
476 }
477 
478 int
479 ncprange_isdefault(const struct ncprange *range)
480 {
481   switch (range->ncprange_family) {
482   case AF_INET:
483     if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
484       return 1;
485     break;
486 
487 #ifndef NOINET6
488   case AF_INET6:
489     if (range->ncprange_ip6width == 0 &&
490         IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
491       return 1;
492     break;
493 #endif
494   }
495 
496   return 0;
497 }
498 
499 void
500 ncprange_setdefault(struct ncprange *range, int af)
501 {
502   memset(range, '\0', sizeof *range);
503   range->ncprange_family = af;
504 }
505 
506 int
507 ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
508 {
509 #ifndef NOINET6
510   const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
511   const u_char *addrp, *rangep;
512   int bits;
513 #endif
514 
515   if (range->ncprange_family != addr->ncpaddr_family)
516     return 0;
517 
518   switch (range->ncprange_family) {
519   case AF_INET:
520     return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
521              range->ncprange_ip4mask.s_addr);
522 
523 #ifndef NOINET6
524   case AF_INET6:
525     rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
526     addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
527 
528     for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
529       if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
530         return 0;
531 
532     return 1;
533 #endif
534   }
535 
536   return 0;
537 }
538 
539 int
540 ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
541 {
542   switch (range->ncprange_family) {
543   case AF_INET:
544     return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
545              range->ncprange_ip4mask.s_addr);
546   }
547 
548   return 0;
549 }
550 
551 void
552 ncprange_copy(struct ncprange *range, const struct ncprange *from)
553 {
554   switch (from->ncprange_family) {
555   case AF_INET:
556     range->ncprange_family = AF_INET;
557     range->ncprange_ip4addr = from->ncprange_ip4addr;
558     range->ncprange_ip4mask = from->ncprange_ip4mask;
559     range->ncprange_ip4width = from->ncprange_ip4width;
560     break;
561 
562 #ifndef NOINET6
563   case AF_INET6:
564     range->ncprange_family = AF_INET6;
565     range->ncprange_ip6addr = from->ncprange_ip6addr;
566     range->ncprange_ip6width = from->ncprange_ip6width;
567     break;
568 #endif
569 
570   default:
571     range->ncprange_family = AF_UNSPEC;
572   }
573 }
574 
575 void
576 ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
577 {
578   ncprange_sethost(range, addr);
579   ncprange_setwidth(range, width);
580 }
581 
582 void
583 ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
584 {
585   switch (from->ncpaddr_family) {
586   case AF_INET:
587     range->ncprange_family = AF_INET;
588     range->ncprange_ip4addr = from->ncpaddr_ip4addr;
589     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
590     range->ncprange_ip4width = 32;
591     break;
592 
593 #ifndef NOINET6
594   case AF_INET6:
595     range->ncprange_family = AF_INET6;
596     range->ncprange_ip6addr = from->ncpaddr_ip6addr;
597     range->ncprange_ip6width = 128;
598     break;
599 #endif
600 
601   default:
602     range->ncprange_family = AF_UNSPEC;
603   }
604 }
605 
606 int
607 ncprange_setwidth(struct ncprange *range, int width)
608 {
609   switch (range->ncprange_family) {
610   case AF_INET:
611     if (width < 0 || width > 32)
612       break;
613     range->ncprange_ip4width = width;
614     range->ncprange_ip4mask = bits2mask4(width);
615     break;
616 
617 #ifndef NOINET6
618   case AF_INET6:
619     if (width < 0 || width > 128)
620       break;
621     range->ncprange_ip6width = width;
622     break;
623 #endif
624 
625   case AF_UNSPEC:
626     return 1;
627   }
628 
629   return 0;
630 }
631 
632 void
633 ncprange_setip4host(struct ncprange *range, struct in_addr from)
634 {
635   range->ncprange_family = AF_INET;
636   range->ncprange_ip4addr = from;
637   range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
638   range->ncprange_ip4width = 32;
639 }
640 
641 void
642 ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
643 {
644   range->ncprange_family = AF_INET;
645   range->ncprange_ip4addr = from;
646   range->ncprange_ip4mask = msk;
647   range->ncprange_ip4width = mask42bits(msk);
648 }
649 
650 
651 int
652 ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
653 {
654   if (range->ncprange_family != AF_INET)
655     return 0;
656   range->ncprange_ip4mask = mask;
657   range->ncprange_ip4width = mask42bits(mask);
658   return 1;
659 }
660 
661 void
662 ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
663                const struct sockaddr *mask)
664 {
665   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
666   const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
667 #ifndef NOINET6
668   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
669   const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
670 #endif
671 
672   switch (host->sa_family) {
673   case AF_INET:
674     range->ncprange_family = AF_INET;
675     range->ncprange_ip4addr = host4->sin_addr;
676     if (mask4) {
677       range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
678       range->ncprange_ip4width = mask42bits(mask4->sin_addr);
679     } else {
680       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
681       range->ncprange_ip4width = 32;
682     }
683     break;
684 
685 #ifndef NOINET6
686   case AF_INET6:
687     range->ncprange_family = AF_INET6;
688     range->ncprange_ip6addr = host6->sin6_addr;
689     range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
690     break;
691 #endif
692 
693   default:
694     range->ncprange_family = AF_UNSPEC;
695   }
696 }
697 
698 void
699 ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
700                struct sockaddr_storage *mask)
701 {
702   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
703   struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
704 #ifndef NOINET6
705   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
706   struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
707 #endif
708 
709   memset(host, '\0', sizeof(*host));
710   if (mask)
711     memset(mask, '\0', sizeof(*mask));
712 
713   switch (range->ncprange_family) {
714   case AF_INET:
715     host4->sin_family = AF_INET;
716     host4->sin_len = sizeof(*host4);
717     host4->sin_addr = range->ncprange_ip4addr;
718     if (mask4) {
719       mask4->sin_family = AF_INET;
720       mask4->sin_len = sizeof(*host4);
721       mask4->sin_addr = bits2mask4(range->ncprange_ip4width);
722     }
723     break;
724 
725 #ifndef NOINET6
726   case AF_INET6:
727     host6->sin6_family = AF_INET6;
728     host6->sin6_len = sizeof(*host6);
729     host6->sin6_addr = range->ncprange_ip6addr;
730     adjust_linklocal(host6);
731     if (mask6) {
732       mask6->sin6_family = AF_INET6;
733       mask6->sin6_len = sizeof(*host6);
734       mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
735     }
736     break;
737 #endif
738 
739   default:
740     host->ss_family = AF_UNSPEC;
741     if (mask)
742       mask->ss_family = AF_UNSPEC;
743     break;
744   }
745 }
746 
747 int
748 ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
749 {
750   switch (range->ncprange_family) {
751   case AF_INET:
752     addr->ncpaddr_family = AF_INET;
753     addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
754     return 1;
755 #ifndef NOINET6
756   case AF_INET6:
757     addr->ncpaddr_family = AF_INET6;
758     addr->ncpaddr_ip6addr =  range->ncprange_ip6addr;
759     return 1;
760 #endif
761   }
762 
763   return 0;
764 }
765 
766 int
767 ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
768 {
769   if (range->ncprange_family != AF_INET)
770     return 0;
771 
772   *addr = range->ncprange_ip4addr;
773   return 1;
774 }
775 
776 int
777 ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
778 {
779   switch (range->ncprange_family) {
780   case AF_INET:
781     *mask = range->ncprange_ip4mask;
782     return 1;
783   }
784 
785   return 0;
786 }
787 
788 int
789 ncprange_getwidth(const struct ncprange *range, int *width)
790 {
791   switch (range->ncprange_family) {
792   case AF_INET:
793     *width = range->ncprange_ip4width;
794     return 1;
795 #ifndef NOINET6
796   case AF_INET6:
797     *width = range->ncprange_ip6width;
798     return 1;
799 #endif
800   }
801 
802   return 0;
803 }
804 
805 const char *
806 ncprange_ntoa(const struct ncprange *range)
807 {
808   char *res;
809   struct ncpaddr addr;
810   int len;
811 
812   if (!ncprange_getaddr(range, &addr))
813     return "<AF_UNSPEC>";
814 
815   res = ncpaddr_ntowa(&addr);
816   len = strlen(res);
817   if (len >= NCP_ASCIIBUFFERSIZE - 1)
818     return res;
819 
820   switch (range->ncprange_family) {
821   case AF_INET:
822     if (range->ncprange_ip4width == -1) {
823       /* A non-contiguous mask */
824       for (; len >= 3; res[len -= 2] = '\0')
825         if (strcmp(res + len - 2, ".0"))
826           break;
827       snprintf(res + len, sizeof res - len, "&0x%08lx",
828                ntohl(range->ncprange_ip4mask.s_addr));
829     } else if (range->ncprange_ip4width < 32)
830       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
831 
832     return res;
833 
834 #ifndef NOINET6
835   case AF_INET6:
836     if (range->ncprange_ip6width != 128)
837       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
838 
839     return res;
840 #endif
841   }
842 
843   return "<AF_UNSPEC>";
844 }
845 
846 #ifndef NOINET6
847 int
848 ncprange_scopeid(const struct ncprange *range)
849 {
850   const struct in6_addr *sin6;
851   int scopeid = -1;
852 
853   if (range->ncprange_family == AF_INET6) {
854     sin6 = &range->ncprange_ip6addr;
855     if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
856       if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
857         scopeid = -1;
858   }
859 
860   return scopeid;
861 }
862 #endif
863 
864 int
865 ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
866 {
867   int bits, len;
868   char *wp;
869   const char *cp;
870   char *s;
871 
872   len = strcspn(data, "/");
873 
874   if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
875     range->ncprange_family = AF_INET;
876     range->ncprange_ip4addr = ncp->ipcp.peer_ip;
877     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
878     range->ncprange_ip4width = 32;
879     return 1;
880 #ifndef NOINET6
881   } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
882     ncprange_sethost(range, &ncp->ipv6cp.hisaddr);
883     return 1;
884 #endif
885   } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
886     range->ncprange_family = AF_INET;
887     range->ncprange_ip4addr = ncp->ipcp.my_ip;
888     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
889     range->ncprange_ip4width = 32;
890     return 1;
891 #ifndef NOINET6
892   } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
893     ncprange_sethost(range, &ncp->ipv6cp.myaddr);
894     return 1;
895 #endif
896   } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
897     range->ncprange_family = AF_INET;
898     range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
899     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
900     range->ncprange_ip4width = 32;
901     return 1;
902   } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
903     range->ncprange_family = AF_INET;
904     range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
905     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
906     range->ncprange_ip4width = 32;
907     return 1;
908   }
909 
910   s = (char *)alloca(len + 1);
911   strncpy(s, data, len);
912   s[len] = '\0';
913   bits = -1;
914 
915   if (data[len] != '\0') {
916     bits = strtol(data + len + 1, &wp, 0);
917     if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
918       log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
919       return 0;
920     }
921   }
922 
923   if ((cp = strchr(data, ':')) == NULL) {
924     range->ncprange_family = AF_INET;
925 
926     range->ncprange_ip4addr = GetIpAddr(s);
927 
928     if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
929       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
930       return 0;
931     }
932 
933     if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
934       range->ncprange_ip4mask.s_addr = INADDR_ANY;
935       range->ncprange_ip4width = 0;
936     } else if (data[len] == '\0') {
937       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
938       range->ncprange_ip4width = 32;
939     } else {
940       if (bits > 32) {
941         log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
942         return 0;
943       }
944       range->ncprange_ip4mask = bits2mask4(bits);
945       range->ncprange_ip4width = bits;
946     }
947 
948     return 1;
949 #ifndef NOINET6
950   } else if (strchr(cp + 1, ':') != NULL) {
951     range->ncprange_family = AF_INET6;
952 
953     if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
954       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
955       return 0;
956     }
957 
958     if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
959       range->ncprange_ip6width = 0;
960     else
961       range->ncprange_ip6width = (bits == -1) ? 128 : bits;
962     return 1;
963 #endif
964   }
965 
966   return 0;
967 }
968