]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/sys-linux.c
add stuff for packet filtering
[ppp.git] / pppd / sys-linux.c
index 1e52e3bddf8ab4bd53fb26855c42c0f3c8d20c62..448d426dfdf04249fa086dec1b6496702bb60dfe 100644 (file)
 #endif
 #endif /* IPX_CHANGE */
 
+#ifdef PPP_FILTER
+#include <net/bpf.h>
+#include <linux/filter.h>
+#endif /* PPP_FILTER */
+
 #ifdef LOCKLIB
 #include <sys/locks.h>
 #endif
@@ -149,6 +154,7 @@ static char proxy_arp_dev[16];              /* Device for proxy arp entry */
 static u_int32_t our_old_addr;         /* for detecting address changes */
 static int     dynaddr_set;            /* 1 if ip_dynaddr set */
 static int     looped;                 /* 1 if using loop */
+static int     link_mtu;               /* mtu for the link (not bundle) */
 
 static struct utsname utsname; /* for the kernel version */
 static int kernel_version;
@@ -176,6 +182,7 @@ static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
 static void decode_version (char *buf, int *version, int *mod, int *patch);
 static int set_kdebugflag(int level);
 static int ppp_registered(void);
+static int make_ppp_unit(void);
 
 extern u_char  inpacket_buf[]; /* borrowed from main.c */
 
@@ -280,7 +287,7 @@ void sys_init(void)
 #ifdef INET6
     sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock6_fd < 0)
-       fatal("Couldn't create IPv6 socket: %m(%d)", errno);
+       sock6_fd = -errno;      /* save errno for later */
 #endif
 
     FD_ZERO(&in_fds);
@@ -413,22 +420,14 @@ int establish_ppp (int tty_fd)
            warn("Couldn't set /dev/ppp (channel) to nonblock: %m");
        set_ppp_fd(fd);
 
-       ifunit = -1;
+       if (!looped)
+           ifunit = -1;
        if (!looped && !multilink) {
            /*
             * Create a new PPP unit.
             */
-           ifunit = req_unit;
-           x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-           if (x < 0 && req_unit >= 0 && errno == EEXIST) {
-               warn("Couldn't allocate PPP unit %d as it is already in use");
-               ifunit = -1;
-               x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-           }
-           if (x < 0) {
-               error("Couldn't create new ppp unit: %m");
+           if (make_ppp_unit() < 0)
                goto err_close;
-           }
        }
 
        if (looped)
@@ -448,8 +447,9 @@ int establish_ppp (int tty_fd)
         */
        set_ppp_fd (tty_fd);
        if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {       
-           if ( ! ok_error (errno))
-               fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+           if (ok_error (errno))
+               goto err;
+           fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
        }
        /* Check that we got the same unit again. */
        if (looped && x != ifunit)
@@ -529,7 +529,7 @@ void disestablish_ppp(int tty_fd)
     if (new_style_driver) {
        close(ppp_fd);
        ppp_fd = -1;
-       if (!looped && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+       if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
            error("Couldn't release PPP unit: %m");
        if (!multilink)
            remove_fd(ppp_dev_fd);
@@ -537,30 +537,101 @@ void disestablish_ppp(int tty_fd)
 }
 
 /*
- * bundle_attach - attach our link to a given PPP unit.
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
  */
-int bundle_attach(int ifnum)
+static int make_ppp_unit()
 {
-    int mrru = 1500;
+       int x;
 
-    if (!new_style_driver)
-       return -1;
+       ifunit = req_unit;
+       x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+               warn("Couldn't allocate PPP unit %d as it is already in use");
+               ifunit = -1;
+               x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       }
+       if (x < 0)
+               error("Couldn't create new ppp unit: %m");
+       return x;
+}
 
-    if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
-       error("Couldn't attach to interface unit %d: %m\n", ifnum);
-       return -1;
-    }
-    set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
-    if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
-       error("Couldn't set interface MRRU: %m");
+/*
+ * cfg_bundle - configure the existing bundle.
+ * Used in demand mode.
+ */
+void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+       int flags;
+       struct ifreq ifr;
 
-    if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0) {
-       error("Couldn't connect to interface unit %d: %m", ifnum);
-       return -1;
-    }
+       if (!new_style_driver)
+               return;
+
+       /* set the mrru, mtu and flags */
+       if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+               error("Couldn't set MRRU: %m");
+       flags = get_flags(ppp_dev_fd);
+       flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
+       flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0);
+
+       if (mtru > 0 && mtru != link_mtu) {
+               memset(&ifr, 0, sizeof(ifr));
+               slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "ppp%d", ifunit);
+               ifr.ifr_mtu = mtru;
+               if (ioctl(sock_fd, SIOCSIFMTU, &ifr) < 0)
+                       error("Couldn't set interface MTU: %m");
+               flags |= SC_MULTILINK;
+       }
 
-    dbglog("bundle_attach succeeded");
-    return 0;
+       set_flags(ppp_dev_fd, flags);
+
+       /* connect up the channel */
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+               fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+       add_fd(ppp_dev_fd);
+}
+
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it.  This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ * In demand mode this uses our existing bundle instead of making
+ * a new one.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+       if (!new_style_driver)
+               return;
+
+       /* make us a ppp unit */
+       if (make_ppp_unit() < 0)
+               die(1);
+
+       /* set the mrru, mtu and flags */
+       cfg_bundle(mrru, mtru, rssn, tssn);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ * We assume the unit is controlled by another pppd.
+ */
+int bundle_attach(int ifnum)
+{
+       if (!new_style_driver)
+               return -1;
+
+       if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
+               if (errno == ENXIO)
+                       return 0;       /* doesn't still exist */
+               fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+       }
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+               fatal("Couldn't connect to interface unit %d: %m", ifnum);
+       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+
+       ifunit = ifnum;
+       return 1;
 }
 
 /********************************************************************
@@ -855,7 +926,7 @@ void output (int unit, unsigned char *p, int len)
        p += 2;
        len -= 2;
        proto = (p[0] << 8) + p[1];
-       if (!multilink && proto != PPP_LCP)
+       if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
            fd = ppp_dev_fd;
     }
     if (write(fd, p, len) < 0) {
@@ -923,13 +994,13 @@ int read_packet (unsigned char *buf)
     nr = -1;
     if (ppp_fd >= 0) {
        nr = read(ppp_fd, buf, len);
-       if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
            error("read: %m");
     }
-    if (nr < 0 && new_style_driver && !multilink) {
+    if (nr < 0 && new_style_driver && ifunit >= 0) {
        /* N.B. we read ppp_fd first since LCP packets come in there. */
        nr = read(ppp_dev_fd, buf, len);
-       if (nr < 0 && errno != EWOULDBLOCK && errno != EIO)
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
            error("read /dev/ppp: %m");
     }
     return (new_style_driver && nr > 0)? nr+2: nr;
@@ -988,7 +1059,8 @@ void ppp_send_config (int unit,int mtu,u_int32_t asyncmap,int pcomp,int accomp)
        
     if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
        fatal("ioctl(SIOCSIFMTU): %m(%d)", errno);
-       
+    link_mtu = mtu;
+
     if (!still_ppp())
        return;
     SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
@@ -1092,6 +1164,33 @@ void ccp_flags_set (int unit, int isopen, int isup)
     }
 }
 
+#ifdef PPP_FILTER
+/*
+ * set_filters - set the active and pass filters in the kernel driver.
+ */
+int set_filters(struct bpf_program *pass, struct bpf_program *active)
+{
+       struct sock_fprog fp;
+
+       fp.len = pass->bf_len;
+       fp.filter = (struct sock_filter *) pass->bf_insns;
+       if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
+               if (errno == ENOTTY)
+                       warn("kernel does not support PPP filtering");
+               else
+                       error("Couldn't set pass-filter in kernel: %m");
+               return 0;
+       }
+       fp.len = active->bf_len;
+       fp.filter = (struct sock_filter *) active->bf_insns;
+       if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) {
+               error("Couldn't set active-filter in kernel: %m");
+               return 0;
+       }
+       return 1;
+}
+#endif /* PPP_FILTER */
+
 /********************************************************************
  *
  * get_idle_time - return how long the link has been idle.
@@ -1600,6 +1699,38 @@ static int get_ether_addr (u_int32_t ipaddr,
     return 1;
 }
 
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+       struct ifreq ifreq;
+       int ret, sock_fd;
+
+       sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock_fd < 0)
+               return 0;
+       memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+       strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+       ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+       close(sock_fd);
+       if (ret >= 0)
+               memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+       return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+       return "eth0";
+}
+
 /********************************************************************
  *
  * Return user specified netmask, modified by any mask we might determine
@@ -1763,35 +1894,34 @@ int ppp_available(void)
     sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
     kernel_version = KVERSION(osmaj, osmin, ospatch);
 
-    if (kernel_version >= KVERSION(2,3,13)) {
-       fd = open("/dev/ppp", O_RDWR);
+    fd = open("/dev/ppp", O_RDWR);
 #if 0
-       if (fd < 0 && errno == ENOENT) {
-           /* try making it and see if that helps. */
-           if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
-                     makedev(108, 0)) >= 0) {
-               fd = open("/dev/ppp", O_RDWR);
-               if (fd >= 0)
-                   info("Created /dev/ppp device node");
-               else
-                   unlink("/dev/ppp"); /* didn't work, undo the mknod */
-           } else if (errno == EEXIST) {
-               fd = open("/dev/ppp", O_RDWR);
-           }
+    if (fd < 0 && errno == ENOENT) {
+       /* try making it and see if that helps. */
+       if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
+                 makedev(108, 0)) >= 0) {
+           fd = open("/dev/ppp", O_RDWR);
+           if (fd >= 0)
+               info("Created /dev/ppp device node");
+           else
+               unlink("/dev/ppp");     /* didn't work, undo the mknod */
+       } else if (errno == EEXIST) {
+           fd = open("/dev/ppp", O_RDWR);
        }
+    }
 #endif /* 0 */
-       if (fd >= 0) {
-           new_style_driver = 1;
-
-           /* XXX should get from driver */
-           driver_version = 2;
-           driver_modification = 4;
-           driver_patch = 0;
-           close(fd);
-           return 1;
-       }
-       return 0;
+    if (fd >= 0) {
+       new_style_driver = 1;
+
+       /* XXX should get from driver */
+       driver_version = 2;
+       driver_modification = 4;
+       driver_patch = 0;
+       close(fd);
+       return 1;
     }
+    if (kernel_version >= KVERSION(2,3,13))
+       return 0;
 
 /*
  * Open a socket for doing the ioctl operations.
@@ -2202,6 +2332,11 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_rtmsg rt6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
@@ -2246,6 +2381,11 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_ifreq ifr6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof(ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
@@ -2355,20 +2495,13 @@ get_pty(master_fdp, slave_fdp, slave_name, uid)
 int
 open_ppp_loopback(void)
 {
-    int flags, x;
+    int flags;
 
     looped = 1;
     if (new_style_driver) {
        /* allocate ourselves a ppp unit */
-       ifunit = req_unit;
-       x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-       if (x < 0 && req_unit >= 0 && errno == EEXIST) {
-           warn("Couldn't allocate PPP unit %d as it is already in use");
-           ifunit = -1;
-           x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
-       }
-       if (x < 0)
-           fatal("Couldn't create PPP unit: %m");
+       if (make_ppp_unit() < 0)
+           die(1);
        set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
        set_kdebugflag(kdebugflag);
        ppp_fd = -1;
@@ -2595,8 +2728,8 @@ sys_check_options(void)
        return 0;
     }
     if (multilink && !new_style_driver) {
-       option_error("multilink is not supported by the kernel driver");
-       return 0;
+       warn("Warning: multilink is not supported by the kernel driver");
+       multilink = 0;
     }
     return 1;
 }