From 388597eee99beac6e5b7430d24c377169f51bd57 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sat, 19 Oct 2019 08:29:39 +0200 Subject: [PATCH] pppd: Add defaultroute6 and related options Which behave like IPv4's defaultroute etc. Signed-off-by: Samuel Thibault Signed-off-by: Paul Mackerras --- pppd/ipv6cp.c | 26 ++++++ pppd/ipv6cp.h | 1 + pppd/pppd.8 | 11 +++ pppd/pppd.h | 6 ++ pppd/sys-linux.c | 199 +++++++++++++++++++++++++++++++++++++++++++++ pppd/sys-solaris.c | 67 +++++++++++++++ 6 files changed, 310 insertions(+) diff --git a/pppd/ipv6cp.c b/pppd/ipv6cp.c index f9badc1..bda91e9 100644 --- a/pppd/ipv6cp.c +++ b/pppd/ipv6cp.c @@ -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)); } diff --git a/pppd/ipv6cp.h b/pppd/ipv6cp.h index 2f4c06d..25235d2 100644 --- a/pppd/ipv6cp.h +++ b/pppd/ipv6cp.h @@ -150,6 +150,7 @@ 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 */ diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 76a2097..ef6046e 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -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. diff --git a/pppd/pppd.h b/pppd/pppd.h index 6e3743f..170eed9 100644 --- a/pppd/pppd.h +++ b/pppd/pppd.h @@ -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)); diff --git a/pppd/sys-linux.c b/pppd/sys-linux.c index eedcb53..89263ed 100644 --- a/pppd/sys-linux.c +++ b/pppd/sys-linux.c @@ -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. diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c index 633cf5f..1d6f012 100644 --- a/pppd/sys-solaris.c +++ b/pppd/sys-solaris.c @@ -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) */ -- 2.39.2