summaryrefslogtreecommitdiff
path: root/pfinet/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'pfinet/options.c')
-rw-r--r--pfinet/options.c351
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;