summaryrefslogtreecommitdiff
path: root/lwip/lwip-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lwip/lwip-util.c')
-rw-r--r--lwip/lwip-util.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/lwip/lwip-util.c b/lwip/lwip-util.c
new file mode 100644
index 00000000..c65b093b
--- /dev/null
+++ b/lwip/lwip-util.c
@@ -0,0 +1,343 @@
+/*
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ Written by Joan Lledó.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Lwip management module */
+
+#include <lwip-util.h>
+
+#include <error.h>
+#include <net/if_arp.h>
+
+#include <lwip/sockets.h>
+#include <lwip/netifapi.h>
+
+#include <lwip-hurd.h>
+#include <options.h>
+#include <netif/hurdethif.h>
+#include <netif/hurdtunif.h>
+#include <netif/hurdloopif.h>
+
+/*
+ * Detect the proper module for the given device name
+ * and returns its init callback
+ */
+static error_t
+create_netif_state (char *name, struct ifcommon *ifc)
+{
+ char *base_name;
+
+ memset (ifc, 0, sizeof (struct ifcommon));
+
+ base_name = strrchr (name, '/');
+ if (base_name)
+ base_name++;
+ else
+ base_name = name;
+
+ if (strncmp (base_name, "tun", 3) == 0)
+ ifc->init = hurdtunif_device_init;
+ else
+ ifc->init = hurdethif_device_init;
+
+ /* Freed in the module terminate callback */
+ ifc->devname = strndup (name, strlen (name));
+
+ return errno;
+}
+
+/* Some checks for IPv4 configurations */
+static int
+ipv4config_is_valid (uint32_t addr, uint32_t netmask,
+ uint32_t gateway, uint32_t broadcast)
+{
+ /* Check whether the user provided a valid netmask */
+ if (netmask != INADDR_NONE && !ip4_addr_netmask_valid (netmask))
+ {
+ error (0, 0, "Error: Invalid network mask.\n");
+ return 0;
+ }
+
+ /* The given gateway, if any, must be in the same network as the address */
+ if (gateway != INADDR_NONE && (gateway & netmask) != (addr & netmask))
+ {
+ error (0, 0,
+ "Error: the gateway is not in the same network as the address.\n");
+ return 0;
+ }
+
+ /*
+ * LwIP doesn't allow setting the broadcast address.
+ * We must ensure the given broadcast address is the default one for this
+ * network.
+ */
+ if (broadcast != INADDR_NONE
+ && netmask != INADDR_NONE && broadcast != (addr | ~netmask))
+ {
+ error (0, 0,
+ "Error: the broadcast address doesn't match the network mask.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Configure the loopback interface */
+static void
+init_loopback ()
+{
+ struct ifcommon ifc;
+
+ memset (&ifc, 0, sizeof (struct ifcommon));
+ ifc.init = hurdloopif_device_init;
+ netif_list->state = &ifc;
+
+ if_init (netif_list);
+}
+
+/* Remove the existing interfaces, but the loopback one */
+void
+remove_ifs ()
+{
+ struct netif *netif;
+
+ netif = netif_list;
+ while (netif != 0)
+ {
+ /* Skip the loopback interface */
+ if (netif_get_state (netif)->type == ARPHRD_LOOPBACK)
+ {
+ netif = netif->next;
+ continue;
+ }
+ if_terminate (netif);
+ netifapi_netif_remove (netif);
+ free (netif);
+
+ netif = netif_list;
+ }
+
+ return;
+}
+
+/* Initialize the interfaces given by the user through command line */
+void
+init_ifs (void *arg)
+{
+ error_t err;
+ struct parse_interface *in;
+ struct parse_hook *ifs;
+ struct netif *netif;
+ struct ifcommon ifc;
+ int8_t ipv6_addr_idx;
+ ip6_addr_t *address6;
+ int i;
+
+ if (netif_list != 0)
+ {
+ if (netif_list->next == 0)
+ init_loopback ();
+ else
+ remove_ifs ();
+ }
+
+ /*
+ * Go through the list backwards. For LwIP
+ * to create its list in the proper order.
+ */
+ ifs = (struct parse_hook *) arg;
+ for (in = ifs->interfaces + ifs->num_interfaces - 1;
+ in >= ifs->interfaces; in--)
+ {
+ /* The interface hasn't been completely configured */
+ if (!in->dev_name[0])
+ continue;
+
+ if (!ipv4config_is_valid (in->address.addr, in->netmask.addr,
+ in->gateway.addr, INADDR_NONE))
+ continue;
+
+ netif = calloc (1, sizeof (struct netif));
+
+ create_netif_state (in->dev_name, &ifc);
+
+ /*
+ * Create a new interface and configre IPv4.
+ *
+ * Fifth parameter (in->name) is a hook.
+ */
+ err = netifapi_netif_add
+ (netif, &in->address, &in->netmask, &in->gateway, &ifc, if_init,
+ tcpip_input);
+ if (err)
+ {
+ /* The interface failed to init */
+ if (netif->state != in->dev_name)
+ /* It failed after setting the control block, must free it */
+ mem_free (netif->state);
+ free (netif);
+ continue;
+ }
+
+ /* Add IPv6 configuration */
+ netif->ip6_autoconfig_enabled = 1;
+ netif_create_ip6_linklocal_address (netif, 1);
+
+ /* Add user given unicast addresses */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+ {
+ address6 = (ip6_addr_t *) & in->addr6[i];
+
+ if (!ip6_addr_isany (address6) && !ip6_addr_ismulticast (address6))
+ {
+ netif_add_ip6_address (netif, address6, &ipv6_addr_idx);
+
+ if (ipv6_addr_idx >= 0)
+ /* First use DAD to make sure nobody else has it */
+ netif_ip6_addr_set_state (netif, ipv6_addr_idx,
+ IP6_ADDR_TENTATIVE);
+ else
+ error (0, 0, "No free slot for IPv6 address: %s\n",
+ ip6addr_ntoa (address6));
+ }
+ }
+
+ /* Up the inerface */
+ netifapi_netif_set_up (netif);
+
+ /* Set the first interface with valid gateway as default */
+ if (in->gateway.addr != INADDR_NONE)
+ {
+ netifapi_netif_set_default (netif);
+ }
+ }
+
+ /* Free the hook */
+ free (ifs->interfaces);
+ free (ifs);
+
+ return;
+}
+
+/*
+ * Change the IP configuration of an interface
+ */
+static error_t
+update_if (struct netif *netif, uint32_t addr, uint32_t netmask,
+ uint32_t peer, uint32_t broadcast, uint32_t gateway,
+ uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+ error_t err;
+ int i;
+
+ err = 0;
+
+ netifapi_netif_set_addr (netif, (ip4_addr_t *) & addr,
+ (ip4_addr_t *) & netmask,
+ (ip4_addr_t *) & gateway);
+
+ if (addr6)
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+ {
+ ip6_addr_t *laddr6 = ((ip6_addr_t *) addr6 + i);
+ if (!ip6_addr_isany (laddr6))
+ {
+ netif_ip6_addr_set (netif, i, laddr6);
+
+ if (!ip6_addr_islinklocal (laddr6))
+ netif_ip6_addr_set_state (netif, i, IP6_ADDR_TENTATIVE);
+ }
+ }
+
+ if (addr6_prefix_len)
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+ *(addr6_prefix_len + i) = 64;
+
+ return err;
+}
+
+/* Get the IP configuration of an interface */
+void
+inquire_device (struct netif *netif, uint32_t * addr, uint32_t * netmask,
+ uint32_t * peer, uint32_t * broadcast, uint32_t * gateway,
+ uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+ int i;
+
+ if (netif)
+ {
+ if (addr)
+ *addr = netif_ip4_addr (netif)->addr;
+
+ if (netmask)
+ *netmask = netif_ip4_netmask (netif)->addr;
+
+ if (peer)
+ *peer = INADDR_NONE;
+
+ if (broadcast)
+ *broadcast =
+ netif_ip4_addr (netif)->addr | ~netif_ip4_netmask (netif)->addr;
+
+ if (gateway)
+ *gateway = netif_ip4_gw (netif)->addr;
+
+ if (addr6)
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+ {
+ *(addr6 + i * 4 + 0) = netif_ip6_addr (netif, i)->addr[0];
+ *(addr6 + i * 4 + 1) = netif_ip6_addr (netif, i)->addr[1];
+ *(addr6 + i * 4 + 2) = netif_ip6_addr (netif, i)->addr[2];
+ *(addr6 + i * 4 + 3) = netif_ip6_addr (netif, i)->addr[3];
+ }
+
+ if (addr6_prefix_len)
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++)
+ *(addr6_prefix_len + i) = 64;
+ }
+}
+
+/*
+ * Check and change the IP configuration of an interface.
+ * Called from ioctls.
+ */
+error_t
+configure_device (struct netif *netif, uint32_t addr, uint32_t netmask,
+ uint32_t peer, uint32_t broadcast, uint32_t gateway,
+ uint32_t * addr6, uint8_t * addr6_prefix_len)
+{
+ error_t err = 0;
+
+ if (netmask != INADDR_NONE)
+ /*
+ * If broadcasting is enabled and we have a netmask lesser than 31 bits
+ * long, we need to update the broadcast address too.
+ */
+ if ((netif->flags & NETIF_FLAG_BROADCAST)
+ && ip4_addr_netmask_valid (netmask) && netmask <= 0xfffffffc)
+ broadcast = (addr | ~netmask);
+
+ if (!ipv4config_is_valid (addr, netmask, gateway, broadcast))
+ err = EINVAL;
+ else
+ err = update_if (netif, addr, netmask, peer, broadcast,
+ gateway, addr6, addr6_prefix_len);
+
+ return err;
+}