From: Joan Lledó <[email protected]>
The new methods try to map common network routing to limited lwip support for
routes.
Only two scenarios ar supported:
* Add/delete subnet route
* Add/delete default route
---
lwip/iioctl-ops.c | 188 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 182 insertions(+), 6 deletions(-)
diff --git a/lwip/iioctl-ops.c b/lwip/iioctl-ops.c
index c7b6c332..a9e382ee 100644
--- a/lwip/iioctl-ops.c
+++ b/lwip/iioctl-ops.c
@@ -30,7 +30,10 @@
#include <lwip-hurd.h>
#include <lwip-util.h>
+#include <lwip/tcpip.h>
#include <netif/ifcommon.h>
+#include <netinet/in.h>
+#include <hurd/ioctl_types.h>
/* Get the interface from its name */
static struct netif *
@@ -167,22 +170,195 @@ siocsifXaddr (struct sock_user *user,
return err;
}
+static void
+set_default_if (void *arg)
+{
+ struct netif *netif;
+
+ netif = (struct netif *) arg;
+
+ netif_set_default (netif);
+}
+
/* 10 SIOCADDRT -- Add a network route */
+/*
+ * Lwip routing is very limited. Each netif has one gateway and all packets
from/to that netif go through there.
+ * Considering this, we need to behave as clients expect.
+ *
+ * These are the supported scenarios:
+ * - A client sending an interface plus a netmask but gateway=any: intends
to add a subnet route.
+ * e.g. `192.168.1.0/24 dev eth0`
+ * - A client sending an interface plus a gateway but netmask=any: intends
to set a default gateway.
+ * e.g. `0.0.0.0/0 via 192.168.1.1`
+ */
kern_return_t
lwip_S_rioctl_siocaddrt (struct sock_user *user,
- const ifname_t ifnam,
- const struct srtentry route)
+ const ifname_t ifnam, const struct srtentry route)
{
- return EOPNOTSUPP;
+ kern_return_t err = 0;
+ struct netif *netif;
+ struct sockaddr sa;
+ size_t buflen = sizeof (struct sockaddr);
+ uint32_t ipv4_addrs[5];
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (!user->isroot)
+ return EPERM;
+
+ /* All ones netmask means host route, not supported by lwip */
+ if (route.rt_mask == INADDR_NONE)
+ return EOPNOTSUPP;
+
+ netif = get_if (ifnam);
+ if (!netif)
+ return ENODEV;
+
+ err = lwip_getsockname (user->sock->sockno, &sa, (socklen_t *)&buflen);
+ if (err)
+ return err;
+
+ if (sa.sa_family != AF_INET)
+ return EINVAL;
+
+ inquire_device (netif, &ipv4_addrs[ADDR], &ipv4_addrs[NETMASK],
+ &ipv4_addrs[DSTADDR], &ipv4_addrs[BRDADDR],
+ &ipv4_addrs[GWADDR], 0, 0);
+
+ if (route.rt_mask != INADDR_ANY && route.rt_gateway == INADDR_ANY)
+ {
+ /*
+ * Subnet route.
+ * Only one network can go through the interface so we set the netmask
to the interface.
+ */
+
+ /* masking current IP must match given dest to be valid */
+ if (ipv4_addrs[ADDR] != INADDR_ANY && ipv4_addrs[ADDR] != INADDR_NONE
+ && (ipv4_addrs[ADDR] & route.rt_mask) != route.rt_dest)
+ return ENETUNREACH;
+
+ ipv4_addrs[NETMASK] = route.rt_mask;
+ }
+ else if (route.rt_gateway != INADDR_ANY)
+ {
+ /*
+ * Netmask is any, and we got a gateway so client is trying to add a
default route.
+ * We set the given gateway to the given interface and set the interface
as default.
+ */
+
+ /* First we verify the gateway is reachable from this netif */
+ if (ipv4_addrs[ADDR] != INADDR_ANY && ipv4_addrs[ADDR] != INADDR_NONE
+ && ipv4_addrs[NETMASK] != INADDR_ANY
+ && ipv4_addrs[NETMASK] != INADDR_NONE
+ && (route.rt_gateway & ipv4_addrs[NETMASK]) !=
+ (ipv4_addrs[ADDR] & ipv4_addrs[NETMASK]))
+ return EHOSTUNREACH;
+
+ ipv4_addrs[GWADDR] = route.rt_gateway;
+ tcpip_callback (set_default_if, netif);
+ }
+ else
+ {
+ /* Any other scenario not supported */
+ return EOPNOTSUPP;
+ }
+
+ err = configure_device (netif, ipv4_addrs[ADDR], ipv4_addrs[NETMASK],
+ ipv4_addrs[DSTADDR], ipv4_addrs[BRDADDR],
+ ipv4_addrs[GWADDR], 0, 0);
+
+ return err;
}
/* 11 SIOCDELRT -- Delete a network route */
+/*
+ * The only routing lwip supports is the default gateway for each netif.
+ * We interpret "deleting a route" as removing the current gateway and netmask,
+ * but only if the given route matches.
+ *
+ * Supported scenarios:
+ * - A client sending an interface plus a netmask but gateway=any: intends
to remove a subnet route.
+ * e.g. `192.168.1.0/24 dev eth0`
+ * - A client sending an interface plus a gateway but netmask=any: intends
to remove a default gateway.
+ * e.g. `0.0.0.0/0 via 192.168.1.1`
+ */
kern_return_t
lwip_S_rioctl_siocdelrt (struct sock_user *user,
- const ifname_t ifnam,
- const struct srtentry route)
+ const ifname_t ifnam, const struct srtentry route)
{
- return EOPNOTSUPP;
+ kern_return_t err = 0;
+ struct netif *netif;
+ struct sockaddr sa;
+ size_t buflen = sizeof (struct sockaddr);
+ uint32_t ipv4_addrs[5];
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (!user->isroot)
+ return EPERM;
+
+ netif = get_if (ifnam);
+ if (!netif)
+ return ENODEV;
+
+ err = lwip_getsockname (user->sock->sockno, &sa, (socklen_t *)&buflen);
+ if (err)
+ return err;
+
+ if (sa.sa_family != AF_INET)
+ return EINVAL;
+
+ inquire_device (netif, &ipv4_addrs[ADDR], &ipv4_addrs[NETMASK],
+ &ipv4_addrs[DSTADDR], &ipv4_addrs[BRDADDR],
+ &ipv4_addrs[GWADDR], 0, 0);
+
+ if (route.rt_mask != INADDR_ANY && route.rt_gateway == INADDR_ANY)
+ {
+ /*
+ * Subnet route.
+ * Only one network can go through the interface so we remove the
netmask from the interface.
+ */
+
+ /* We remove the netmask only if it matches the given one */
+ if (ipv4_addrs[NETMASK] != INADDR_ANY
+ && ipv4_addrs[NETMASK] != INADDR_NONE
+ && ipv4_addrs[NETMASK] != route.rt_mask)
+ return EINVAL;
+
+ ipv4_addrs[NETMASK] = INADDR_NONE;
+ }
+ else if (route.rt_gateway != INADDR_ANY)
+ {
+ /*
+ * Netmask is any, and we got a gateway so client is trying to remove a
default route.
+ * We remove the gateway from the given interface.
+ */
+
+ /* We remove the gateway only if it matches the given one */
+ if (ipv4_addrs[GWADDR] != INADDR_ANY
+ && ipv4_addrs[GWADDR] != INADDR_NONE
+ && ipv4_addrs[GWADDR] != route.rt_gateway)
+ return EINVAL;
+
+ /* And only if it was the default one */
+ if (netif != netif_default)
+ return EINVAL;
+
+ ipv4_addrs[GWADDR] = INADDR_NONE;
+ }
+ else
+ {
+ /* Any other scenario not supported */
+ return EOPNOTSUPP;
+ }
+
+ err = configure_device (netif, ipv4_addrs[ADDR], ipv4_addrs[NETMASK],
+ ipv4_addrs[DSTADDR], ipv4_addrs[BRDADDR],
+ ipv4_addrs[GWADDR], 0, 0);
+
+ return err;
}
/* 12 SIOCSIFADDR -- Set address of a network interface. */
--
2.50.1