From: Joan Lledó <[email protected]>

This adds a new main module to port dhcpdp to the Hurd.

* The Hurd module relies on libpcap to work with BPF filters.
* Addresses and routes are configured via ioctls to the stack.
* Only IPv4 over Ethernet is supported so far.
* Privilege separation and detection of network configuration changes is not 
implemented yet.
* libpcap and liblwip are required dependencies.
* Only 32-bit Hurd has been tested so far.
---
 src/if-hurd.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 395 insertions(+)
 create mode 100644 src/if-hurd.c

diff --git a/src/if-hurd.c b/src/if-hurd.c
new file mode 100644
index 00000000..89f320ea
--- /dev/null
+++ b/src/if-hurd.c
@@ -0,0 +1,395 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * libpcap interface driver for dhcpcd
+ * Copyright (c) 2025 Joan Lledó <[email protected]>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if_arp.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <hurd/pfinet.h>
+#include <device/device.h>
+
+#include "logerr.h"
+#include "if.h"
+#include "dhcpcd.h"
+
+#define ETH_HWADDR_LEN 6
+
+/* Get the MAC address from an array of int */
+#define GET_HWADDR_BYTE(x,n)  (((unsigned char*)x)[n])
+
+/*
+ * We should just use `struct ifrtreq` from net/route.h instead of defining 
this.
+ * Regrettably, dhcpcd defines some macros inside `struct rt` that conflict 
with
+ * `struct ifrtreq` fields, so we need to mimic `struct ifrtreq` here, but
+ * renaming the fields.
+ */
+struct kroute {
+  char ifname[IF_NAMESIZE];
+  in_addr_t dest;
+  in_addr_t mask;
+  in_addr_t gateway;
+  char padding[sizeof(int) * 7];
+};
+
+int
+if_getssid(__unused struct interface *ifp) {
+    /* Not supported. Returning -1 means interface is not wireless */
+    return -1;
+}
+
+bool
+if_roaming(__unused struct interface *ifp) {
+    /* Not supported */
+    return false;
+}
+
+static int
+if_copyrt(struct dhcpcd_ctx *ctx, struct rt *dest, struct kroute *src) {
+  struct interface *ifp;
+
+  ifp = if_find(ctx->ifaces, src->ifname);
+  if (ifp == NULL) {
+      logerr("%s: interface not found: %s", __func__, src->ifname);
+      return -1;
+  }
+
+  memset(dest, 0, sizeof(struct rt));
+
+  dest->rt_ifp = ifp;
+  dest->rt_ss_dest.sin.sin_addr.s_addr = src->dest;
+  dest->rt_ss_netmask.sin.sin_addr.s_addr = src->mask;
+  dest->rt_ss_gateway.sin.sin_addr.s_addr = src->gateway;
+
+  return 0;
+}
+
+static int
+if_copykrt(struct kroute *dst, const struct rt *src) {
+  in_addr_t mask, gateway, dest;
+
+  dest = src->rt_ss_dest.sin.sin_addr.s_addr;
+  mask = src->rt_ss_netmask.sin.sin_addr.s_addr;
+  gateway = src->rt_ss_gateway.sin.sin_addr.s_addr;
+
+  memset(dst, 0, sizeof(struct kroute));
+
+  strncpy(dst->ifname, src->rt_ifp->name, IF_NAMESIZE);
+  dst->dest = dest;
+  dst->mask = mask;
+  dst->gateway = gateway;
+
+  return 0;
+}
+
+/* Get all routes for existing interfaces and add them to th RB tree.
+ * Lwip implements limited routing, just one default gateway for each interface
+ */
+int
+if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *routes, int af) {
+  mach_port_t pfinet;
+  char socket_inet[32];
+  char *raw_data;
+  struct kroute *kroutes;
+  unsigned int i, len = 0, kroutes_count;
+  struct rt rt, *rtn = NULL;
+  error_t err;
+
+  snprintf(socket_inet, sizeof(socket_inet), _SERVERS_SOCKET "/%d", af);
+  pfinet = file_name_lookup (socket_inet, O_RDONLY, 0);
+  if (pfinet == MACH_PORT_NULL) {
+      logerr("%s: cannot open file: %s", __func__, socket_inet);
+      return -1;
+  }
+
+  err = pfinet_getroutes (pfinet, (vm_size_t)-1, &raw_data, &len);
+  if (err) {
+      errno = err;
+      logerr("%s: cannot get routes from stack", __func__);
+      goto errout;
+  }
+
+  kroutes = (struct kroute *)raw_data;
+  kroutes_count = len / sizeof(struct kroute);
+
+  for (i = 0; i < kroutes_count; i++) {
+    if (if_copyrt(ctx, &rt, &kroutes[i]) != 0)
+      goto errout;
+
+    if ((rtn = rt_new(rt.rt_ifp)) == NULL) {
+      goto errout;
+    }
+
+    memcpy(rtn, &rt, sizeof(*rtn));
+
+    if (rb_tree_insert_node(routes, rtn) != rtn)
+      rt_free(rtn);
+  }
+
+  mach_port_deallocate (mach_task_self (), pfinet);
+  vm_deallocate (mach_task_self(), (vm_address_t)raw_data, len);
+
+  return 0;
+
+errout:
+  mach_port_deallocate (mach_task_self (), pfinet);
+
+  if (raw_data)
+    vm_deallocate (mach_task_self(), (vm_address_t)raw_data, len);
+
+  return -1;
+}
+
+int
+if_vimaster(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname) {
+    /* TODO: Return 1 when the interface is created by eth-multiplexer  */
+    return 0;
+}
+
+#ifdef INET
+int
+if_route(unsigned char cmd, const struct rt *rt) {
+  int err;
+  struct dhcpcd_ctx *ctx;
+  struct kroute krt;
+
+  if_copykrt(&krt, rt);
+
+  ctx = rt->rt_ifp->ctx;
+
+  err = if_ioctl(ctx,
+        cmd == RTM_DELETE ? SIOCDELRT : SIOCADDRT, &krt, sizeof(krt));
+
+  return err;
+}
+
+int
+if_address(unsigned char cmd, const struct ipv4_addr *ia) {
+    int err;
+    struct ifreq ifr;
+    struct sockaddr_in *sin;
+    struct dhcpcd_ctx *ctx;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ia->iface->name, sizeof(ifr.ifr_name));
+    sin = (struct sockaddr_in *)&ifr.ifr_addr;
+    sin->sin_family = AF_INET;
+    sin->sin_addr = ia->addr;
+    sin->sin_len = sizeof(struct sockaddr_in);
+
+    ctx = ia->iface->ctx;
+
+    err = if_ioctl(ctx,
+        cmd == RTM_DELADDR ? SIOCDIFADDR : SIOCSIFADDR, &ifr, sizeof(ifr));
+
+    return err;
+}
+
+int
+if_addrflags(__unused const struct interface *ifp, __unused const struct 
in_addr *addr,
+    __unused const char *alias) {
+    /* TODO */
+    return 0;
+}
+#endif
+
+#ifdef INET6
+int
+if_address6(__unused unsigned char cmd, __unused const struct ipv6_addr *ia) {
+    /* TODO: We could add custom IOCTLs for this */
+    return 0;
+}
+
+int
+if_getlifetime6(__unused struct ipv6_addr *addr) {
+    /* TODO: We could add custom IOCTLs fro this */
+    return 0;
+}
+
+int
+if_addrflags6(__unused const struct interface *ifp, __unused const struct 
in6_addr *addr,
+    __unused const char *alias) {
+    /* TODO: We could add custom IOCTLs for this */
+    return 0;
+}
+
+void
+if_setup_inet6(__unused const struct interface *ifp) {
+    /* TODO */
+}
+#endif
+
+#ifdef PRIVSEP
+ssize_t
+ps_root_os(__unused struct dhcpcd_ctx *ctx, __unused struct ps_msghdr *psm, 
__unused struct msghdr *msg,
+    __unused void **rdata, __unused size_t *rlen, __unused bool *free_data) {
+    /* TODO */
+    return 0;
+}
+#endif
+
+int
+os_init(void) {
+    /* Not needed for now */
+    return 0;
+}
+
+/*
+ * Initialize the given interface.
+ * For now, we assume the interface is ethernet and try to get its MAC address
+ */
+int
+if_init(struct interface *ifp) {
+    mach_port_t master;
+    kern_return_t kr;
+    mach_msg_type_number_t count = 2;
+    device_t mach_dev;
+    int net_address[2];
+
+    /* Nothing to do for loopback */
+    if (ifp->flags & IFF_LOOPBACK)
+        return 0;
+
+    ifp->hwtype = ARPHRD_ETHER;
+
+    /* Open the device. Try devnode first */
+    master = file_name_lookup(ifp->name, O_READ | O_WRITE, 0);
+
+    if (master != MACH_PORT_NULL)
+        kr = device_open(master, D_WRITE | D_READ, "eth", &mach_dev);
+    else {
+        /* If unsuccessful, try Mach device */
+        kr = get_privileged_ports(NULL, &master);
+
+        if (kr) {
+            logerrx("%s: cannot open device: %s", __func__, ifp->name);
+            return -1;
+        }
+
+        kr = device_open(master, D_READ | D_WRITE, ifp->name, &mach_dev);
+    }
+
+    mach_port_deallocate(mach_task_self(), master);
+
+    if (kr) {
+        logerrx("%s: cannot open device: %s", __func__, ifp->name);
+        return -1;
+    }
+
+    /* Get the MAC address */
+    kr = device_get_status (mach_dev, NET_ADDRESS, net_address, &count);
+    if (kr) {
+        logerrx("%s: cannot get device MAC address: %s", __func__, ifp->name);
+        goto eexit;
+    }
+
+    /* We expect 6 bytes. Don't allow partial or incomplete addresses */
+    if (count * sizeof (int) < ETH_HWADDR_LEN) {
+        logerrx("%s: insufficient MAC address data: %d bytes", __func__, count 
* sizeof(int));
+        goto eexit;
+    }
+
+    net_address[0] = (int)ntohl((uint32_t)net_address[0]);
+    net_address[1] = (int)ntohl((uint32_t)net_address[1]);
+
+    /* Set MAC hardware address */
+    ifp->hwaddr[0] = GET_HWADDR_BYTE(net_address, 0);
+    ifp->hwaddr[1] = GET_HWADDR_BYTE(net_address, 1);
+    ifp->hwaddr[2] = GET_HWADDR_BYTE(net_address, 2);
+    ifp->hwaddr[3] = GET_HWADDR_BYTE(net_address, 3);
+    ifp->hwaddr[4] = GET_HWADDR_BYTE(net_address, 4);
+    ifp->hwaddr[5] = GET_HWADDR_BYTE(net_address, 5);
+    ifp->hwlen = ETH_HWADDR_LEN;
+
+    return 0;
+
+eexit:
+    device_close(mach_dev);
+    mach_port_deallocate(mach_task_self(), mach_dev);
+    return -1;
+}
+
+int
+if_opensockets_os(__unused struct dhcpcd_ctx *ctx) {
+    ctx->link_fd = -1;
+
+    return 0;
+}
+
+void
+if_closesockets_os(__unused struct dhcpcd_ctx *ctx) {
+    /* Nothing to do, as link_fd is not initialized */
+}
+
+int
+if_conf(__unused struct interface *ifp) {
+    /* Not needed for now */
+    return 0;
+}
+
+int
+if_handlelink(__unused struct dhcpcd_ctx *ctx) {
+    /* No needed if link_fd is not initialized */
+    return 0;
+}
+
+int
+if_setmac(__unused struct interface *ifp, __unused void *mac, __unused uint8_t 
maclen) {
+    /* TODO */
+    return 0;
+}
+
+unsigned short
+if_vlanid(__unused const struct interface *ifp) {
+    /* TODO */
+    return 0;
+}
+
+int
+if_carrier(__unused struct interface *ifp, __unused const void *ifadata) {
+    /* TODO */
+    return 0;
+}
+
+bool
+if_ignore(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname) {
+    /* TODO */
+    return 0;
+}
+
+int
+if_machinearch(__unused char *str, __unused size_t len) {
+    /* TODO */
+    return 0;
+}
+
+int
+if_applyra(__unused const struct ra *rap) {
+    /* TODO */
+    return 0;
+}
-- 
2.50.1


Reply via email to