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