ncpaddr.c revision 116654
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: head/usr.sbin/ppp/ncpaddr.c 116654 2003-06-21 10:14:52Z ume $
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#define	NCP_ASCIIBUFFERSIZE	52
80
81static struct in_addr
82bits2mask4(int bits)
83{
84  struct in_addr result;
85  u_int32_t bit = 0x80000000;
86
87  result.s_addr = 0;
88
89  while (bits) {
90    result.s_addr |= bit;
91    bit >>= 1;
92    bits--;
93  }
94
95  result.s_addr = htonl(result.s_addr);
96  return result;
97}
98
99static int
100mask42bits(struct in_addr mask)
101{
102  u_int32_t msk = ntohl(mask.s_addr);
103  u_int32_t tst;
104  int ret;
105
106  for (ret = 32, tst = 1; tst; ret--, tst <<= 1)
107    if (msk & tst)
108      break;
109
110  for (tst <<= 1; tst; tst <<= 1)
111    if (!(msk & tst))
112      break;
113
114  return tst ? -1 : ret;
115}
116
117#ifndef NOINET6
118static struct in6_addr
119bits2mask6(int bits)
120{
121  struct in6_addr result;
122  u_int32_t bit = 0x80;
123  u_char *c = result.s6_addr;
124
125  memset(&result, '\0', sizeof result);
126
127  while (bits) {
128    if (bit == 0) {
129      bit = 0x80;
130      c++;
131    }
132    *c |= bit;
133    bit >>= 1;
134    bits--;
135  }
136
137  return result;
138}
139
140static int
141mask62bits(const struct in6_addr *mask)
142{
143  const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
144  const u_char *c, *p, *end;
145  int masklen, m;
146
147  p = (const u_char *)mask;
148  for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
149    masklen += 8;
150
151  if (p < end) {
152    for (c = masks, m = 0; c < masks + sizeof masks; c++, m++)
153      if (*c == *p) {
154        masklen += m;
155        break;
156      }
157  }
158
159  return masklen;
160}
161
162static void
163adjust_linklocal(struct sockaddr_in6 *sin6)
164{
165    /* XXX: ?????!?!?!!!!!  This is horrible ! */
166#if 0
167    /*
168     * The kernel does not understand sin6_scope_id for routing at this moment.
169     * We should rather keep the embedded ID.
170     * jinmei@kame.net, 20011026
171     */
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#endif
179}
180#endif
181
182void
183ncpaddr_init(struct ncpaddr *addr)
184{
185  addr->ncpaddr_family = AF_UNSPEC;
186}
187
188int
189ncpaddr_isset(const struct ncpaddr *addr)
190{
191  return addr->ncpaddr_family != AF_UNSPEC;
192}
193
194int
195ncpaddr_isdefault(const struct ncpaddr *addr)
196{
197  switch (addr->ncpaddr_family) {
198  case AF_INET:
199    if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
200      return 1;
201    break;
202
203#ifndef NOINET6
204  case AF_INET6:
205    if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
206      return 1;
207    break;
208#endif
209  }
210
211  return 0;
212}
213
214int
215ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
216{
217  if (addr->ncpaddr_family != cmp->ncpaddr_family)
218    return 0;
219
220  switch (addr->ncpaddr_family) {
221  case AF_INET:
222    return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
223
224#ifndef NOINET6
225  case AF_INET6:
226    return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
227                   sizeof addr->ncpaddr_ip6addr);
228#endif
229
230  case AF_UNSPEC:
231    return 1;
232  }
233
234  return 0;
235}
236
237void
238ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
239{
240  switch (from->ncpaddr_family) {
241  case AF_INET:
242    addr->ncpaddr_family = AF_INET;
243    addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
244    break;
245#ifndef NOINET6
246  case AF_INET6:
247    addr->ncpaddr_family = AF_INET6;
248    addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
249    break;
250#endif
251  default:
252    addr->ncpaddr_family = AF_UNSPEC;
253  }
254}
255
256void
257ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
258{
259  addr->ncpaddr_family = AF_INET;
260  addr->ncpaddr_ip4addr.s_addr = ip;
261}
262
263int
264ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
265{
266  if (addr->ncpaddr_family != AF_INET)
267    return 0;
268  *ip = addr->ncpaddr_ip4addr.s_addr;
269  return 1;
270}
271
272void
273ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
274{
275  addr->ncpaddr_family = AF_INET;
276  addr->ncpaddr_ip4addr = ip;
277}
278
279int
280ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
281{
282  if (addr->ncpaddr_family != AF_INET)
283    return 0;
284  *ip = addr->ncpaddr_ip4addr;
285  return 1;
286}
287
288#ifndef NOINET6
289void
290ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
291{
292  addr->ncpaddr_family = AF_INET6;
293  addr->ncpaddr_ip6addr = *ip6;
294}
295
296int
297ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
298{
299  if (addr->ncpaddr_family != AF_INET6)
300    return 0;
301  *ip6 = addr->ncpaddr_ip6addr;
302  return 1;
303}
304#endif
305
306void
307ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
308{
309  struct sockaddr_in *host4 = (struct sockaddr_in *)host;
310#ifndef NOINET6
311  struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
312#endif
313
314  memset(host, '\0', sizeof(*host));
315
316  switch (addr->ncpaddr_family) {
317  case AF_INET:
318    host4->sin_family = AF_INET;
319    host4->sin_len = sizeof(*host4);
320    host4->sin_addr = addr->ncpaddr_ip4addr;
321    break;
322
323#ifndef NOINET6
324  case AF_INET6:
325    host6->sin6_family = AF_INET6;
326    host6->sin6_len = sizeof(*host6);
327    host6->sin6_addr = addr->ncpaddr_ip6addr;
328    break;
329#endif
330
331  default:
332    host->ss_family = AF_UNSPEC;
333    break;
334  }
335}
336
337void
338ncpaddr_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
369static char *
370ncpaddr_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#ifdef NI_WITHSCOPEID
390    if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
391                    NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
392#else
393    if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
394                    NULL, 0, NI_NUMERICHOST) != 0)
395#endif
396      break;
397
398    return res;
399#endif
400  }
401
402  snprintf(res, sizeof res, "<AF_UNSPEC>");
403  return res;
404}
405
406const char *
407ncpaddr_ntoa(const struct ncpaddr *addr)
408{
409  return ncpaddr_ntowa(addr);
410}
411
412
413int
414ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
415{
416  struct ncprange range;
417
418  if (!ncprange_aton(&range, ncp, data))
419    return 0;
420
421  if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32 &&
422      range.ncprange_ip4addr.s_addr != INADDR_ANY) {
423    log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
424    return 0;
425  }
426
427#ifndef NOINET6
428  if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128 &&
429      !IN6_IS_ADDR_UNSPECIFIED(&range.ncprange_ip6addr)) {
430    log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
431    return 0;
432  }
433#endif
434
435  switch (range.ncprange_family) {
436  case AF_INET:
437    addr->ncpaddr_family = range.ncprange_family;
438    addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
439    return 1;
440
441#ifndef NOINET6
442  case AF_INET6:
443    addr->ncpaddr_family = range.ncprange_family;
444    addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
445    return 1;
446#endif
447  }
448
449  return 0;
450}
451
452void
453ncprange_init(struct ncprange *range)
454{
455  range->ncprange_family = AF_UNSPEC;
456}
457
458int
459ncprange_isset(const struct ncprange *range)
460{
461  return range->ncprange_family != AF_UNSPEC;
462}
463
464int
465ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
466{
467  if (range->ncprange_family != cmp->ncprange_family)
468    return 0;
469
470  switch (range->ncprange_family) {
471  case AF_INET:
472    if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
473      return 0;
474    return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
475
476#ifndef NOINET6
477  case AF_INET6:
478    if (range->ncprange_ip6width != cmp->ncprange_ip6width)
479      return 0;
480    return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
481                   sizeof range->ncprange_ip6addr);
482#endif
483
484  case AF_UNSPEC:
485    return 1;
486  }
487
488  return 0;
489}
490
491int
492ncprange_isdefault(const struct ncprange *range)
493{
494  switch (range->ncprange_family) {
495  case AF_INET:
496    if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
497      return 1;
498    break;
499
500#ifndef NOINET6
501  case AF_INET6:
502    if (range->ncprange_ip6width == 0 &&
503        IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
504      return 1;
505    break;
506#endif
507  }
508
509  return 0;
510}
511
512void
513ncprange_setdefault(struct ncprange *range, int af)
514{
515  memset(range, '\0', sizeof *range);
516  range->ncprange_family = af;
517}
518
519int
520ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
521{
522#ifndef NOINET6
523  const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
524  const u_char *addrp, *rangep;
525  int bits;
526#endif
527
528  if (range->ncprange_family != addr->ncpaddr_family)
529    return 0;
530
531  switch (range->ncprange_family) {
532  case AF_INET:
533    return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
534             range->ncprange_ip4mask.s_addr);
535
536#ifndef NOINET6
537  case AF_INET6:
538    rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
539    addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
540
541    for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
542      if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
543        return 0;
544
545    return 1;
546#endif
547  }
548
549  return 0;
550}
551
552int
553ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
554{
555  switch (range->ncprange_family) {
556  case AF_INET:
557    return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
558             range->ncprange_ip4mask.s_addr);
559  }
560
561  return 0;
562}
563
564void
565ncprange_copy(struct ncprange *range, const struct ncprange *from)
566{
567  switch (from->ncprange_family) {
568  case AF_INET:
569    range->ncprange_family = AF_INET;
570    range->ncprange_ip4addr = from->ncprange_ip4addr;
571    range->ncprange_ip4mask = from->ncprange_ip4mask;
572    range->ncprange_ip4width = from->ncprange_ip4width;
573    break;
574
575#ifndef NOINET6
576  case AF_INET6:
577    range->ncprange_family = AF_INET6;
578    range->ncprange_ip6addr = from->ncprange_ip6addr;
579    range->ncprange_ip6width = from->ncprange_ip6width;
580    break;
581#endif
582
583  default:
584    range->ncprange_family = AF_UNSPEC;
585  }
586}
587
588void
589ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
590{
591  ncprange_sethost(range, addr);
592  ncprange_setwidth(range, width);
593}
594
595void
596ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
597{
598  switch (from->ncpaddr_family) {
599  case AF_INET:
600    range->ncprange_family = AF_INET;
601    range->ncprange_ip4addr = from->ncpaddr_ip4addr;
602    if (from->ncpaddr_ip4addr.s_addr == INADDR_ANY) {
603      range->ncprange_ip4mask.s_addr = INADDR_ANY;
604      range->ncprange_ip4width = 0;
605    } else {
606      range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
607      range->ncprange_ip4width = 32;
608    }
609    break;
610
611#ifndef NOINET6
612  case AF_INET6:
613    range->ncprange_family = AF_INET6;
614    range->ncprange_ip6addr = from->ncpaddr_ip6addr;
615    range->ncprange_ip6width = 128;
616    break;
617#endif
618
619  default:
620    range->ncprange_family = AF_UNSPEC;
621  }
622}
623
624int
625ncprange_ishost(const struct ncprange *range)
626{
627  switch (range->ncprange_family) {
628  case AF_INET:
629    return range->ncprange_ip4width == 32;
630#ifndef NOINET6
631  case AF_INET6:
632    return range->ncprange_ip6width == 128;
633#endif
634  }
635
636  return (0);
637}
638
639int
640ncprange_setwidth(struct ncprange *range, int width)
641{
642  switch (range->ncprange_family) {
643  case AF_INET:
644    if (width < 0 || width > 32)
645      break;
646    range->ncprange_ip4width = width;
647    range->ncprange_ip4mask = bits2mask4(width);
648    break;
649
650#ifndef NOINET6
651  case AF_INET6:
652    if (width < 0 || width > 128)
653      break;
654    range->ncprange_ip6width = width;
655    break;
656#endif
657
658  case AF_UNSPEC:
659    return 1;
660  }
661
662  return 0;
663}
664
665void
666ncprange_setip4host(struct ncprange *range, struct in_addr from)
667{
668  range->ncprange_family = AF_INET;
669  range->ncprange_ip4addr = from;
670  if (from.s_addr == INADDR_ANY) {
671    range->ncprange_ip4mask.s_addr = INADDR_ANY;
672    range->ncprange_ip4width = 0;
673  } else {
674    range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
675    range->ncprange_ip4width = 32;
676  }
677}
678
679void
680ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
681{
682  range->ncprange_family = AF_INET;
683  range->ncprange_ip4addr = from;
684  range->ncprange_ip4mask = msk;
685  range->ncprange_ip4width = mask42bits(msk);
686}
687
688
689int
690ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
691{
692  if (range->ncprange_family != AF_INET)
693    return 0;
694  range->ncprange_ip4mask = mask;
695  range->ncprange_ip4width = mask42bits(mask);
696  return 1;
697}
698
699void
700ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
701               const struct sockaddr *mask)
702{
703  const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
704  const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
705#ifndef NOINET6
706  const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
707  const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
708#endif
709
710  switch (host->sa_family) {
711  case AF_INET:
712    range->ncprange_family = AF_INET;
713    range->ncprange_ip4addr = host4->sin_addr;
714    if (host4->sin_addr.s_addr == INADDR_ANY) {
715      range->ncprange_ip4mask.s_addr = INADDR_ANY;
716      range->ncprange_ip4width = 0;
717    } else if (mask4 && mask4->sin_family == AF_INET) {
718      range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
719      range->ncprange_ip4width = mask42bits(mask4->sin_addr);
720    } else {
721      range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
722      range->ncprange_ip4width = 32;
723    }
724    break;
725
726#ifndef NOINET6
727  case AF_INET6:
728    range->ncprange_family = AF_INET6;
729    range->ncprange_ip6addr = host6->sin6_addr;
730    if (IN6_IS_ADDR_UNSPECIFIED(&host6->sin6_addr))
731      range->ncprange_ip6width = 0;
732    else
733      range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
734    break;
735#endif
736
737  default:
738    range->ncprange_family = AF_UNSPEC;
739  }
740}
741
742void
743ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
744               struct sockaddr_storage *mask)
745{
746  struct sockaddr_in *host4 = (struct sockaddr_in *)host;
747  struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
748#ifndef NOINET6
749  struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
750  struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
751#endif
752
753  memset(host, '\0', sizeof(*host));
754  if (mask)
755    memset(mask, '\0', sizeof(*mask));
756
757  switch (range->ncprange_family) {
758  case AF_INET:
759    host4->sin_family = AF_INET;
760    host4->sin_len = sizeof(*host4);
761    host4->sin_addr = range->ncprange_ip4addr;
762    if (mask4) {
763      mask4->sin_family = AF_INET;
764      mask4->sin_len = sizeof(*host4);
765      mask4->sin_addr = range->ncprange_ip4mask;
766    }
767    break;
768
769#ifndef NOINET6
770  case AF_INET6:
771    host6->sin6_family = AF_INET6;
772    host6->sin6_len = sizeof(*host6);
773    host6->sin6_addr = range->ncprange_ip6addr;
774    if (mask6) {
775      mask6->sin6_family = AF_INET6;
776      mask6->sin6_len = sizeof(*host6);
777      mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
778    }
779    break;
780#endif
781
782  default:
783    host->ss_family = AF_UNSPEC;
784    if (mask)
785      mask->ss_family = AF_UNSPEC;
786    break;
787  }
788}
789
790int
791ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
792{
793  switch (range->ncprange_family) {
794  case AF_INET:
795    addr->ncpaddr_family = AF_INET;
796    addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
797    return 1;
798#ifndef NOINET6
799  case AF_INET6:
800    addr->ncpaddr_family = AF_INET6;
801    addr->ncpaddr_ip6addr =  range->ncprange_ip6addr;
802    return 1;
803#endif
804  }
805
806  return 0;
807}
808
809int
810ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
811{
812  if (range->ncprange_family != AF_INET)
813    return 0;
814
815  *addr = range->ncprange_ip4addr;
816  return 1;
817}
818
819int
820ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
821{
822  switch (range->ncprange_family) {
823  case AF_INET:
824    *mask = range->ncprange_ip4mask;
825    return 1;
826  }
827
828  return 0;
829}
830
831int
832ncprange_getwidth(const struct ncprange *range, int *width)
833{
834  switch (range->ncprange_family) {
835  case AF_INET:
836    *width = range->ncprange_ip4width;
837    return 1;
838#ifndef NOINET6
839  case AF_INET6:
840    *width = range->ncprange_ip6width;
841    return 1;
842#endif
843  }
844
845  return 0;
846}
847
848const char *
849ncprange_ntoa(const struct ncprange *range)
850{
851  char *res;
852  struct ncpaddr addr;
853  int len;
854
855  if (!ncprange_getaddr(range, &addr))
856    return "<AF_UNSPEC>";
857
858  res = ncpaddr_ntowa(&addr);
859  len = strlen(res);
860  if (len >= NCP_ASCIIBUFFERSIZE - 1)
861    return res;
862
863  switch (range->ncprange_family) {
864  case AF_INET:
865    if (range->ncprange_ip4width == -1) {
866      /* A non-contiguous mask */
867      for (; len >= 3; res[len -= 2] = '\0')
868        if (strcmp(res + len - 2, ".0"))
869          break;
870      snprintf(res + len, sizeof res - len, "&0x%08lx",
871               (unsigned long)ntohl(range->ncprange_ip4mask.s_addr));
872    } else if (range->ncprange_ip4width < 32)
873      snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
874
875    return res;
876
877#ifndef NOINET6
878  case AF_INET6:
879    if (range->ncprange_ip6width != 128)
880      snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
881
882    return res;
883#endif
884  }
885
886  return "<AF_UNSPEC>";
887}
888
889#ifndef NOINET6
890int
891ncprange_scopeid(const struct ncprange *range)
892{
893  const struct in6_addr *sin6;
894  int scopeid = -1;
895
896  if (range->ncprange_family == AF_INET6) {
897    sin6 = &range->ncprange_ip6addr;
898    if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
899      if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
900        scopeid = -1;
901  }
902
903  return scopeid;
904}
905#endif
906
907int
908ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
909{
910  int bits, len;
911  char *wp;
912  const char *cp;
913  char *s;
914
915  len = strcspn(data, "/");
916
917  if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
918    range->ncprange_family = AF_INET;
919    range->ncprange_ip4addr = ncp->ipcp.peer_ip;
920    range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
921    range->ncprange_ip4width = 32;
922    return 1;
923#ifndef NOINET6
924  } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
925    range->ncprange_family = AF_INET6;
926    range->ncprange_ip6addr = ncp->ipv6cp.hisaddr.ncpaddr_ip6addr;
927    range->ncprange_ip6width = 128;
928    return 1;
929#endif
930  } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
931    range->ncprange_family = AF_INET;
932    range->ncprange_ip4addr = ncp->ipcp.my_ip;
933    range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
934    range->ncprange_ip4width = 32;
935    return 1;
936#ifndef NOINET6
937  } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
938    range->ncprange_family = AF_INET6;
939    range->ncprange_ip6addr = ncp->ipv6cp.myaddr.ncpaddr_ip6addr;
940    range->ncprange_ip6width = 128;
941    return 1;
942#endif
943  } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
944    range->ncprange_family = AF_INET;
945    range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
946    range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
947    range->ncprange_ip4width = 32;
948    return 1;
949  } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
950    range->ncprange_family = AF_INET;
951    range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
952    range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
953    range->ncprange_ip4width = 32;
954    return 1;
955  }
956
957  s = (char *)alloca(len + 1);
958  strncpy(s, data, len);
959  s[len] = '\0';
960  bits = -1;
961
962  if (data[len] != '\0') {
963    bits = strtol(data + len + 1, &wp, 0);
964    if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
965      log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
966      return 0;
967    }
968  }
969
970  if ((cp = strchr(data, ':')) == NULL) {
971    range->ncprange_family = AF_INET;
972
973    range->ncprange_ip4addr = GetIpAddr(s);
974
975    if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
976      log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
977      return 0;
978    }
979
980    if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
981      range->ncprange_ip4mask.s_addr = INADDR_ANY;
982      range->ncprange_ip4width = 0;
983    } else if (bits == -1) {
984      range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
985      range->ncprange_ip4width = 32;
986    } else if (bits > 32) {
987      log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
988      return 0;
989    } else {
990      range->ncprange_ip4mask = bits2mask4(bits);
991      range->ncprange_ip4width = bits;
992    }
993
994    return 1;
995#ifndef NOINET6
996  } else if (strchr(cp + 1, ':') != NULL) {
997    range->ncprange_family = AF_INET6;
998
999    if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
1000      log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
1001      return 0;
1002    }
1003
1004    if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
1005      range->ncprange_ip6width = 0;
1006    else
1007      range->ncprange_ip6width = (bits == -1) ? 128 : bits;
1008    return 1;
1009#endif
1010  }
1011
1012  return 0;
1013}
1014