This adds support for ioctls for adding and removing routes in pfinet. Tested to still access network, but there seems to be a mismatch between <net/route.h> and <linux/route.h>, I can only make it compile using the linux header.
--- hurd/iioctl.defs | 17 ++- hurd/ioctl_types.h | 2 + pfinet/iioctl-ops.c | 266 ++++++++++++++++++++++++++++++++++++++++++++ pfinet/options.c | 107 +++--------------- 4 files changed, 301 insertions(+), 91 deletions(-) diff --git a/hurd/iioctl.defs b/hurd/iioctl.defs index dfa89033f..cd3378cd7 100644 --- a/hurd/iioctl.defs +++ b/hurd/iioctl.defs @@ -40,9 +40,24 @@ type ifname_t = array[16] of char; /* IFNAMSIZ is 16. */ definition of _IOT_ifreq in <net/if.h>. */ type sockaddr_t = struct[16] of char; /* sizeof(struct sockaddr) is 16. */ +/* This is the whole struct rtentry from <net/route.h>. */ +type rtentry_t = struct[84] of char; /* sizeof(struct rtentry) is 84. */ + skip; skip; skip; skip; /* 0 1 2 3 unused */ skip; skip; skip; skip; /* 4 5 6 7 unused */ -skip; skip; skip; skip; /* 8 9 10 11 unused */ +skip; skip; /* 8 9 unused */ + +/* 10 SIOCADDRT */ +routine iioctl_siocaddrt ( + reqport: io_t; + ifnam: ifname_t; + route: rtentry_t); + +/* 11 SIOCDELRT */ +routine iioctl_siocdelrt ( + reqport: io_t; + ifnam: ifname_t; + route: rtentry_t); /* 12 SIOCSIFADDR */ routine iioctl_siocsifaddr ( diff --git a/hurd/ioctl_types.h b/hurd/ioctl_types.h index 8baa36040..6a52fb817 100644 --- a/hurd/ioctl_types.h +++ b/hurd/ioctl_types.h @@ -30,4 +30,6 @@ typedef struct winsize winsize_t; typedef struct sockaddr sockaddr_t; typedef char ifname_t[16]; +#include <linux/route.h> +typedef struct rtentry rtentry_t; #endif /* hurd/ioctl_types.h */ diff --git a/pfinet/iioctl-ops.c b/pfinet/iioctl-ops.c index 191c65913..7a7bb9217 100644 --- a/pfinet/iioctl-ops.c +++ b/pfinet/iioctl-ops.c @@ -22,9 +22,14 @@ #include <linux/netdevice.h> #include <linux/notifier.h> +#include <linux/inetdevice.h> +#include <linux/ip.h> +#include <linux/route.h> +#include <linux/rtnetlink.h> #include "iioctl_S.h" #include <netinet/in.h> +#include <arpa/inet.h> #include <fcntl.h> #include <string.h> #include <unistd.h> @@ -35,6 +40,9 @@ #include <sys/ioctl.h> #include <net/if.h> #include <net/sock.h> +#include <net/route.h> +#include <net/ip_fib.h> +#include <net/addrconf.h> extern struct notifier_block *netdev_chain; @@ -64,6 +72,214 @@ struct device *get_dev (const char *name) return dev; } +static uint32_t +get_gateway(struct device *dev) +{ + uint32_t gateway = 0; + struct rt_key key = { 0 }; + struct fib_result res; + + /* Get gateway */ + gateway = INADDR_NONE; + key.oif = dev->ifindex; + if (! main_table->tb_lookup (main_table, &key, &res) + && FIB_RES_GW(res) != INADDR_ANY) + gateway = FIB_RES_GW (res); + return 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. + */ +static error_t +delete_gateway(struct device *dev, struct sockaddr_in *dst, struct sockaddr_in *gw) +{ + error_t err; + 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); + + memset (&req.rtm, 0, sizeof req.rtm); + memset (&rta, 0, sizeof rta); + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNICAST; + req.rtm.rtm_protocol = RTPROT_STATIC; + + /* Delete any existing default route on configured device */ + req.nlh.nlmsg_type = RTM_DELROUTE; + req.nlh.nlmsg_flags = 0; + rta.rta_oif = &dev->ifindex; + rta.rta_dst = &dst->sin_addr.s_addr; + rta.rta_gw = &gw->sin_addr.s_addr; + 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) + return err; + err = 0; + } + return err; +} + +static error_t +add_gateway(struct device *dev, struct sockaddr_in *dst, struct sockaddr_in *gw) +{ + error_t err; + 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); + + memset (&req.rtm, 0, sizeof req.rtm); + memset (&rta, 0, sizeof rta); + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNICAST; + req.rtm.rtm_protocol = RTPROT_STATIC; + + /* Add a gateway */ + rta.rta_oif = &dev->ifindex; + rta.rta_dst = &dst->sin_addr.s_addr; + rta.rta_gw = &gw->sin_addr.s_addr; + req.nlh.nlmsg_type = RTM_NEWROUTE; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_APPEND; + tb = fib_new_table (req.rtm.rtm_table); + err = (!tb ? ENOBUFS + : - (*tb->tb_insert) (tb, &req.rtm, &rta, &req.nlh, 0)); + return err; +} + +/* Setup a static route (required for e.g. DHCP) */ +static error_t +add_static_route(struct device *dev, struct sockaddr_in *dst) +{ + error_t err; + struct kern_rta rta; + struct + { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct fib_table *tb; + + if (!dev->name) + return ENODEV; + + /* Simulate the SIOCADDRT behavior. */ + memset (&req.rtm, 0, sizeof req.rtm); + memset (&rta, 0, sizeof rta); + req.nlh.nlmsg_type = RTM_NEWROUTE; + + /* Append this routing for addr. By this way we can always send + dhcp messages (e.g dhcp renew). */ + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_APPEND; + req.rtm.rtm_protocol = RTPROT_BOOT; + req.rtm.rtm_scope = RT_SCOPE_LINK; + req.rtm.rtm_type = RTN_UNICAST; + rta.rta_dst = &dst->sin_addr.s_addr; + rta.rta_oif = &dev->ifindex; + + tb = fib_new_table (req.rtm.rtm_table); + if (tb) + err = tb->tb_insert (tb, &req.rtm, &rta, &req.nlh, NULL); + else + err = ENOBUFS; + return err; +} + +static error_t +delete_static_route(struct device *dev, struct sockaddr_in *dst) +{ + error_t err; + 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); + + memset (&req.rtm, 0, sizeof req.rtm); + memset (&rta, 0, sizeof rta); + + /* Delete existing static route on configured device matching src/dst */ + req.nlh.nlmsg_type = RTM_DELROUTE; + req.nlh.nlmsg_flags = 0; + + req.rtm.rtm_protocol = RTPROT_BOOT; + req.rtm.rtm_scope = RT_SCOPE_LINK; + req.rtm.rtm_type = RTN_UNICAST; + rta.rta_dst = &dst->sin_addr.s_addr; + rta.rta_oif = &dev->ifindex; + 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) + return err; + err = 0; + } + return err; +} + +error_t +add_route (struct device *dev, struct rtentry *route) +{ + error_t err; + struct sockaddr_in *dst = (struct sockaddr_in *)&route->rt_dst; + struct sockaddr_in *gw = (struct sockaddr_in *)&route->rt_gateway; + + if (!dst) + return EINVAL; + + if (route->rt_flags & RTF_GATEWAY) + err = add_gateway(dev, dst, gw); + else + err = add_static_route(dev, dst); + + return err; +} + +error_t +delete_route (struct device *dev, struct rtentry *route) +{ + error_t err; + struct sockaddr_in *dst = (struct sockaddr_in *)&route->rt_dst; + struct sockaddr_in *gw = (struct sockaddr_in *)&route->rt_gateway; + + if (!dst) + return EINVAL; + + if (route->rt_flags & RTF_GATEWAY) + err = delete_gateway(dev, dst, gw); + else + err = delete_static_route(dev, dst); + + return err; +} + enum siocgif_type { ADDR, @@ -158,6 +374,56 @@ siocsifXaddr (struct sock_user *user, return err; } +/* 10 SIOCADDRT -- Add a network route */ +kern_return_t +S_iioctl_siocaddrt (struct sock_user *user, + const ifname_t ifnam, + const struct rtentry route) +{ + error_t err = 0; + struct device *dev; + + if (!user) + return EOPNOTSUPP; + + dev = get_dev (ifnam); + + if (!dev) + err = ENODEV; + else if (user->sock->sk->family != AF_INET) + err = EINVAL; + else + err = add_route (dev, &route); + + pthread_mutex_unlock (&global_lock); + return err; +} + +/* 11 SIOCDELRT -- Delete a network route */ +kern_return_t +S_iioctl_siocdelrt (struct sock_user *user, + const ifname_t ifnam, + const struct rtentry route) +{ + error_t err = 0; + struct device *dev; + + if (!user) + return EOPNOTSUPP; + + dev = get_dev (ifnam); + + if (!dev) + err = ENODEV; + else if (user->sock->sk->family != AF_INET) + err = EINVAL; + else + err = delete_route (dev, &route); + + pthread_mutex_unlock (&global_lock); + return err; +} + /* 12 SIOCSIFADDR -- Set address of a network interface. */ SIOCSIF (addr, ADDR); diff --git a/pfinet/options.c b/pfinet/options.c index ae44759dc..6de0b580a 100644 --- a/pfinet/options.c +++ b/pfinet/options.c @@ -60,6 +60,10 @@ 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); +/* iioctl.c */ +extern error_t add_route (struct device *dev, struct rtentry *route); +extern error_t delete_route (struct device *dev, struct rtentry *route); + #ifdef CONFIG_IPV6 static struct rt6_info * ipv6_get_dflt_router (void); #endif @@ -504,65 +508,32 @@ parse_opt (int opt, char *arg, struct argp_state *state) #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); + /* Set the default gateway. */ - memset (&req.rtm, 0, sizeof req.rtm); - memset (&rta, 0, sizeof rta); - req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; - req.rtm.rtm_type = RTN_UNICAST; - req.rtm.rtm_protocol = RTPROT_STATIC; + { + struct rtentry route = {0}; /* hardcoded dst/gw 0.0.0.0 */ + route.rt_flags = RTF_GATEWAY; if (!gw4_in) { /* Delete any existing default route on configured devices */ - for (in = h->interfaces; in < h->interfaces + h->num_interfaces; - in++) + for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) { - req.nlh.nlmsg_type = RTM_DELROUTE; - req.nlh.nlmsg_flags = 0; - rta.rta_oif = &in->device->ifindex; - tb = fib_get_table (req.rtm.rtm_table); - if (tb) + err = delete_route (in->device, &route); + if (err) { - err = - (*tb->tb_delete) - (tb, &req.rtm, &rta, &req.nlh, 0); - if (err && err != ESRCH) - { - pthread_mutex_unlock (&global_lock); - FAIL (err, 17, 0, - "cannot remove old default gateway"); - } - err = 0; + pthread_mutex_unlock (&global_lock); + FAIL (err, 17, 0, "cannot delete default gateway"); } } } else { /* Add a default route, replacing any existing one. */ - rta.rta_oif = &gw4_in->device->ifindex; - rta.rta_gw = &gw4_in->gateway; - 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)); + err = add_route (gw4_in->device, &route); if (err) { - pthread_mutex_unlock (&global_lock); + pthread_mutex_unlock (&global_lock); FAIL (err, 17, 0, "cannot set default gateway"); } } @@ -592,55 +563,11 @@ parse_opt (int opt, char *arg, struct argp_state *state) /* Setup the routing required for DHCP. */ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) { - struct kern_rta rta; - struct - { - struct nlmsghdr nlh; - struct rtmsg rtm; - } req; - struct fib_table *tb; - struct rtentry route; - struct sockaddr_in *dst; - struct device *dev; - if (!in->device) continue; + struct rtentry route = {0}; // hardcoded 0.0.0.0 dst address - dst = (struct sockaddr_in *) &route.rt_dst; - if (!in->device->name) - { - pthread_mutex_unlock (&global_lock); - FAIL (ENODEV, 17, 0, "unknown device"); - } - dev = dev_get (in->device->name); - if (!dev) - { - pthread_mutex_unlock (&global_lock); - FAIL (ENODEV, 17, 0, "unknown device"); - } - - /* Simulate the SIOCADDRT behavior. */ - memset (&route, 0, sizeof (struct rtentry)); - memset (&req.rtm, 0, sizeof req.rtm); - memset (&rta, 0, sizeof rta); - req.nlh.nlmsg_type = RTM_NEWROUTE; - - /* Append this routing for 0.0.0.0. By this way we can send always - dhcp messages (e.g dhcp renew). */ - req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE - | NLM_F_APPEND; - req.rtm.rtm_protocol = RTPROT_BOOT; - req.rtm.rtm_scope = RT_SCOPE_LINK; - req.rtm.rtm_type = RTN_UNICAST; - rta.rta_dst = &dst->sin_addr.s_addr; - rta.rta_oif = &dev->ifindex; - - tb = fib_new_table (req.rtm.rtm_table); - if (tb) - err = tb->tb_insert (tb, &req.rtm, &rta, &req.nlh, NULL); - else - err = ENOBUFS; - + err = add_route (in->device, &route); if (err) { pthread_mutex_unlock (&global_lock); -- 2.34.1