]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/sys-linux.c
pppd: Fix usage of BOTHER ioctl API on Linux (#314)
[ppp.git] / pppd / sys-linux.c
index 8d6eee93550622d5cbe0b9803fe714c11d65b713..513fc3dc009ef3010f434328121e8dde4b918137 100644 (file)
@@ -96,7 +96,6 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <ctype.h>
-#include <termios.h>
 #include <unistd.h>
 
 /* This is in netdevice.h. However, this compile will fail miserably if
 
 #if !defined(__GLIBC__) || __GLIBC__ >= 2
 #include <asm/types.h>         /* glibc 2 conflicts with linux/types.h */
-#include <asm/ioctls.h>
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <net/route.h>
 #include <sys/locks.h>
 #endif
 
-#ifndef BOTHER
-#define BOTHER 0010000
-#endif
-struct termios2 {
-       unsigned int c_iflag;
-       unsigned int c_oflag;
-       unsigned int c_cflag;
-       unsigned int c_lflag;
-       unsigned char c_line;
-       unsigned char c_cc[19];
-       unsigned int c_ispeed;
-       unsigned int c_ospeed;
-};
+/*
+ * Instead of system header file <termios.h> use local "termios_linux.h" header
+ * file as it provides additional support for arbitrary baud rates via BOTHER.
+ */
+#include "termios_linux.h"
 
 #ifdef INET6
 #ifndef _LINUX_IN6_H
@@ -697,11 +687,11 @@ static int make_ppp_unit(void)
 
        if (x == 0 && req_ifname[0] != '\0') {
                struct ifreq ifr;
-               char t[MAXIFNAMELEN];
+               char t[IFNAMSIZ];
                memset(&ifr, 0, sizeof(struct ifreq));
                slprintf(t, sizeof(t), "%s%d", PPP_DRV_NAME, ifunit);
-               strlcpy(ifr.ifr_name, t, IF_NAMESIZE);
-               strlcpy(ifr.ifr_newname, req_ifname, IF_NAMESIZE);
+               strlcpy(ifr.ifr_name, t, IFNAMSIZ);
+               strlcpy(ifr.ifr_newname, req_ifname, IFNAMSIZ);
                x = ioctl(sock_fd, SIOCSIFNAME, &ifr);
                if (x < 0)
                    error("Couldn't rename interface %s to %s: %m", t, req_ifname);
@@ -1053,40 +1043,32 @@ void set_up_tty(int tty_fd, int local)
            cfsetospeed (&tios, speed);
            cfsetispeed (&tios, speed);
            speed = cfgetospeed(&tios);
+           baud_rate = baud_rate_of(speed);
+       } else {
+#ifdef BOTHER
+           tios.c_cflag &= ~CBAUD;
+           tios.c_cflag |= BOTHER;
+           tios.c_ospeed = inspeed;
+#ifdef IBSHIFT
+           /* B0 sets input baudrate to the output baudrate */
+           tios.c_cflag &= ~(CBAUD << IBSHIFT);
+           tios.c_cflag |= B0 << IBSHIFT;
+           tios.c_ispeed = inspeed;
+#endif
+           baud_rate = inspeed;
+#else
+           baud_rate = 0;
+#endif
        }
-       baud_rate = baud_rate_of(speed);
     }
     else {
        speed = cfgetospeed(&tios);
        baud_rate = baud_rate_of(speed);
-    }
-
-    while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
-       if (errno != EINTR)
-           fatal("tcsetattr: %m (line %d)", __LINE__);
-    restore_term = 1;
-
-/* Most Linux architectures and drivers support arbitrary baud rate values via BOTHER */
-#ifdef TCGETS2
-    if (!baud_rate) {
-       struct termios2 tios2;
-       if (ioctl(tty_fd, TCGETS2, &tios2) == 0) {
-           if (inspeed) {
-               tios2.c_cflag &= ~CBAUD;
-               tios2.c_cflag |= BOTHER;
-               tios2.c_ispeed = inspeed;
-               tios2.c_ospeed = inspeed;
-#ifdef TCSETS2
-               if (ioctl(tty_fd, TCSETS2, &tios2) == 0)
-                   baud_rate = inspeed;
+#ifdef BOTHER
+       if (!baud_rate)
+           baud_rate = tios.c_ospeed;
 #endif
-           } else {
-               if ((tios2.c_cflag & CBAUD) == BOTHER && tios2.c_ospeed)
-                   baud_rate = tios2.c_ospeed;
-           }
-       }
     }
-#endif
 
 /*
  * We can't proceed if the serial port baud rate is unknown,
@@ -1098,6 +1080,11 @@ void set_up_tty(int tty_fd, int local)
        else
            fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
     }
+
+    while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
+       if (errno != EINTR)
+           fatal("tcsetattr: %m (line %d)", __LINE__);
+    restore_term = 1;
 }
 
 /********************************************************************
@@ -3013,7 +3000,14 @@ static int sif6addr_rtnetlink(unsigned int iface, eui64_t our_eui64, eui64_t his
 
     /* error == 0 indicates success, negative value is errno code */
     if (nlresp.nlerr.error != 0) {
-        error("sif6addr_rtnetlink: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__);
+        /*
+         * Linux kernel versions prior 3.11 do not support setting IPv6 peer
+         * addresses and error response is expected. On older kernel versions
+         * do not show this error message. On error pppd tries to fallback to
+         * the old IOCTL method.
+         */
+        if (kernel_version >= KVERSION(3,11,0))
+            error("sif6addr_rtnetlink: %s (line %d)", strerror(-nlresp.nlerr.error), __LINE__);
         return 0;
     }
 
@@ -3029,6 +3023,7 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct in6_ifreq ifr6;
     struct ifreq ifr;
     struct in6_rtmsg rt6;
+    int ret;
 
     if (sock6_fd < 0) {
        errno = -sock6_fd;
@@ -3044,8 +3039,16 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
 
     if (kernel_version >= KVERSION(2,1,16)) {
         /* Set both local address and remote peer address (with route for it) via rtnetlink */
-        return sif6addr_rtnetlink(ifr.ifr_ifindex, our_eui64, his_eui64);
+        ret = sif6addr_rtnetlink(ifr.ifr_ifindex, our_eui64, his_eui64);
     } else {
+        ret = 0;
+    }
+
+    /*
+     * Linux kernel versions prior 3.11 do not support setting IPv6 peer address
+     * via rtnetlink. So if sif6addr_rtnetlink() fails then try old IOCTL method.
+     */
+    if (!ret) {
         /* Local interface */
         memset(&ifr6, 0, sizeof(ifr6));
         IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);