pppd: Add defaultroute6 and related options master
authorSamuel Thibault <samuel.thibault@ens-lyon.org>
Sat, 19 Oct 2019 06:29:39 +0000 (08:29 +0200)
committerPaul Mackerras <paulus@ozlabs.org>
Sat, 19 Oct 2019 06:29:39 +0000 (17:29 +1100)
Which behave like IPv4's defaultroute etc.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pppd/ipv6cp.c
pppd/ipv6cp.h
pppd/pppd.8
pppd/pppd.h
pppd/sys-linux.c
pppd/sys-solaris.c

index f9badc1e0f1da422642058e95a0fd064071d2280..bda91e90afb0b38ca303c666393deec19756e223 100644 (file)
@@ -177,6 +177,7 @@ ipv6cp_options ipv6cp_hisoptions[NUM_PPP];  /* Options that we ack'd */
 int no_ifaceid_neg = 0;
 
 /* local vars */
+static int default_route_set[NUM_PPP];         /* Have set up a default route */
 static int ipv6cp_is_up;
 
 /* Hook for a plugin to know when IPv6 protocol has come up */
@@ -245,6 +246,15 @@ static option_t ipv6cp_option_list[] = {
     { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
       "Accept peer's interface identifier for us", 1 },
 
+    { "defaultroute6", o_bool, &ipv6cp_wantoptions[0].default_route,
+      "Add default IPv6 route", OPT_ENABLE|1, &ipv6cp_allowoptions[0].default_route },
+    { "nodefaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route,
+      "disable defaultroute6 option", OPT_A2CLR,
+      &ipv6cp_wantoptions[0].default_route },
+    { "-defaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route,
+      "disable defaultroute6 option", OPT_ALIAS | OPT_A2CLR,
+      &ipv6cp_wantoptions[0].default_route },
+
     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
       "Use (default) IPv4 address as interface identifier", 1 },
 
@@ -443,6 +453,10 @@ ipv6cp_init(unit)
     wo->vj_protocol = IPV6CP_COMP;
 #endif
 
+    /*
+     * XXX This controls whether the user may use the defaultroute option.
+     */
+    ao->default_route = 1;
 }
 
 
@@ -1151,6 +1165,9 @@ ipv6_demand_conf(u)
 #endif
     if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
        return 0;
+    if (wo->default_route)
+       if (sif6defaultroute(u, wo->ourid, wo->hisid))
+           default_route_set[u] = 1;
 
     notice("ipv6_demand_conf");
     notice("local  LL address %s", llv6_ntoa(wo->ourid));
@@ -1230,6 +1247,10 @@ ipv6cp_up(f)
                return;
            }
 
+           /* assign a default route through the interface if required */
+           if (ipv6cp_wantoptions[f->unit].default_route)
+               if (sif6defaultroute(f->unit, go->ourid, ho->hisid))
+                   default_route_set[f->unit] = 1;
        }
        demand_rexmit(PPP_IPV6);
        sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
@@ -1251,6 +1272,11 @@ ipv6cp_up(f)
        }
        sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
 
+       /* assign a default route through the interface if required */
+       if (ipv6cp_wantoptions[f->unit].default_route)
+           if (sif6defaultroute(f->unit, go->ourid, ho->hisid))
+               default_route_set[f->unit] = 1;
+
        notice("local  LL address %s", llv6_ntoa(go->ourid));
        notice("remote LL address %s", llv6_ntoa(ho->hisid));
     }
index 2f4c06ddc1892c2c27f26a519f5e78efc82fc219..25235d27fd2313e202a8817fe48b58cf0128e1b6 100644 (file)
 typedef struct ipv6cp_options {
     int neg_ifaceid;           /* Negotiate interface identifier? */
     int req_ifaceid;           /* Ask peer to send interface identifier? */
+    int default_route;         /* Assign default route through interface? */
     int accept_local;          /* accept peer's value for iface id? */
     int opt_local;             /* ourtoken set by option */
     int opt_remote;            /* histoken set by option */
index 76a209766f2afc89b8e43187d87c05271f740ae5..ef6046e5b945492a8e73b2e36cda8591ba7c4d0a 100644 (file)
@@ -127,6 +127,12 @@ is no other default route with the same metric.  With the default
 value of -1, the route is only added if there is no default route at
 all.
 .TP
+.B defaultroute6
+Add a default IPv6 route to the system routing tables, using the peer as
+the gateway, when IPv6CP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken.  This option
+is privileged if the \fInodefaultroute6\fR option has been specified.
+.TP
 .B disconnect \fIscript
 Execute the command specified by \fIscript\fR, by passing it to a
 shell, after
@@ -743,6 +749,11 @@ Disable the \fIdefaultroute\fR option.  The system administrator who
 wishes to prevent users from creating default routes with pppd
 can do so by placing this option in the /etc/ppp/options file.
 .TP
+.B nodefaultroute6
+Disable the \fIdefaultroute6\fR option.  The system administrator who
+wishes to prevent users from adding a default route with pppd
+can do so by placing this option in the /etc/ppp/options file.
+.TP
 .B nodeflate
 Disables Deflate compression; pppd will not request or agree to
 compress packets using the Deflate scheme.
index 6e3743fd9c97de74caf9638e717aa60525bebabb..170eed97441a77c69090c680fb8fbd582ca87f58 100644 (file)
@@ -683,6 +683,12 @@ int  sifdefaultroute __P((int, u_int32_t, u_int32_t));
                                /* Create default route through i/f */
 int  cifdefaultroute __P((int, u_int32_t, u_int32_t));
                                /* Delete default route through i/f */
+#ifdef INET6
+int  sif6defaultroute __P((int, eui64_t, eui64_t));
+                               /* Create default IPv6 route through i/f */
+int  cif6defaultroute __P((int, eui64_t, eui64_t));
+                               /* Delete default IPv6 route through i/f */
+#endif
 int  sifproxyarp __P((int, u_int32_t));
                                /* Add proxy ARP entry for peer */
 int  cifproxyarp __P((int, u_int32_t));
index eedcb53b06923d3e39459551b0a2f594d99f8b47..89263ed1c5b75231471dc83271f591381cf45cd2 100644 (file)
@@ -163,6 +163,7 @@ struct in6_ifreq {
        eui64_copy(eui64, sin6.s6_addr32[2]);                   \
        } while (0)
 
+static const eui64_t nulleui64;
 #endif /* INET6 */
 
 /* We can get an EIO error on an ioctl if the modem has hung up */
@@ -207,6 +208,7 @@ 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 int     have_default_route6;    /* Gateway for default IPv6 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 */
 static u_int32_t our_old_addr;         /* for detecting address changes */
@@ -234,6 +236,7 @@ static void close_route_table (void);
 static int open_route_table (void);
 static int read_route_table (struct rtentry *rt);
 static int defaultroute_exists (struct rtentry *rt, int metric);
+static int defaultroute6_exists (struct in6_rtmsg *rt, int metric);
 static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
                           char *name, int namelen);
 static void decode_version (char *buf, int *version, int *mod, int *patch);
@@ -350,6 +353,10 @@ void sys_cleanup(void)
  */
     if (have_default_route)
        cifdefaultroute(0, 0, 0);
+#ifdef INET6
+    if (have_default_route6)
+       cif6defaultroute(0, nulleui64, nulleui64);
+#endif
 
     if (has_proxy_arp)
        cifproxyarp(0, proxy_arp_addr);
@@ -1710,6 +1717,198 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
     return 1;
 }
 
+#ifdef INET6
+/*
+ * /proc/net/ipv6_route parsing stuff.
+ */
+static int route_dest_plen_col;
+static int open_route6_table (void);
+static int read_route6_table (struct in6_rtmsg *rt);
+
+/********************************************************************
+ *
+ * open_route6_table - open the interface to the route table
+ */
+static int open_route6_table (void)
+{
+    char *path;
+
+    close_route_table();
+
+    path = path_to_procfs("/net/ipv6_route");
+    route_fd = fopen (path, "r");
+    if (route_fd == NULL) {
+       error("can't open routing table %s: %m", path);
+       return 0;
+    }
+
+    /* default to usual columns */
+    route_dest_col = 0;
+    route_dest_plen_col = 1;
+    route_gw_col = 4;
+    route_metric_col = 5;
+    route_flags_col = 8;
+    route_dev_col = 9;
+    route_num_cols = 10;
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * read_route6_table - read the next entry from the route table
+ */
+
+static void hex_to_in6_addr(struct in6_addr *addr, const char *s)
+{
+    char hex8[9];
+    unsigned i;
+    uint32_t v;
+
+    hex8[8] = 0;
+    for (i = 0; i < 4; i++) {
+       memcpy(hex8, s + 8*i, 8);
+       v = strtoul(hex8, NULL, 16);
+       addr->s6_addr32[i] = v;
+    }
+}
+
+static int read_route6_table(struct in6_rtmsg *rt)
+{
+    char *cols[ROUTE_MAX_COLS], *p;
+    int col;
+
+    memset (rt, '\0', sizeof (struct in6_rtmsg));
+
+    if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
+       return 0;
+
+    p = route_buffer;
+    for (col = 0; col < route_num_cols; ++col) {
+       cols[col] = strtok(p, route_delims);
+       if (cols[col] == NULL)
+           return 0;           /* didn't get enough columns */
+       p = NULL;
+    }
+
+    hex_to_in6_addr(&rt->rtmsg_dst, cols[route_dest_col]);
+    rt->rtmsg_dst_len = strtoul(cols[route_dest_plen_col], NULL, 16);
+    hex_to_in6_addr(&rt->rtmsg_gateway, cols[route_gw_col]);
+
+    rt->rtmsg_metric = strtoul(cols[route_metric_col], NULL, 16);
+    rt->rtmsg_flags = strtoul(cols[route_flags_col], NULL, 16);
+    rt->rtmsg_ifindex = if_nametoindex(cols[route_dev_col]);
+
+    return 1;
+}
+
+/********************************************************************
+ *
+ * defaultroute6_exists - determine if there is a default route
+ */
+
+static int defaultroute6_exists (struct in6_rtmsg *rt, int metric)
+{
+    int result = 0;
+
+    if (!open_route6_table())
+       return 0;
+
+    while (read_route6_table(rt) != 0) {
+       if ((rt->rtmsg_flags & RTF_UP) == 0)
+           continue;
+
+       if (rt->rtmsg_dst_len != 0)
+           continue;
+       if (rt->rtmsg_dst.s6_addr32[0] == 0L
+        && rt->rtmsg_dst.s6_addr32[1] == 0L
+        && rt->rtmsg_dst.s6_addr32[2] == 0L
+        && rt->rtmsg_dst.s6_addr32[3] == 0L
+        && (metric < 0 || rt->rtmsg_metric == metric)) {
+           result = 1;
+           break;
+       }
+    }
+
+    close_route_table();
+    return result;
+}
+
+/********************************************************************
+ *
+ * sif6defaultroute - assign a default route through the address given.
+ *
+ * If the global default_rt_repl_rest flag is set, then this function
+ * already replaced the original system defaultroute with some other
+ * route and it should just replace the current defaultroute with
+ * another one, without saving the current route. Use: demand mode,
+ * when pppd sets first a defaultroute it it's temporary ppp0 addresses
+ * and then changes the temporary addresses to the addresses for the real
+ * ppp connection when it has come up.
+ */
+
+int sif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway)
+{
+    struct in6_rtmsg rt;
+    char buf[IF_NAMESIZE];
+
+    if (defaultroute6_exists(&rt, dfl_route_metric) &&
+           rt.rtmsg_ifindex != if_nametoindex(ifname)) {
+       if (rt.rtmsg_flags & RTF_GATEWAY)
+           error("not replacing existing default route via gateway");
+       else
+           error("not replacing existing default route through %s",
+                 if_indextoname(rt.rtmsg_ifindex, buf));
+       return 0;
+    }
+
+    memset (&rt, 0, sizeof (rt));
+
+    rt.rtmsg_ifindex = if_nametoindex(ifname);
+    rt.rtmsg_metric = dfl_route_metric + 1; /* +1 for binary compatibility */
+    rt.rtmsg_dst_len = 0;
+
+    rt.rtmsg_flags = RTF_UP;
+    if (ioctl(sock6_fd, SIOCADDRT, &rt) < 0) {
+       if ( ! ok_error ( errno ))
+           error("default route ioctl(SIOCADDRT): %m");
+       return 0;
+    }
+
+    have_default_route6 = 1;
+    return 1;
+}
+
+/********************************************************************
+ *
+ * cif6defaultroute - delete a default route through the address given.
+ */
+
+int cif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway)
+{
+    struct in6_rtmsg rt;
+
+    have_default_route6 = 0;
+
+    memset (&rt, '\0', sizeof (rt));
+
+    rt.rtmsg_ifindex = if_nametoindex(ifname);
+    rt.rtmsg_metric = dfl_route_metric + 1; /* +1 for binary compatibility */
+    rt.rtmsg_dst_len = 0;
+
+    rt.rtmsg_flags = RTF_UP;
+    if (ioctl(sock6_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
+       if (still_ppp()) {
+           if ( ! ok_error ( errno ))
+               error("default route ioctl(SIOCDELRT): %m");
+           return 0;
+       }
+    }
+
+    return 1;
+}
+#endif /* INET6 */
+
 /********************************************************************
  *
  * sifproxyarp - Make a proxy ARP entry for the peer.
index 633cf5f78cf9fa48958d4a7bbf38b1c294a4ae24..1d6f012c90f2fc9113461dc5d9f051fce31babc6 100644 (file)
@@ -197,12 +197,20 @@ static int        if6_is_up = 0;  /* IPv6 interface has been marked up */
        l.lifr_addr = laddr;                    \
        } while (0)
 
+#define _IN6A_LLX_FROM_EUI64(s, eui64, as) do {        \
+       s->s6_addr32[0] = htonl(as);    \
+       eui64_copy(eui64, s->s6_addr32[2]);     \
+       } while (0)
+
 #define IN6_LLADDR_FROM_EUI64(l, s, eui64)  \
     _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000)
 
 #define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \
     _IN6_LLX_FROM_EUI64(l, s, eui64, 0)
 
+#define IN6A_LLADDR_FROM_EUI64(s, eui64)  \
+    _IN6A_LLX_FROM_EUI64(s, eui64, 0xfe800000)
+
 #endif /* defined(INET6) && defined(SOL2) */
 
 #if defined(INET6) && defined(SOL2)
@@ -237,6 +245,7 @@ static int  tty_npushed;
 static int     if_is_up;       /* Interface has been marked up */
 static u_int32_t remote_addr;          /* IP address of peer */
 static u_int32_t default_route_gateway;        /* Gateway for default route added */
+static eui64_t default_route_gateway6; /* Gateway for default IPv6 route added */
 static u_int32_t proxy_arp_addr;       /* Addr for proxy arp entry added */
 
 /* Prototypes for procedures local to this file. */
@@ -786,6 +795,8 @@ sys_cleanup()
        sifdown(0);
     if (default_route_gateway)
        cifdefaultroute(0, default_route_gateway, default_route_gateway);
+    if (default_route_gateway6)
+       cif6defaultroute(0, default_route_gateway6, default_route_gateway6);
     if (proxy_arp_addr)
        cifproxyarp(0, proxy_arp_addr);
 #if defined(SOL2)
@@ -1956,6 +1967,62 @@ cif6addr(u, o, h)
     return 1;
 }
 
+/*
+ * sif6defaultroute - assign a default route through the address given.
+ */
+int
+sif6defaultroute(u, l, g)
+    int u;
+    eui64_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;                     /* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    memset(&rt.rtmsg_dst, 0, sizeof(rt.rtmsg_dst));
+    rt.rtmsg_dst_len = 0;
+    IN6A_LLADDR_FROM_EUI64(&rt.rtmsg_gateway, g);
+    rt.rtmsg_flags = RTF_GATEWAY;
+
+    if (ioctl(ip6fd, SIOCADDRT, &rt) < 0) {
+       error("Can't add default route: %m");
+       return 0;
+    }
+
+    default_route_gateway6 = g;
+    return 1;
+}
+
+/*
+ * cif6defaultroute - delete a default route through the address given.
+ */
+int
+cif6defaultroute(u, l, g)
+    int u;
+    eui64_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;                     /* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    memset(&rt.rtmsg_dst, 0, sizeof(rt.rtmsg_dst));
+    rt.rtmsg_dst_len = 0;
+    IN6A_LLADDR_FROM_EUI64(&rt.rtmsg_gateway, g);
+    rt.rtmsg_flags = RTF_GATEWAY;
+
+    if (ioctl(ip6fd, SIOCDELRT, &rt) < 0) {
+       error("Can't delete default route: %m");
+       return 0;
+    }
+
+    default_route_gateway6 = 0;
+    return 1;
+}
+
 #endif /* defined(SOL2) && defined(INET6) */