iface.c revision 81695
140516Swpaul/*-
240516Swpaul * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
340516Swpaul * All rights reserved.
440516Swpaul *
540516Swpaul * Redistribution and use in source and binary forms, with or without
640516Swpaul * modification, are permitted provided that the following conditions
740516Swpaul * are met:
840516Swpaul * 1. Redistributions of source code must retain the above copyright
940516Swpaul *    notice, this list of conditions and the following disclaimer.
1040516Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1140516Swpaul *    notice, this list of conditions and the following disclaimer in the
1240516Swpaul *    documentation and/or other materials provided with the distribution.
1340516Swpaul *
1440516Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1540516Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1640516Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1740516Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1840516Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1940516Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2040516Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2140516Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2240516Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2340516Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2440516Swpaul * SUCH DAMAGE.
2540516Swpaul *
2640516Swpaul * $FreeBSD: head/usr.sbin/ppp/iface.c 81695 2001-08-15 13:05:39Z brian $
2740516Swpaul */
2840516Swpaul
2940516Swpaul#include <sys/param.h>
3040516Swpaul#include <sys/socket.h>
3140516Swpaul#include <netinet/in.h>
3241273Swpaul#include <net/if.h>
3340516Swpaul#include <net/if_dl.h>
3440516Swpaul#include <net/if_var.h>
3540516Swpaul#include <net/route.h>
3640516Swpaul#include <arpa/inet.h>
3740516Swpaul#include <netinet/in_systm.h>
3840516Swpaul#include <netinet/in_var.h>
3940516Swpaul#include <netinet/ip.h>
4040516Swpaul#ifndef NOINET6
4140516Swpaul#include <netinet6/nd6.h>
4240516Swpaul#endif
4340516Swpaul#include <sys/un.h>
4440516Swpaul
4540516Swpaul#include <errno.h>
4640516Swpaul#include <string.h>
4740516Swpaul#include <stdio.h>
4840516Swpaul#include <stdlib.h>
4940516Swpaul#include <sys/ioctl.h>
5040516Swpaul#include <sys/sysctl.h>
5140516Swpaul#include <termios.h>
5240516Swpaul#include <unistd.h>
5340516Swpaul
5440516Swpaul#include "layer.h"
5540516Swpaul#include "defs.h"
5640516Swpaul#include "command.h"
5740516Swpaul#include "mbuf.h"
5840516Swpaul#include "log.h"
5940516Swpaul#include "id.h"
6040516Swpaul#include "timer.h"
6140516Swpaul#include "fsm.h"
6240516Swpaul#include "iplist.h"
6340516Swpaul#include "lqr.h"
6440516Swpaul#include "hdlc.h"
6540516Swpaul#include "throughput.h"
6640516Swpaul#include "slcompress.h"
6740516Swpaul#include "descriptor.h"
6840516Swpaul#include "ncpaddr.h"
6940516Swpaul#include "ip.h"
7040516Swpaul#include "ipcp.h"
7140516Swpaul#include "filter.h"
7240516Swpaul#include "lcp.h"
7340516Swpaul#include "ccp.h"
7440516Swpaul#include "link.h"
7540516Swpaul#include "mp.h"
7640516Swpaul#ifndef NORADIUS
7740516Swpaul#include "radius.h"
7840516Swpaul#endif
7940516Swpaul#include "ipv6cp.h"
8040516Swpaul#include "ncp.h"
8140516Swpaul#include "bundle.h"
8240516Swpaul#include "prompt.h"
8340516Swpaul#include "iface.h"
8440516Swpaul
8540516Swpaul
8640516Swpaulstruct iface *
8740516Swpauliface_Create(const char *name)
8840516Swpaul{
8940516Swpaul  int mib[6], maxtries, err;
9040516Swpaul  size_t needed, namelen;
9140516Swpaul  char *buf, *ptr, *end;
9240516Swpaul  struct if_msghdr *ifm;
9340516Swpaul  struct ifa_msghdr *ifam;
9440516Swpaul  struct sockaddr_dl *dl;
9540516Swpaul  struct sockaddr *sa[RTAX_MAX];
9640516Swpaul  struct iface *iface;
9740516Swpaul  struct iface_addr *addr;
9840516Swpaul
9940516Swpaul  mib[0] = CTL_NET;
10040516Swpaul  mib[1] = PF_ROUTE;
10140516Swpaul  mib[2] = 0;
10240516Swpaul  mib[3] = 0;
10340516Swpaul  mib[4] = NET_RT_IFLIST;
10440516Swpaul  mib[5] = 0;
10540516Swpaul
10640516Swpaul  maxtries = 20;
10740516Swpaul  err = 0;
10840516Swpaul  do {
10940516Swpaul    if (maxtries-- == 0 || (err && err != ENOMEM)) {
11040516Swpaul      fprintf(stderr, "iface_Create: sysctl: %s\n", strerror(err));
11140516Swpaul      return NULL;
11240516Swpaul    }
11340516Swpaul
11440516Swpaul    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
11540516Swpaul      fprintf(stderr, "iface_Create: sysctl: estimate: %s\n",
11640516Swpaul                strerror(errno));
11740516Swpaul      return NULL;
11840516Swpaul    }
11940516Swpaul
12040516Swpaul    if ((buf = (char *)malloc(needed)) == NULL) {
12140516Swpaul      fprintf(stderr, "iface_Create: malloc failed: %s\n", strerror(errno));
12240516Swpaul      return NULL;
12340516Swpaul    }
12440516Swpaul
12540516Swpaul    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
12640516Swpaul      err = errno;
12740516Swpaul      free(buf);
12840516Swpaul      buf = NULL;
12940516Swpaul    }
13040516Swpaul  } while (buf == NULL);
13140516Swpaul
13240516Swpaul  ptr = buf;
13340516Swpaul  end = buf + needed;
13440516Swpaul  iface = NULL;
13540516Swpaul  namelen = strlen(name);
13641273Swpaul
13740516Swpaul  while (ptr < end && iface == NULL) {
13840516Swpaul    ifm = (struct if_msghdr *)ptr;			/* On if_msghdr */
13940516Swpaul    if (ifm->ifm_type != RTM_IFINFO)
14040516Swpaul      break;
14140516Swpaul    dl = (struct sockaddr_dl *)(ifm + 1);		/* Single _dl at end */
14240516Swpaul    if (dl->sdl_nlen == namelen && !strncmp(name, dl->sdl_data, namelen)) {
14340516Swpaul      iface = (struct iface *)malloc(sizeof *iface);
14440516Swpaul      if (iface == NULL) {
14540516Swpaul        fprintf(stderr, "iface_Create: malloc: %s\n", strerror(errno));
14640516Swpaul        return NULL;
14741243Swpaul      }
14841243Swpaul      iface->name = strdup(name);
14940516Swpaul      iface->index = ifm->ifm_index;
15040516Swpaul      iface->flags = ifm->ifm_flags;
15140516Swpaul      iface->mtu = 0;
15240516Swpaul      iface->addrs = 0;
15340516Swpaul      iface->addr = NULL;
15440516Swpaul    }
15540516Swpaul    ptr += ifm->ifm_msglen;				/* First ifa_msghdr */
15640516Swpaul    for (; ptr < end; ptr += ifam->ifam_msglen) {
15740516Swpaul      ifam = (struct ifa_msghdr *)ptr;			/* Next if address */
15840516Swpaul
15940516Swpaul      if (ifam->ifam_type != RTM_NEWADDR)		/* finished this if */
16040516Swpaul        break;
16140516Swpaul
16240516Swpaul      if (iface != NULL && ifam->ifam_addrs & RTA_IFA) {
16340516Swpaul        /* Found a configured interface ! */
16440516Swpaul        iface_ParseHdr(ifam, sa);
16540516Swpaul
16640516Swpaul        if (sa[RTAX_IFA] && (sa[RTAX_IFA]->sa_family == AF_INET
16740516Swpaul#ifndef NOINET6
16840516Swpaul                             || sa[RTAX_IFA]->sa_family == AF_INET6
16940516Swpaul#endif
17040516Swpaul                             )) {
17140516Swpaul          /* Record the address */
17240516Swpaul
17340516Swpaul          addr = (struct iface_addr *)
17440516Swpaul            realloc(iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
17540516Swpaul          if (addr == NULL)
17640516Swpaul            break;
17740516Swpaul          iface->addr = addr;
17840516Swpaul
17940516Swpaul          addr += iface->addrs;
18040516Swpaul          iface->addrs++;
18140516Swpaul
18240516Swpaul          ncprange_setsa(&addr->ifa, sa[RTAX_IFA], sa[RTAX_NETMASK]);
18340516Swpaul          if (sa[RTAX_BRD])
18440516Swpaul            ncpaddr_setsa(&addr->peer, sa[RTAX_BRD]);
18540516Swpaul          else
18640516Swpaul            ncpaddr_init(&addr->peer);
18740516Swpaul        }
18840516Swpaul      }
18940516Swpaul    }
19040516Swpaul  }
19140516Swpaul
19240516Swpaul  free(buf);
19340516Swpaul
19440516Swpaul  return iface;
19540516Swpaul}
19640516Swpaul
19740516Swpaulstatic int
19840516Swpauliface_addr_Zap(const char *name, struct iface_addr *addr, int s)
19940516Swpaul{
20040516Swpaul  struct ifaliasreq ifra;
20140516Swpaul#ifndef NOINET6
20240516Swpaul  struct in6_aliasreq ifra6;
20340516Swpaul#endif
20440516Swpaul  struct sockaddr_in *me4, *msk4, *peer4;
20540516Swpaul  struct sockaddr_storage ssme, sspeer, ssmsk;
20640516Swpaul  int res;
20740516Swpaul
20840516Swpaul  ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
20940516Swpaul  ncpaddr_getsa(&addr->peer, &sspeer);
21040516Swpaul  res = 0;
21140516Swpaul
21240516Swpaul  switch (ncprange_family(&addr->ifa)) {
21340516Swpaul  case AF_INET:
21440516Swpaul    memset(&ifra, '\0', sizeof ifra);
21540516Swpaul    strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
21640516Swpaul
21740516Swpaul    me4 = (struct sockaddr_in *)&ifra.ifra_addr;
21840516Swpaul    memcpy(me4, &ssme, sizeof *me4);
21940516Swpaul
22040516Swpaul    msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
22140516Swpaul    memcpy(msk4, &ssmsk, sizeof *msk4);
22240516Swpaul
22340516Swpaul    peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
22440516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
22540516Swpaul      peer4->sin_family = AF_INET;
22640516Swpaul      peer4->sin_len = sizeof(*peer4);
22740516Swpaul      peer4->sin_addr.s_addr = INADDR_NONE;
22840516Swpaul    } else
22940516Swpaul      memcpy(peer4, &sspeer, sizeof *peer4);
23040516Swpaul
23140516Swpaul    res = ID0ioctl(s, SIOCDIFADDR, &ifra);
23240516Swpaul    break;
23340516Swpaul
23440516Swpaul#ifndef NOINET6
23540516Swpaul  case AF_INET6:
23640516Swpaul    memset(&ifra6, '\0', sizeof ifra6);
23740516Swpaul    strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
23840516Swpaul
23940516Swpaul    memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
24040516Swpaul    memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
24140516Swpaul    ifra6.ifra_prefixmask.sin6_family = AF_UNSPEC;
24240516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
24340516Swpaul      ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
24440516Swpaul    else
24540516Swpaul      memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
24640516Swpaul    ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
24740516Swpaul    ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
24840516Swpaul
24940516Swpaul    res = ID0ioctl(s, SIOCDIFADDR_IN6, &ifra6);
25040516Swpaul    break;
25140516Swpaul#endif
25240516Swpaul  }
25340516Swpaul
25440516Swpaul  if (res == -1) {
25540516Swpaul    char dst[40];
25640516Swpaul    const char *end =
25740516Swpaul#ifndef NOINET6
25840516Swpaul      ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
25940516Swpaul#endif
26040516Swpaul      "";
26140516Swpaul
26240516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
26340516Swpaul      log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s): %s\n",
26440516Swpaul                 end, ncprange_ntoa(&addr->ifa), strerror(errno));
26540516Swpaul    else {
26640516Swpaul      snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
26740516Swpaul      log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s -> %s): %s\n",
26840516Swpaul                 end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
26940516Swpaul    }
27040516Swpaul  }
27140516Swpaul
27240516Swpaul  return res != -1;
27340516Swpaul}
27440516Swpaul
27540516Swpaulstatic void
27640516Swpauliface_addr_Add(const char *name, struct iface_addr *addr, int s)
27740516Swpaul{
27840516Swpaul  struct ifaliasreq ifra;
27940516Swpaul#ifndef NOINET6
28040516Swpaul  struct in6_aliasreq ifra6;
28140516Swpaul#endif
28240516Swpaul  struct sockaddr_in *me4, *msk4, *peer4;
28340516Swpaul  struct sockaddr_storage ssme, sspeer, ssmsk;
28440516Swpaul  int res;
28540516Swpaul
28640516Swpaul  ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
28740516Swpaul  ncpaddr_getsa(&addr->peer, &sspeer);
28840516Swpaul  res = 0;
28940516Swpaul
29040516Swpaul  switch (ncprange_family(&addr->ifa)) {
29140516Swpaul  case AF_INET:
29240516Swpaul    memset(&ifra, '\0', sizeof ifra);
29340516Swpaul    strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
29440516Swpaul
29540516Swpaul    me4 = (struct sockaddr_in *)&ifra.ifra_addr;
29640516Swpaul    memcpy(me4, &ssme, sizeof *me4);
29740516Swpaul
29840516Swpaul    msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
29940516Swpaul    memcpy(msk4, &ssmsk, sizeof *msk4);
30040516Swpaul
30140516Swpaul    peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
30240516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
30340516Swpaul      peer4->sin_family = AF_INET;
30440516Swpaul      peer4->sin_len = sizeof(*peer4);
30540516Swpaul      peer4->sin_addr.s_addr = INADDR_NONE;
30640516Swpaul    } else
30740516Swpaul      memcpy(peer4, &sspeer, sizeof *peer4);
30840516Swpaul
30940516Swpaul    res = ID0ioctl(s, SIOCAIFADDR, &ifra);
31040516Swpaul    break;
31140516Swpaul
31240516Swpaul#ifndef NOINET6
31340516Swpaul  case AF_INET6:
31440516Swpaul    memset(&ifra6, '\0', sizeof ifra6);
31540516Swpaul    strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
31640516Swpaul
31740516Swpaul    memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
31840516Swpaul    memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
31940516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
32040516Swpaul      ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
32140516Swpaul    else
32240516Swpaul      memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
32340516Swpaul    ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
32440516Swpaul    ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
32540516Swpaul
32640516Swpaul    res = ID0ioctl(s, SIOCAIFADDR_IN6, &ifra6);
32740516Swpaul    break;
32840516Swpaul#endif
32940516Swpaul  }
33040516Swpaul
33140516Swpaul  if (res == -1) {
33240516Swpaul    char dst[40];
33340516Swpaul    const char *end =
33440516Swpaul#ifndef NOINET6
33540516Swpaul      ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
33640516Swpaul#endif
33740516Swpaul      "";
33840516Swpaul
33940516Swpaul    if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
34040516Swpaul      log_Printf(LogWARN, "iface add: ioctl(SIOCAIFADDR%s, %s): %s\n",
34140516Swpaul                 end, ncprange_ntoa(&addr->ifa), strerror(errno));
34240516Swpaul    else {
34340516Swpaul      snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
34440516Swpaul      log_Printf(LogWARN, "iface add: ioctl(SIOCDIFADDR%s, %s -> %s): %s\n",
34540516Swpaul                 end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
34640516Swpaul    }
34740516Swpaul  }
34840516Swpaul}
34940516Swpaul
35040516Swpaul
35140516Swpaulvoid
35240516Swpauliface_Clear(struct iface *iface, struct ncp *ncp, int family, int how)
35340516Swpaul{
35440516Swpaul  int addrs, af, inskip, in6skip, n, s4 = -1, s6 = -1, *s;
35540516Swpaul
35640516Swpaul  if (iface->addrs) {
35740516Swpaul    inskip = in6skip = how == IFACE_CLEAR_ALL ? 0 : 1;
35840516Swpaul    addrs = 0;
35940516Swpaul
36040516Swpaul    for (n = 0; n < iface->addrs; n++) {
36140516Swpaul      af = ncprange_family(&iface->addr[n].ifa);
36240516Swpaul      if (family == 0 || family == af) {
36340516Swpaul        if (!iface->addr[n].system && (how & IFACE_SYSTEM))
36440516Swpaul          continue;
36540516Swpaul        switch (af) {
36640516Swpaul        case AF_INET:
36740516Swpaul          if (inskip) {
36840516Swpaul            inskip = 0;
36940516Swpaul            continue;
37040516Swpaul          }
37140516Swpaul          s = &s4;
37240516Swpaul          break;
37340516Swpaul
37440516Swpaul#ifndef NOINET6
37540516Swpaul        case AF_INET6:
37640516Swpaul          if (in6skip) {
37740516Swpaul            in6skip = 0;
37840516Swpaul            continue;
37940516Swpaul          }
38040516Swpaul          s = &s6;
38140516Swpaul          break;
38240516Swpaul#endif
38340516Swpaul        }
38440516Swpaul
38540516Swpaul        if (*s == -1 && (*s = ID0socket(af, SOCK_DGRAM, 0)) == -1)
38640516Swpaul          log_Printf(LogERROR, "iface_Clear: socket(): %s\n", strerror(errno));
38740516Swpaul        else if (iface_addr_Zap(iface->name, iface->addr + n, *s)) {
38840516Swpaul          ncp_IfaceAddrDeleted(ncp, iface->addr + n);
38940516Swpaul          bcopy(iface->addr + n + 1, iface->addr + n,
39040516Swpaul                (iface->addrs - n - 1) * sizeof *iface->addr);
39140516Swpaul          iface->addrs--;
39240516Swpaul          n--;
39340516Swpaul        }
39440516Swpaul      }
39540516Swpaul    }
39640516Swpaul
39740516Swpaul    /* Don't bother realloc()ing - we have little to gain */
39840516Swpaul
39940516Swpaul    if (s4)
40040516Swpaul      close(s4);
40140516Swpaul    if (s6)
40240516Swpaul      close(s6);
40340516Swpaul  }
40440516Swpaul}
40540516Swpaul
40640516Swpaulint
40740516Swpauliface_Add(struct iface *iface, struct ncp *ncp, const struct ncprange *ifa,
40840516Swpaul          const struct ncpaddr *peer, int how)
40940516Swpaul{
41040516Swpaul  int af, n, s, width;
41140516Swpaul  struct ncpaddr ifaddr, ncplocal;
41240516Swpaul  struct iface_addr *addr;
41340516Swpaul
41440516Swpaul  af = ncprange_family(ifa);
41540516Swpaul  if ((s = ID0socket(af, SOCK_DGRAM, 0)) == -1) {
41640516Swpaul    log_Printf(LogERROR, "iface_Add: socket(): %s\n", strerror(errno));
41740516Swpaul    return 0;
41840516Swpaul  }
41940516Swpaul  ncprange_getaddr(ifa, &ncplocal);
42040516Swpaul
42140516Swpaul  for (n = 0; n < iface->addrs; n++) {
42240516Swpaul    if (ncprange_contains(&iface->addr[n].ifa, &ncplocal)) {
42340516Swpaul      if (!(how & IFACE_FORCE_ADD)) {
42440516Swpaul        close(s);
42540516Swpaul        return 0;	/* errno = EEXIST; */
42640516Swpaul      }
42740516Swpaul
42840516Swpaul      if (ncprange_equal(&iface->addr[n].ifa, ifa) &&
42940516Swpaul          ncpaddr_equal(&iface->addr[n].peer, peer)) {
43040516Swpaul        close(s);
43140516Swpaul        return 1;	/* Already there */
43240516Swpaul      }
43340516Swpaul
43440516Swpaul      width =
43540516Swpaul#ifndef NOINET6
43640516Swpaul        (af == AF_INET6) ? 128 :
43740516Swpaul#endif
43840516Swpaul      32;
43940516Swpaul      iface_addr_Zap(iface->name, iface->addr + n, s);
44040516Swpaul      ncprange_setwidth(&iface->addr[n].ifa, width);
44140516Swpaul      ncprange_getaddr(&iface->addr[n].ifa, &ifaddr);
44240516Swpaul      if (ncpaddr_equal(&ifaddr, &ncplocal))
44340516Swpaul        ncpaddr_copy(&iface->addr[n].peer, peer);
44440516Swpaul      else
44540516Swpaul        ncpaddr_init(&iface->addr[n].peer);
44640516Swpaul      iface_addr_Add(iface->name, iface->addr + n, s);
44740516Swpaul      if (ncpaddr_equal(&ifaddr, &ncplocal)) {
44840516Swpaul        close(s);
44940516Swpaul        ncp_IfaceAddrAdded(ncp, iface->addr + n);
45040516Swpaul        return 1;
45140516Swpaul      }
45240516Swpaul    }
45340516Swpaul  }
45440516Swpaul
45540516Swpaul  addr = (struct iface_addr *)realloc
45640516Swpaul    (iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
45740516Swpaul  if (addr == NULL) {
45840516Swpaul    log_Printf(LogERROR, "iface_inAdd: realloc: %s\n", strerror(errno));
45940516Swpaul    close(s);
46040516Swpaul    return 0;
46140516Swpaul  }
46240516Swpaul  iface->addr = addr;
46340516Swpaul
46440516Swpaul  if (how & IFACE_ADD_FIRST) {
46540516Swpaul    /* Stuff it at the start of our list */
46640516Swpaul    n = 0;
46740516Swpaul    bcopy(iface->addr, iface->addr + 1, iface->addrs * sizeof *iface->addr);
46840516Swpaul  } else
46940516Swpaul    n = iface->addrs;
47040516Swpaul
47140516Swpaul  iface->addrs++;
47240516Swpaul  ncprange_copy(&iface->addr[n].ifa, ifa);
47340516Swpaul  ncpaddr_copy(&iface->addr[n].peer, peer);
47440516Swpaul  iface->addr[n].system = !!(how & IFACE_SYSTEM);
47540516Swpaul  iface_addr_Add(iface->name, iface->addr + n, s);
47640516Swpaul
47740516Swpaul  close(s);
47840516Swpaul  ncp_IfaceAddrAdded(ncp, iface->addr + n);
47940516Swpaul
48040516Swpaul  return 1;
48140516Swpaul}
48240516Swpaul
48340516Swpaulint
48440516Swpauliface_Delete(struct iface *iface, struct ncp *ncp, const struct ncpaddr *del)
48540516Swpaul{
48640516Swpaul  struct ncpaddr found;
48740516Swpaul  int n, res, s;
48840516Swpaul
48940516Swpaul  if ((s = ID0socket(ncpaddr_family(del), SOCK_DGRAM, 0)) == -1) {
49040516Swpaul    log_Printf(LogERROR, "iface_Delete: socket(): %s\n", strerror(errno));
49140516Swpaul    return 0;
49240516Swpaul  }
49340516Swpaul
49440516Swpaul  for (n = res = 0; n < iface->addrs; n++) {
49540516Swpaul    ncprange_getaddr(&iface->addr[n].ifa, &found);
49640516Swpaul    if (ncpaddr_equal(&found, del)) {
49740516Swpaul      iface_addr_Zap(iface->name, iface->addr + n, s);
49840516Swpaul      ncp_IfaceAddrDeleted(ncp, iface->addr + n);
49940516Swpaul      bcopy(iface->addr + n + 1, iface->addr + n,
50040516Swpaul            (iface->addrs - n - 1) * sizeof *iface->addr);
50140516Swpaul      iface->addrs--;
50240516Swpaul      res = 1;
50340516Swpaul      break;
50440516Swpaul    }
50540516Swpaul  }
50640516Swpaul
50740516Swpaul  close(s);
50840516Swpaul
50940516Swpaul  return res;
51040516Swpaul}
51140516Swpaul
51240516Swpaul#define IFACE_ADDFLAGS 1
51340516Swpaul#define IFACE_DELFLAGS 2
51440516Swpaul
51540516Swpaulstatic int
51640516Swpauliface_ChangeFlags(const char *ifname, int flags, int how)
51740516Swpaul{
51840516Swpaul  struct ifreq ifrq;
51940516Swpaul  int s;
52040516Swpaul
52140516Swpaul  s = ID0socket(AF_INET, SOCK_DGRAM, 0);
52240516Swpaul  if (s < 0) {
52340516Swpaul    log_Printf(LogERROR, "iface_ChangeFlags: socket: %s\n", strerror(errno));
52440516Swpaul    return 0;
52540516Swpaul  }
52640516Swpaul
52740516Swpaul  memset(&ifrq, '\0', sizeof ifrq);
52840516Swpaul  strncpy(ifrq.ifr_name, ifname, sizeof ifrq.ifr_name - 1);
52940516Swpaul  ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
53040516Swpaul  if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
53140516Swpaul    log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCGIFFLAGS): %s\n",
53240516Swpaul       strerror(errno));
53340516Swpaul    close(s);
53440516Swpaul    return 0;
53540516Swpaul  }
53640516Swpaul
53740516Swpaul  if (how == IFACE_ADDFLAGS)
53840516Swpaul    ifrq.ifr_flags |= flags;
53940516Swpaul  else
54040516Swpaul    ifrq.ifr_flags &= ~flags;
54140516Swpaul
54240516Swpaul  if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
54340516Swpaul    log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCSIFFLAGS): %s\n",
54440516Swpaul       strerror(errno));
54540516Swpaul    close(s);
54640516Swpaul    return 0;
54740516Swpaul  }
54840516Swpaul  close(s);
54940516Swpaul
55040516Swpaul  return 1;	/* Success */
55140516Swpaul}
55240516Swpaul
55340516Swpaulint
55440516Swpauliface_SetFlags(const char *ifname, int flags)
55540516Swpaul{
55640516Swpaul  return iface_ChangeFlags(ifname, flags, IFACE_ADDFLAGS);
55740516Swpaul}
55840516Swpaul
55940516Swpaulint
56040516Swpauliface_ClearFlags(const char *ifname, int flags)
56140516Swpaul{
56240516Swpaul  return iface_ChangeFlags(ifname, flags, IFACE_DELFLAGS);
56340516Swpaul}
56440516Swpaul
56540516Swpaulvoid
56640516Swpauliface_Destroy(struct iface *iface)
56740516Swpaul{
56840516Swpaul  /*
56940516Swpaul   * iface_Clear(iface, IFACE_CLEAR_ALL) must be called manually
57040516Swpaul   * if that's what the user wants.  It's better to leave the interface
57140516Swpaul   * allocated so that existing connections can continue to work.
57240516Swpaul   */
57340516Swpaul
57440516Swpaul  if (iface != NULL) {
57540516Swpaul    free(iface->name);
57640516Swpaul    free(iface->addr);
57740516Swpaul    free(iface);
57840516Swpaul  }
57940516Swpaul}
58040516Swpaul
58141273Swpaul#define if_entry(x) { IFF_##x, #x }
58240516Swpaul
58340516Swpaulstruct {
58440516Swpaul  int flag;
58540516Swpaul  const char *value;
58640516Swpaul} if_flags[] = {
58740516Swpaul  if_entry(UP),
58840516Swpaul  if_entry(BROADCAST),
58940516Swpaul  if_entry(DEBUG),
59040516Swpaul  if_entry(LOOPBACK),
59140516Swpaul  if_entry(POINTOPOINT),
59240516Swpaul  if_entry(RUNNING),
59340516Swpaul  if_entry(NOARP),
59440516Swpaul  if_entry(PROMISC),
59540516Swpaul  if_entry(ALLMULTI),
59640516Swpaul  if_entry(OACTIVE),
59740516Swpaul  if_entry(SIMPLEX),
59840516Swpaul  if_entry(LINK0),
59940516Swpaul  if_entry(LINK1),
60040516Swpaul  if_entry(LINK2),
60140516Swpaul  if_entry(MULTICAST),
60240516Swpaul  { 0, "???" }
60340516Swpaul};
60440516Swpaul
60540516Swpaulint
60640516Swpauliface_Show(struct cmdargs const *arg)
60740516Swpaul{
60840516Swpaul  struct ncpaddr ncpaddr;
60940516Swpaul  struct iface *iface = arg->bundle->iface, *current;
61040516Swpaul  int f, flags;
61140516Swpaul#ifndef NOINET6
61240516Swpaul  int scopeid, width;
61340516Swpaul#endif
61440516Swpaul  struct in_addr mask;
61540516Swpaul
61640516Swpaul  current = iface_Create(iface->name);
61740516Swpaul  flags = iface->flags = current->flags;
61840516Swpaul  iface_Destroy(current);
61940516Swpaul
62040516Swpaul  prompt_Printf(arg->prompt, "%s (idx %d) <", iface->name, iface->index);
62140516Swpaul  for (f = 0; f < sizeof if_flags / sizeof if_flags[0]; f++)
62240516Swpaul    if ((if_flags[f].flag & flags) || (!if_flags[f].flag && flags)) {
62340516Swpaul      prompt_Printf(arg->prompt, "%s%s", flags == iface->flags ? "" : ",",
62440516Swpaul                    if_flags[f].value);
62540516Swpaul      flags &= ~if_flags[f].flag;
62640516Swpaul    }
62740516Swpaul  prompt_Printf(arg->prompt, "> mtu %d has %d address%s:\n", iface->mtu,
62840516Swpaul                iface->addrs, iface->addrs == 1 ? "" : "es");
62940516Swpaul
63040516Swpaul  for (f = 0; f < iface->addrs; f++) {
63140516Swpaul    ncprange_getaddr(&iface->addr[f].ifa, &ncpaddr);
63240516Swpaul    switch (ncprange_family(&iface->addr[f].ifa)) {
63340516Swpaul    case AF_INET:
63440516Swpaul      prompt_Printf(arg->prompt, "  inet %s --> ", ncpaddr_ntoa(&ncpaddr));
63540516Swpaul      if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
63640516Swpaul        prompt_Printf(arg->prompt, "255.255.255.255");
63740516Swpaul      else
63840516Swpaul        prompt_Printf(arg->prompt, "%s", ncpaddr_ntoa(&iface->addr[f].peer));
63940516Swpaul      ncprange_getip4mask(&iface->addr[f].ifa, &mask);
64040516Swpaul      prompt_Printf(arg->prompt, " netmask 0x%08lx", (long)ntohl(mask.s_addr));
64140516Swpaul      break;
64240516Swpaul
64340516Swpaul#ifndef NOINET6
64440516Swpaul    case AF_INET6:
64540516Swpaul      prompt_Printf(arg->prompt, "  inet6 %s", ncpaddr_ntoa(&ncpaddr));
64640516Swpaul      if (ncpaddr_family(&iface->addr[f].peer) != AF_UNSPEC)
64740516Swpaul        prompt_Printf(arg->prompt, " --> %s",
64840516Swpaul                      ncpaddr_ntoa(&iface->addr[f].peer));
64940516Swpaul      ncprange_getwidth(&iface->addr[f].ifa, &width);
65040516Swpaul      if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
65140516Swpaul        prompt_Printf(arg->prompt, " prefixlen %d", width);
65240516Swpaul      if ((scopeid = ncprange_scopeid(&iface->addr[f].ifa)) != -1)
65340516Swpaul        prompt_Printf(arg->prompt, " scopeid 0x%x", (unsigned)scopeid);
65440516Swpaul      break;
65540516Swpaul#endif
65640516Swpaul    }
65740516Swpaul    prompt_Printf(arg->prompt, "\n");
65840516Swpaul  }
65940516Swpaul
66040516Swpaul  return 0;
66140516Swpaul}
66240516Swpaul
66340516Swpaulvoid
66440516Swpauliface_ParseHdr(struct ifa_msghdr *ifam, struct sockaddr *sa[RTAX_MAX])
66540516Swpaul{
66640516Swpaul  char *wp;
66740516Swpaul  int rtax;
66840516Swpaul
66940516Swpaul  wp = (char *)(ifam + 1);
67040516Swpaul
67140516Swpaul  for (rtax = 0; rtax < RTAX_MAX; rtax++)
67240516Swpaul    if (ifam->ifam_addrs & (1 << rtax)) {
67340516Swpaul      sa[rtax] = (struct sockaddr *)wp;
67440516Swpaul      wp += ROUNDUP(sa[rtax]->sa_len);
67540516Swpaul    } else
67640516Swpaul      sa[rtax] = NULL;
67740516Swpaul}
67840516Swpaul