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>
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 */
{ "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 },
wo->vj_protocol = IPV6CP_COMP;
#endif
+ /*
+ * XXX This controls whether the user may use the defaultroute option.
+ */
+ ao->default_route = 1;
}
#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));
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);
}
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));
}
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 */
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
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.
/* 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));
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 */
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 */
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);
*/
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);
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.
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)
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. */
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)
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) */