diff options
Diffstat (limited to 'pfinet/options.c')
-rw-r--r-- | pfinet/options.c | 351 |
1 files changed, 310 insertions, 41 deletions
diff --git a/pfinet/options.c b/pfinet/options.c index b308dc36..21a35c61 100644 --- a/pfinet/options.c +++ b/pfinet/options.c @@ -1,8 +1,8 @@ /* Pfinet option parsing - Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 2000, 2001, 2006, 2007 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.ai.mit.edu> + Written by Miles Bader <miles@gnu.org> This file is part of the GNU Hurd. @@ -25,14 +25,41 @@ #include <hurd.h> #include <argp.h> #include <argz.h> +#include <error.h> #include <netinet/in.h> #include <arpa/inet.h> #include "pfinet.h" +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/ip.h> +#include <linux/route.h> +#include <linux/rtnetlink.h> +#include <net/route.h> +#include <net/sock.h> +#include <net/ip_fib.h> +#include <net/ip6_fib.h> +#include <net/ip6_route.h> +#include <net/addrconf.h> + /* Our interface to the set of devices. */ extern error_t find_device (char *name, struct device **device); extern error_t enumerate_devices (error_t (*fun) (struct device *dev)); + +/* devinet.c */ +extern error_t configure_device (struct device *dev, uint32_t addr, + uint32_t netmask, uint32_t peer, + uint32_t broadcast); +extern void inquire_device (struct device *dev, uint32_t *addr, + uint32_t *netmask, uint32_t *peer, + uint32_t *broadcast); + +/* addrconf.c */ +extern struct inet6_dev *ipv6_find_idev (struct device *dev); +extern int inet6_addr_add (int ifindex, struct in6_addr *pfx, int plen); +extern int inet6_addr_del (int ifindex, struct in6_addr *pfx, int plen); + /* Pfinet options. Used for both startup and runtime. */ static const struct argp_option options[] = @@ -41,7 +68,14 @@ static const struct argp_option options[] = {0,0,0,0,"These apply to a given interface:", 2}, {"address", 'a', "ADDRESS", 0, "Set the network address"}, {"netmask", 'm', "MASK", 0, "Set the netmask"}, + {"peer", 'p', "ADDRESS", 0, "Set the peer address"}, {"gateway", 'g', "ADDRESS", 0, "Set the default gateway"}, + {"ipv4", '4', "NAME", 0, "Put active IPv4 translator on NAME"}, +#ifdef CONFIG_IPV6 + {"ipv6", '6', "NAME", 0, "Put active IPv6 translator on NAME"}, + {"address6", 'A', "ADDR/LEN",0, "Set the global IPv6 address"}, + {"gateway6", 'G', "ADDRESS", 0, "Set the IPv6 default gateway"}, +#endif {"shutdown", 's', 0, 0, "Shut it down"}, {0} }; @@ -56,8 +90,14 @@ struct parse_interface /* The network interface in question. */ struct device *device; - /* New values to apply to it. */ - unsigned long address, netmask, gateway; + /* New values to apply to it. (IPv4) */ + uint32_t address, netmask, peer, gateway; + +#ifdef CONFIG_IPV6 + /* New IPv6 configuration to apply. */ + struct inet6_ifaddr address6; + struct in6_addr gateway6; +#endif }; /* Used to hold data during argument parsing. */ @@ -88,9 +128,29 @@ parse_hook_add_interface (struct parse_hook *h) h->curint->device = 0; h->curint->address = INADDR_NONE; h->curint->netmask = INADDR_NONE; + h->curint->peer = INADDR_NONE; h->curint->gateway = INADDR_NONE; + +#ifdef CONFIG_IPV6 + memset (&h->curint->address6, 0, sizeof (struct inet6_ifaddr)); + memset (&h->curint->gateway6, 0, sizeof (struct in6_addr)); +#endif + return 0; } + +#ifdef CONFIG_IPV6 +static struct rt6_info * +ipv6_get_dflt_router (void) +{ + struct in6_addr daddr = { 0 }; + + struct fib6_node *fib = fib6_lookup + (&ip6_routing_table, &daddr, NULL); + return fib->leaf; +} +#endif /* CONFIG_IPV6 */ + static error_t parse_opt (int opt, char *arg, struct argp_state *state) @@ -117,6 +177,7 @@ parse_opt (int opt, char *arg, struct argp_state *state) /* Parse STR and return the corresponding internet address. If STR is not a valid internet address, signal an error mentioned TYPE. */ +#undef ADDR #define ADDR(str, type) \ ({ unsigned long addr = inet_addr (str); \ if (addr == INADDR_NONE) PERR (EINVAL, "Malformed %s", type); \ @@ -125,6 +186,11 @@ parse_opt (int opt, char *arg, struct argp_state *state) switch (opt) { struct parse_interface *in; + uint32_t gateway; +#ifdef CONFIG_IPV6 + struct parse_interface *gw6_in; + char *ptr; +#endif case 'i': /* An interface. */ @@ -154,25 +220,73 @@ parse_opt (int opt, char *arg, struct argp_state *state) break; case 'a': - h->curint->address = ADDR (arg, "address"); + h->curint->address = ADDR (arg, "address"); if (!IN_CLASSA (ntohl (h->curint->address)) && !IN_CLASSB (ntohl (h->curint->address)) && !IN_CLASSC (ntohl (h->curint->address))) { if (IN_MULTICAST (ntohl (h->curint->address))) - FAIL (EINVAL, 1, 0, - "%s: Cannot set interface address to multicast address", + FAIL (EINVAL, 1, 0, + "%s: Cannot set interface address to multicast address", arg); - else + else FAIL (EINVAL, 1, 0, "%s: Illegal or undefined network address", arg); } break; case 'm': h->curint->netmask = ADDR (arg, "netmask"); break; + case 'p': + h->curint->peer = ADDR (arg, "peer"); break; case 'g': h->curint->gateway = ADDR (arg, "gateway"); break; + case '4': + pfinet_bind (PORTCLASS_INET, arg); + + /* Install IPv6 port class on bootstrap port. */ + pfinet_bootstrap_portclass = PORTCLASS_INET6; + break; + +#ifdef CONFIG_IPV6 + case '6': + pfinet_bind (PORTCLASS_INET6, arg); + break; + + case 'A': + if ((ptr = strchr (arg, '/'))) + { + h->curint->address6.prefix_len = atoi (ptr + 1); + if (h->curint->address6.prefix_len > 128) + FAIL (EINVAL, 1, 0, "%s: The prefix-length is invalid", arg); + + *ptr = 0; + } + else + { + h->curint->address6.prefix_len = 64; + fprintf (stderr, "No prefix-length given, defaulting to %s/64.\n", + arg); + } + + if (inet_pton (AF_INET6, arg, &h->curint->address6.addr) <= 0) + PERR (EINVAL, "Malformed address"); + + if (IN6_IS_ADDR_MULTICAST (&h->curint->address6.addr)) + FAIL (EINVAL, 1, 0, "%s: Cannot set interface address to " + "multicast address", arg); + break; + + case 'G': + if (inet_pton (AF_INET6, arg, &h->curint->gateway6) <= 0) + PERR (EINVAL, "Malformed gateway"); + + if (IN6_IS_ADDR_MULTICAST (&h->curint->gateway6)) + FAIL (EINVAL, 1, 0, "%s: Cannot set gateway to " + "multicast address", arg); + break; +#endif /* CONFIG_IPV6 */ + case ARGP_KEY_INIT: /* Initialize our parsing state. */ h = malloc (sizeof (struct parse_hook)); @@ -201,57 +315,171 @@ parse_opt (int opt, char *arg, struct argp_state *state) if (err) FAIL (err, 13, 0, "No default interface"); } - +#if 0 /* XXX what does this mean??? */ /* Check for bogus option combinations. */ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) if (in->netmask != INADDR_NONE && in->address == INADDR_NONE && in->device->pa_addr == 0) /* Specifying a netmask for an address-less interface is a no-no. */ FAIL (EDESTADDRREQ, 14, 0, "Cannot set netmask"); +#endif - /* Successfully finished parsing, return a result. */ + gateway = INADDR_NONE; +#ifdef CONFIG_IPV6 + gw6_in = NULL; +#endif for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) { - struct device *dev = in->device; - if (in->address != INADDR_NONE || in->netmask != INADDR_NONE) + if (in->gateway != INADDR_NONE) + { + if (gateway != INADDR_NONE) + FAIL (err, 15, 0, "Cannot have multiple default gateways"); + gateway = in->gateway; + in->gateway = INADDR_NONE; + } + +#ifdef CONFIG_IPV6 + if (!IN6_IS_ADDR_UNSPECIFIED (&in->gateway6)) { - if (dev->pa_addr != 0) - /* There's already an address, delete the old entry. */ - ip_rt_del (dev->pa_addr & dev->pa_mask, dev); + if (gw6_in != NULL) + FAIL (err, 15, 0, "Cannot have multiple IPv6 " + "default gateways"); + gw6_in = in; + } +#endif + } + /* Successfully finished parsing, return a result. */ - if (in->address != INADDR_NONE) - dev->pa_addr = in->address; + __mutex_lock (&global_lock); + + for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) + { +#ifdef CONFIG_IPV6 + struct inet6_dev *idev = NULL; + if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL + && in->device) + idev = ipv6_find_idev(in->device); +#endif - if (in->netmask != INADDR_NONE) - dev->pa_mask = in->netmask; - else + if (in->address != INADDR_NONE || in->netmask != INADDR_NONE) + { + err = configure_device (in->device, in->address, in->netmask, + in->peer, INADDR_NONE); + if (err) { - if (IN_CLASSA (ntohl (dev->pa_addr))) - dev->pa_mask = htonl (IN_CLASSA_NET); - else if (IN_CLASSB (ntohl (dev->pa_addr))) - dev->pa_mask = htonl (IN_CLASSB_NET); - else if (IN_CLASSC (ntohl (dev->pa_addr))) - dev->pa_mask = htonl (IN_CLASSC_NET); - else - abort (); + __mutex_unlock (&global_lock); + FAIL (err, 16, 0, "cannot configure interface"); } + } + +#ifdef CONFIG_IPV6 + if (!idev) + continue; - dev->family = AF_INET; - dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; + /* First let's remove all non-local addresses. */ + struct inet6_ifaddr *ifa = idev->addr_list; - ip_rt_add (0, dev->pa_addr & dev->pa_mask, dev->pa_mask, - 0, dev, 0, 0); + while (ifa) + { + struct inet6_ifaddr *c_ifa = ifa; + ifa = ifa->if_next; + + if (IN6_ARE_ADDR_EQUAL (&c_ifa->addr, &in->address6.addr)) + memset (&in->address6, 0, sizeof (struct inet6_ifaddr)); + + else if (!IN6_IS_ADDR_LINKLOCAL (&c_ifa->addr) + && !IN6_IS_ADDR_SITELOCAL (&c_ifa->addr)) + inet6_addr_del (in->device->ifindex, &c_ifa->addr, + c_ifa->prefix_len); } - if (in->gateway != INADDR_NONE) + + if (!IN6_IS_ADDR_UNSPECIFIED (&in->address6.addr)) + { + /* Now assign the new address */ + inet6_addr_add (in->device->ifindex, &in->address6.addr, + in->address6.prefix_len); + } +#endif /* CONFIG_IPV6 */ + } + + /* Set the default gateway. This code is cobbled together from what + the SIOCADDRT ioctl code does, and from the apparent functionality + of the "netlink" layer from perusing a little. */ + { + struct kern_rta rta; + struct + { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct fib_table *tb; + + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 0; + req.nlh.nlmsg_len = NLMSG_LENGTH (sizeof req.rtm); + + bzero (&req.rtm, sizeof req.rtm); + bzero (&rta, sizeof rta); + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNICAST; + req.rtm.rtm_protocol = RTPROT_STATIC; + rta.rta_gw = &gateway; + + if (gateway == INADDR_NONE) + { + /* Delete any existing default route. */ + req.nlh.nlmsg_type = RTM_DELROUTE; + req.nlh.nlmsg_flags = 0; + tb = fib_get_table (req.rtm.rtm_table); + if (tb) + { + err = - (*tb->tb_delete) (tb, &req.rtm, &rta, &req.nlh, 0); + if (err && err != ESRCH) + { + __mutex_unlock (&global_lock); + FAIL (err, 17, 0, "cannot remove old default gateway"); + } + err = 0; + } + } + else + { + /* Add a default route, replacing any existing one. */ + req.nlh.nlmsg_type = RTM_NEWROUTE; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; + tb = fib_new_table (req.rtm.rtm_table); + err = (!tb ? ENOBUFS + : - (*tb->tb_insert) (tb, &req.rtm, &rta, &req.nlh, 0)); + if (err) + { + __mutex_unlock (&global_lock); + FAIL (err, 17, 0, "cannot set default gateway"); + } + } + } + + /* Set IPv6 default router. */ +#ifdef CONFIG_IPV6 + if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL) + { + struct rt6_info *rt6i = ipv6_get_dflt_router (); + + if (!gw6_in || rt6i->rt6i_dev != gw6_in->device + || !IN6_ARE_ADDR_EQUAL (&rt6i->rt6i_gateway, &gw6_in->gateway6)) { - ip_rt_del (0, dev); - ip_rt_add (RTF_GATEWAY, 0, 0, in->gateway, dev, 0, 0); + rt6_purge_dflt_routers (0); + if (gw6_in) + rt6_add_dflt_router (&gw6_in->gateway6, gw6_in->device); } } +#endif + + __mutex_unlock (&global_lock); + /* Fall through to free hook. */ case ARGP_KEY_ERROR: - /* Parsing error occured, free everything. */ + /* Parsing error occurred, free everything. */ free_hook: free (h->interfaces); free (h); @@ -275,6 +503,11 @@ trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) error_t add_dev_opts (struct device *dev) { error_t err = 0; + uint32_t addr, mask, peer, broad; + struct rt_key key = { 0 }; + struct fib_result res; + + inquire_device (dev, &addr, &mask, &peer, &broad); #define ADD_OPT(fmt, args...) \ do { char buf[100]; \ @@ -287,12 +520,48 @@ trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) ADD_OPT ("--%s=%s", name, inet_ntoa (i)); } while (0) ADD_OPT ("--interface=%s", dev->name); - if (dev->pa_addr != 0) - ADD_ADDR_OPT ("address", dev->pa_addr); - if (dev->pa_mask != 0) - ADD_ADDR_OPT ("netmask", dev->pa_mask); + if (addr != INADDR_NONE) + ADD_ADDR_OPT ("address", addr); + if (mask != INADDR_NONE) + ADD_ADDR_OPT ("netmask", mask); + if (peer != addr) + ADD_ADDR_OPT ("peer", peer); + key.iif = dev->ifindex; + if (! main_table->tb_lookup (main_table, &key, &res)) + ADD_ADDR_OPT ("gateway", FIB_RES_GW (res)); + +#undef ADD_ADDR_OPT + +#ifdef CONFIG_IPV6 + struct inet6_dev *idev = NULL; + + if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL) + idev = ipv6_find_idev(dev); + + if (idev) + { + struct inet6_ifaddr *ifa = idev->addr_list; + static char addr_buf[INET6_ADDRSTRLEN]; + + /* Push all IPv6 addresses assigned to the interface. */ + do + { + inet_ntop (AF_INET6, &ifa->addr, addr_buf, INET6_ADDRSTRLEN); + ADD_OPT ("--address6=%s/%d", addr_buf, ifa->prefix_len); + } + while ((ifa = ifa->if_next)); + + /* Last not least push --gateway6 option. */ + struct rt6_info *rt6i = ipv6_get_dflt_router (); + if(rt6i->rt6i_dev == dev) + { + inet_ntop (AF_INET6, &rt6i->rt6i_gateway, addr_buf, + INET6_ADDRSTRLEN); + ADD_OPT ("--gateway6=%s", addr_buf); + } + } +#endif /* CONFIG_IPV6 */ - /* XXX how do we figure out the default gateway? */ #undef ADD_OPT return err; |