pppd: Separate IPv6 handling for sifup/sifdown
authorBenjamin Cama <benjamin.cama@telecom-bretagne.eu>
Wed, 26 Feb 2014 18:13:39 +0000 (19:13 +0100)
committerPaul Mackerras <paulus@samba.org>
Sun, 9 Mar 2014 03:55:18 +0000 (14:55 +1100)
The current code is buggy regarding handling of link state when using
both IPCP and IPv6CP: if IPv6CP has been set up and if during IPCP
negociation, ipcp_up() fails, it will incorrectly take the interface
down. The simple solution here is to change the platform code to do the
same as on Solaris: separate IPv6CP up/down state handling with sif6up()
and sif6down(), so that we really know when the interface is allowed to
go down.

Signed-off-by: Benjamin Cama <benjamin.cama@telecom-bretagne.eu>
Signed-off-by: Paul Mackerras <paulus@samba.org>
pppd/ipv6cp.c
pppd/sys-linux.c

index caa2b265d7e7b20f3b3bb1a093d88d26fb1cdcda..7e760732d42db32507b4032bdf2e71b88dbecfa1 100644 (file)
@@ -1147,13 +1147,13 @@ ipv6_demand_conf(u)
     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
 
 #if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-#if defined(SOL2)
+#if defined(SOL2) || defined(__linux__)
     if (!sif6up(u))
        return 0;
 #else
     if (!sifup(u))
        return 0;
-#endif /* defined(SOL2) */
+#endif /* defined(SOL2) || defined(__linux__) */
 #endif    
     if (!sif6addr(u, wo->ourid, wo->hisid))
        return 0;
@@ -1260,10 +1260,10 @@ ipv6cp_up(f)
 #endif
 
        /* bring the interface up for IPv6 */
-#if defined(SOL2)
+#if defined(SOL2) || defined(__linux__)
        if (!sif6up(f->unit)) {
            if (debug)
-               warn("sifup failed (IPV6)");
+               warn("sif6up failed (IPV6)");
            ipv6cp_close(f->unit, "Interface configuration failed");
            return;
        }
@@ -1274,7 +1274,7 @@ ipv6cp_up(f)
            ipv6cp_close(f->unit, "Interface configuration failed");
            return;
        }
-#endif /* defined(SOL2) */
+#endif /* defined(SOL2) || defined(__linux__) */
 
 #if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
        if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
@@ -1349,7 +1349,9 @@ ipv6cp_down(f)
        ipv6cp_clear_addrs(f->unit, 
                           ipv6cp_gotoptions[f->unit].ourid,
                           ipv6cp_hisoptions[f->unit].hisid);
-#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(__linux__)
+       sif6down(f->unit);
+#elif defined(SVR4) && (defined(SNI) || defined(__USLC))
        sifdown(f->unit);
 #endif
     }
index 72a7727c403f79762a93bf1342e3b02aa869c27a..a0af43b4e368f3a10b44f93c5b51d3ddf26c4a7c 100644 (file)
@@ -205,6 +205,7 @@ static char loop_name[20];
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
 
 static int     if_is_up;       /* Interface has been marked up */
+static int     if6_is_up;      /* Interface has been marked up for IPv6, to help differentiate */
 static int     have_default_route;     /* Gateway for default route added */
 static u_int32_t proxy_arp_addr;       /* Addr for proxy arp entry added */
 static char proxy_arp_dev[16];         /* Device for proxy arp entry */
@@ -338,6 +339,9 @@ void sys_cleanup(void)
        if_is_up = 0;
        sifdown(0);
     }
+    if (if6_is_up)
+       sif6down(0);
+
 /*
  * Delete any routes through the device.
  */
@@ -2252,25 +2256,12 @@ int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
 
 int sifup(int u)
 {
-    struct ifreq ifr;
-
-    memset (&ifr, '\0', sizeof (ifr));
-    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
-    if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-       if (! ok_error (errno))
-           error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
-       return 0;
-    }
+    int ret;
 
-    ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
-    if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-       if (! ok_error (errno))
-           error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
-       return 0;
-    }
-    if_is_up++;
+    if ((ret = setifstate(u, 1)))
+       if_is_up++;
 
-    return 1;
+    return ret;
 }
 
 /********************************************************************
@@ -2281,11 +2272,59 @@ int sifup(int u)
 
 int sifdown (int u)
 {
-    struct ifreq ifr;
-
     if (if_is_up && --if_is_up > 0)
        return 1;
 
+#ifdef INET6
+    if (if6_is_up)
+       return 1;
+#endif /* INET6 */
+
+    return setifstate(u, 0);
+}
+
+#ifdef INET6
+/********************************************************************
+ *
+ * sif6up - Config the interface up for IPv6
+ */
+
+int sif6up(int u)
+{
+    int ret;
+
+    if ((ret = setifstate(u, 1)))
+       if6_is_up = 1;
+
+    return ret;
+}
+
+/********************************************************************
+ *
+ * sif6down - Disable the IPv6CP protocol and config the interface
+ *           down if there are no remaining protocols.
+ */
+
+int sif6down (int u)
+{
+    if6_is_up = 0;
+
+    if (if_is_up)
+       return 1;
+
+    return setifstate(u, 0);
+}
+#endif /* INET6 */
+
+/********************************************************************
+ *
+ * setifstate - Config the interface up or down
+ */
+
+int setifstate (int u, int state)
+{
+    struct ifreq ifr;
+
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
@@ -2294,7 +2333,10 @@ int sifdown (int u)
        return 0;
     }
 
-    ifr.ifr_flags &= ~IFF_UP;
+    if (state)
+       ifr.ifr_flags |= IFF_UP;
+    else
+       ifr.ifr_flags &= ~IFF_UP;
     ifr.ifr_flags |= IFF_POINTOPOINT;
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))