iface.c revision 78410
1/*-
2 * Copyright (c) 1998 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/iface.c 78410 2001-06-18 14:59:36Z brian $
27 */
28
29#include <sys/param.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <net/if.h>
33#include <net/if_dl.h>
34#include <net/route.h>
35#include <arpa/inet.h>
36#include <netinet/in_systm.h>
37#include <netinet/ip.h>
38#include <sys/un.h>
39
40#include <errno.h>
41#include <string.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <sys/ioctl.h>
45#include <sys/sysctl.h>
46#include <termios.h>
47#include <unistd.h>
48
49#include "layer.h"
50#include "defs.h"
51#include "command.h"
52#include "mbuf.h"
53#include "log.h"
54#include "id.h"
55#include "timer.h"
56#include "fsm.h"
57#include "iplist.h"
58#include "lqr.h"
59#include "hdlc.h"
60#include "throughput.h"
61#include "slcompress.h"
62#include "descriptor.h"
63#include "ipcp.h"
64#include "filter.h"
65#include "lcp.h"
66#include "ccp.h"
67#include "link.h"
68#include "mp.h"
69#ifndef NORADIUS
70#include "radius.h"
71#endif
72#include "bundle.h"
73#include "prompt.h"
74#include "iface.h"
75
76
77static int
78bitsinmask(struct in_addr mask)
79{
80  u_int32_t bitmask, maskaddr;
81  int bits;
82
83  bitmask = 0xffffffff;
84  maskaddr = ntohl(mask.s_addr);
85  for (bits = 32; bits >= 0; bits--) {
86    if (maskaddr == bitmask)
87      break;
88    bitmask &= ~(1 << (32 - bits));
89  }
90
91  return bits;
92}
93
94struct iface *
95iface_Create(const char *name)
96{
97  int mib[6], s, maxtries, err;
98  size_t needed, namelen;
99  char *buf, *ptr, *end;
100  struct if_msghdr *ifm;
101  struct ifa_msghdr *ifam;
102  struct sockaddr_dl *dl;
103  struct sockaddr *sa[RTAX_MAX];
104  struct iface *iface;
105  struct iface_addr *addr;
106
107  s = socket(AF_INET, SOCK_DGRAM, 0);
108  if (s < 0) {
109    fprintf(stderr, "iface_Create: socket(): %s\n", strerror(errno));
110    return NULL;
111  }
112
113  mib[0] = CTL_NET;
114  mib[1] = PF_ROUTE;
115  mib[2] = 0;
116  mib[3] = 0;
117  mib[4] = NET_RT_IFLIST;
118  mib[5] = 0;
119
120  maxtries = 20;
121  err = 0;
122  do {
123    if (maxtries-- == 0 || (err && err != ENOMEM)) {
124      fprintf(stderr, "iface_Create: sysctl: %s\n", strerror(err));
125      close(s);
126      return NULL;
127    }
128
129    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
130      fprintf(stderr, "iface_Create: sysctl: estimate: %s\n",
131                strerror(errno));
132      close(s);
133      return NULL;
134    }
135
136    if ((buf = (char *)malloc(needed)) == NULL) {
137      fprintf(stderr, "iface_Create: malloc failed: %s\n", strerror(errno));
138      close(s);
139      return NULL;
140    }
141
142    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
143      err = errno;
144      free(buf);
145      buf = NULL;
146    }
147  } while (buf == NULL);
148
149  ptr = buf;
150  end = buf + needed;
151  iface = NULL;
152  namelen = strlen(name);
153
154  while (ptr < end && iface == NULL) {
155    ifm = (struct if_msghdr *)ptr;			/* On if_msghdr */
156    if (ifm->ifm_type != RTM_IFINFO)
157      break;
158    dl = (struct sockaddr_dl *)(ifm + 1);		/* Single _dl at end */
159    if (dl->sdl_nlen == namelen && !strncmp(name, dl->sdl_data, namelen)) {
160      iface = (struct iface *)malloc(sizeof *iface);
161      if (iface == NULL) {
162        fprintf(stderr, "iface_Create: malloc: %s\n", strerror(errno));
163        return NULL;
164      }
165      iface->name = strdup(name);
166      iface->index = ifm->ifm_index;
167      iface->flags = ifm->ifm_flags;
168      iface->mtu = 0;
169      iface->in_addrs = 0;
170      iface->in_addr = NULL;
171    }
172    ptr += ifm->ifm_msglen;				/* First ifa_msghdr */
173    for (; ptr < end; ptr += ifam->ifam_msglen) {
174      ifam = (struct ifa_msghdr *)ptr;			/* Next if address */
175
176      if (ifam->ifam_type != RTM_NEWADDR)		/* finished this if */
177        break;
178
179      if (iface != NULL && ifam->ifam_addrs & RTA_IFA) {
180        /* Found a configured interface ! */
181        iface_ParseHdr(ifam, sa);
182
183        if (sa[RTAX_IFA] && sa[RTAX_IFA]->sa_family == AF_INET) {
184          /* Record the address */
185
186          addr = (struct iface_addr *)realloc
187            (iface->in_addr, (iface->in_addrs + 1) * sizeof iface->in_addr[0]);
188          if (addr == NULL)
189            break;
190          iface->in_addr = addr;
191
192          addr += iface->in_addrs;
193          iface->in_addrs++;
194
195          addr->ifa = ((struct sockaddr_in *)sa[RTAX_IFA])->sin_addr;
196
197          if (sa[RTAX_BRD])
198            addr->brd = ((struct sockaddr_in *)sa[RTAX_BRD])->sin_addr;
199          else
200            addr->brd.s_addr = INADDR_ANY;
201
202          if (sa[RTAX_NETMASK])
203            addr->mask = ((struct sockaddr_in *)sa[RTAX_NETMASK])->sin_addr;
204          else
205            addr->mask.s_addr = INADDR_ANY;
206
207          addr->bits = bitsinmask(addr->mask);
208        }
209      }
210    }
211  }
212
213  free(buf);
214  close(s);
215
216  return iface;
217}
218
219static void
220iface_addr_Zap(const char *name, struct iface_addr *addr)
221{
222  struct ifaliasreq ifra;
223  struct sockaddr_in *me, *peer;
224  int s;
225
226  s = ID0socket(AF_INET, SOCK_DGRAM, 0);
227  if (s < 0)
228    log_Printf(LogERROR, "iface_addr_Zap: socket(): %s\n", strerror(errno));
229  else {
230    memset(&ifra, '\0', sizeof ifra);
231    strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
232    me = (struct sockaddr_in *)&ifra.ifra_addr;
233    peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
234    me->sin_family = peer->sin_family = AF_INET;
235    me->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
236    me->sin_addr = addr->ifa;
237    peer->sin_addr = addr->brd;
238    log_Printf(LogDEBUG, "Delete %s\n", inet_ntoa(addr->ifa));
239    if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0)
240      log_Printf(LogWARN, "iface_addr_Zap: ioctl(SIOCDIFADDR, %s): %s\n",
241                 inet_ntoa(addr->ifa), strerror(errno));
242    close(s);
243  }
244}
245
246void
247iface_inClear(struct iface *iface, int how)
248{
249  int n, addrs;
250
251  if (iface->in_addrs) {
252    addrs = n = how == IFACE_CLEAR_ALL ? 0 : 1;
253    for (; n < iface->in_addrs; n++)
254      iface_addr_Zap(iface->name, iface->in_addr + n);
255
256    iface->in_addrs = addrs;
257    /* Don't bother realloc()ing - we have little to gain */
258  }
259}
260
261int
262iface_inAdd(struct iface *iface, struct in_addr ifa, struct in_addr mask,
263            struct in_addr brd, int how)
264{
265  int slot, s, chg, nochange;
266  struct ifaliasreq ifra;
267  struct sockaddr_in *me, *peer, *msk;
268  struct iface_addr *addr;
269
270  for (slot = 0; slot < iface->in_addrs; slot++)
271    if (iface->in_addr[slot].ifa.s_addr == ifa.s_addr) {
272      if (how & IFACE_FORCE_ADD)
273        break;
274      else
275        /* errno = EEXIST; */
276        return 0;
277    }
278
279  addr = (struct iface_addr *)realloc
280    (iface->in_addr, (iface->in_addrs + 1) * sizeof iface->in_addr[0]);
281  if (addr == NULL) {
282    log_Printf(LogERROR, "iface_inAdd: realloc: %s\n", strerror(errno));
283    return 0;
284  }
285  iface->in_addr = addr;
286
287  /*
288   * We've gotta be careful here.  If we try to add an address with the
289   * same destination as an existing interface, nothing will work.
290   * Instead, we tweak all previous address entries that match the
291   * to-be-added destination to 255.255.255.255 (w/ a similar netmask).
292   * There *may* be more than one - if the user has ``iface add''ed
293   * stuff previously.
294   */
295  nochange = 0;
296  s = -1;
297  for (chg = 0; chg < iface->in_addrs; chg++) {
298    if ((iface->in_addr[chg].brd.s_addr == brd.s_addr &&
299         brd.s_addr != INADDR_BROADCAST) || chg == slot) {
300      /*
301       * If we've found an entry that exactly matches what we want to add,
302       * don't remove it and then add it again.  If we do, it's possible
303       * that the kernel will (correctly) ``tidy up'' any routes that use
304       * the IP number as a destination.
305       */
306      if (chg == slot && iface->in_addr[chg].mask.s_addr == mask.s_addr) {
307        if (brd.s_addr == iface->in_addr[slot].brd.s_addr)
308          nochange = 1;
309        /*
310         * If only the destination address has changed, the SIOCAIFADDR
311         * we do after the current loop will change it.
312         */
313        continue;
314      }
315      if (s == -1 && (s = ID0socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
316        log_Printf(LogERROR, "iface_inAdd: socket(): %s\n", strerror(errno));
317        return 0;
318      }
319
320      memset(&ifra, '\0', sizeof ifra);
321      strncpy(ifra.ifra_name, iface->name, sizeof ifra.ifra_name - 1);
322      me = (struct sockaddr_in *)&ifra.ifra_addr;
323      msk = (struct sockaddr_in *)&ifra.ifra_mask;
324      peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
325      me->sin_family = msk->sin_family = peer->sin_family = AF_INET;
326      me->sin_len = msk->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
327      me->sin_addr = iface->in_addr[chg].ifa;
328      msk->sin_addr = iface->in_addr[chg].mask;
329      peer->sin_addr = iface->in_addr[chg].brd;
330      log_Printf(LogDEBUG, "Delete %s\n", inet_ntoa(me->sin_addr));
331      ID0ioctl(s, SIOCDIFADDR, &ifra);	/* Don't care if it fails... */
332      if (chg != slot) {
333        peer->sin_addr.s_addr = iface->in_addr[chg].brd.s_addr =
334          msk->sin_addr.s_addr = iface->in_addr[chg].mask.s_addr =
335            INADDR_BROADCAST;
336        iface->in_addr[chg].bits = 32;
337        log_Printf(LogDEBUG, "Add %s -> 255.255.255.255\n",
338                   inet_ntoa(me->sin_addr));
339        if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0 && errno != EEXIST) {
340          /* Oops - that's bad(ish) news !  We've lost an alias ! */
341          log_Printf(LogERROR, "iface_inAdd: ioctl(SIOCAIFADDR): %s: %s\n",
342               inet_ntoa(me->sin_addr), strerror(errno));
343          iface->in_addrs--;
344          bcopy(iface->in_addr + chg + 1, iface->in_addr + chg,
345                (iface->in_addrs - chg) * sizeof iface->in_addr[0]);
346          if (slot > chg)
347            slot--;
348          chg--;
349        }
350      }
351    }
352  }
353
354  if (!nochange) {
355    if (s == -1 && (s = ID0socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
356      log_Printf(LogERROR, "iface_inAdd: socket(): %s\n", strerror(errno));
357      return 0;
358    }
359    memset(&ifra, '\0', sizeof ifra);
360    strncpy(ifra.ifra_name, iface->name, sizeof ifra.ifra_name - 1);
361    me = (struct sockaddr_in *)&ifra.ifra_addr;
362    msk = (struct sockaddr_in *)&ifra.ifra_mask;
363    peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
364    me->sin_family = msk->sin_family = peer->sin_family = AF_INET;
365    me->sin_len = msk->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
366    me->sin_addr = ifa;
367    msk->sin_addr = mask;
368    peer->sin_addr = brd;
369
370    if (log_IsKept(LogDEBUG)) {
371      char buf[16];
372
373      strncpy(buf, inet_ntoa(brd), sizeof buf-1);
374      buf[sizeof buf - 1] = '\0';
375      log_Printf(LogDEBUG, "Add %s -> %s\n", inet_ntoa(ifa), buf);
376    }
377
378    /* An EEXIST failure w/ brd == INADDR_BROADCAST is ok (and works!) */
379    if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0 &&
380        (brd.s_addr != INADDR_BROADCAST || errno != EEXIST)) {
381      log_Printf(LogERROR, "iface_inAdd: ioctl(SIOCAIFADDR): %s: %s\n",
382                 inet_ntoa(ifa), strerror(errno));
383      ID0ioctl(s, SIOCDIFADDR, &ifra);	/* EEXIST ? */
384      close(s);
385      return 0;
386    }
387  }
388
389  if (s != -1)
390    close(s);
391
392  if (slot == iface->in_addrs) {
393    /* We're adding a new interface address */
394
395    if (how & IFACE_ADD_FIRST) {
396      /* Stuff it at the start of our list */
397      slot = 0;
398      bcopy(iface->in_addr, iface->in_addr + 1,
399            iface->in_addrs * sizeof iface->in_addr[0]);
400    }
401
402    iface->in_addrs++;
403  } else if (how & IFACE_ADD_FIRST) {
404    /* Shift it up to the first slot */
405    bcopy(iface->in_addr, iface->in_addr + 1, slot * sizeof iface->in_addr[0]);
406    slot = 0;
407  }
408
409  iface->in_addr[slot].ifa = ifa;
410  iface->in_addr[slot].mask = mask;
411  iface->in_addr[slot].brd = brd;
412  iface->in_addr[slot].bits = bitsinmask(iface->in_addr[slot].mask);
413
414  return 1;
415}
416
417int
418iface_inDelete(struct iface *iface, struct in_addr ip)
419{
420  int n;
421
422  for (n = 0; n < iface->in_addrs; n++)
423    if (iface->in_addr[n].ifa.s_addr == ip.s_addr) {
424      iface_addr_Zap(iface->name, iface->in_addr + n);
425      bcopy(iface->in_addr + n + 1, iface->in_addr + n,
426            (iface->in_addrs - n - 1) * sizeof iface->in_addr[0]);
427      iface->in_addrs--;
428      return 1;
429    }
430
431  return 0;
432}
433
434#define IFACE_ADDFLAGS 1
435#define IFACE_DELFLAGS 2
436
437static int
438iface_ChangeFlags(const char *ifname, int flags, int how)
439{
440  struct ifreq ifrq;
441  int s;
442
443  s = ID0socket(AF_INET, SOCK_DGRAM, 0);
444  if (s < 0) {
445    log_Printf(LogERROR, "iface_ChangeFlags: socket: %s\n", strerror(errno));
446    return 0;
447  }
448
449  memset(&ifrq, '\0', sizeof ifrq);
450  strncpy(ifrq.ifr_name, ifname, sizeof ifrq.ifr_name - 1);
451  ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
452  if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
453    log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCGIFFLAGS): %s\n",
454       strerror(errno));
455    close(s);
456    return 0;
457  }
458
459  if (how == IFACE_ADDFLAGS)
460    ifrq.ifr_flags |= flags;
461  else
462    ifrq.ifr_flags &= ~flags;
463
464  if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
465    log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCSIFFLAGS): %s\n",
466       strerror(errno));
467    close(s);
468    return 0;
469  }
470  close(s);
471
472  return 1;	/* Success */
473}
474
475int
476iface_SetFlags(const char *ifname, int flags)
477{
478  return iface_ChangeFlags(ifname, flags, IFACE_ADDFLAGS);
479}
480
481int
482iface_ClearFlags(const char *ifname, int flags)
483{
484  return iface_ChangeFlags(ifname, flags, IFACE_DELFLAGS);
485}
486
487void
488iface_Destroy(struct iface *iface)
489{
490  /*
491   * iface_Clear(iface, IFACE_CLEAR_ALL) must be called manually
492   * if that's what the user wants.  It's better to leave the interface
493   * allocated so that existing connections can continue to work.
494   */
495
496  if (iface != NULL) {
497    free(iface->name);
498    free(iface->in_addr);
499    free(iface);
500  }
501}
502
503#define if_entry(x) { IFF_##x, #x }
504
505struct {
506  int flag;
507  const char *value;
508} if_flags[] = {
509  if_entry(UP),
510  if_entry(BROADCAST),
511  if_entry(DEBUG),
512  if_entry(LOOPBACK),
513  if_entry(POINTOPOINT),
514  if_entry(RUNNING),
515  if_entry(NOARP),
516  if_entry(PROMISC),
517  if_entry(ALLMULTI),
518  if_entry(OACTIVE),
519  if_entry(SIMPLEX),
520  if_entry(LINK0),
521  if_entry(LINK1),
522  if_entry(LINK2),
523  if_entry(MULTICAST),
524  { 0, "???" }
525};
526
527int
528iface_Show(struct cmdargs const *arg)
529{
530  struct iface *iface = arg->bundle->iface, *current;
531  int f, flags;
532
533  current = iface_Create(iface->name);
534  flags = iface->flags = current->flags;
535  iface_Destroy(current);
536
537  prompt_Printf(arg->prompt, "%s (idx %d) <", iface->name, iface->index);
538  for (f = 0; f < sizeof if_flags / sizeof if_flags[0]; f++)
539    if ((if_flags[f].flag & flags) || (!if_flags[f].flag && flags)) {
540      prompt_Printf(arg->prompt, "%s%s", flags == iface->flags ? "" : ",",
541                    if_flags[f].value);
542      flags &= ~if_flags[f].flag;
543    }
544  prompt_Printf(arg->prompt, "> mtu %d has %d address%s:\n", iface->mtu,
545                iface->in_addrs, iface->in_addrs == 1 ? "" : "es");
546
547  for (f = 0; f < iface->in_addrs; f++) {
548    prompt_Printf(arg->prompt, "  %s", inet_ntoa(iface->in_addr[f].ifa));
549    if (iface->in_addr[f].bits >= 0)
550      prompt_Printf(arg->prompt, "/%d", iface->in_addr[f].bits);
551    if (iface->flags & IFF_POINTOPOINT)
552      prompt_Printf(arg->prompt, " -> %s", inet_ntoa(iface->in_addr[f].brd));
553    else if (iface->flags & IFF_BROADCAST)
554      prompt_Printf(arg->prompt, " broadcast %s",
555                    inet_ntoa(iface->in_addr[f].brd));
556    if (iface->in_addr[f].bits < 0)
557      prompt_Printf(arg->prompt, " (mask %s)",
558                    inet_ntoa(iface->in_addr[f].mask));
559    prompt_Printf(arg->prompt, "\n");
560  }
561
562  return 0;
563}
564
565void
566iface_ParseHdr(struct ifa_msghdr *ifam, struct sockaddr *sa[RTAX_MAX])
567{
568  char *wp;
569  int rtax;
570
571  wp = (char *)(ifam + 1);
572
573  for (rtax = 0; rtax < RTAX_MAX; rtax++)
574    if (ifam->ifam_addrs & (1 << rtax)) {
575      sa[rtax] = (struct sockaddr *)wp;
576      wp += ROUNDUP(sa[rtax]->sa_len);
577    } else
578      sa[rtax] = NULL;
579}
580