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