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