Sorry for top posting, my client makes it near impossible to do otherwise.


> It doesn't yet, netdde is based on Linux. rumpnet (on its way) will be based 
>on BSD.



 From userland, should I be able to tell the difference when rumpnet lands with 
regards to what ioctls are supported

and how to obtain OS specific stuff like interface carrier state?


Thanks


Roy








From: Samuel Thibault <[email protected]>
To: "Roy Marples"<[email protected]>
Cc: "martin-ericracine"<[email protected]>, ""Joan 
Lledó""<[email protected]>, "bug-hurd"<[email protected]>
Date: Tue, 02 Jun 2026 12:13:59 +0100
Subject: Re: Fwd: [PATCH 4/6] New GNU/Hurd backend



Roy Marples, le mar. 02 juin 2026 09:14:42 +0100, a ecrit: 
> Knowing that Hurd uses NetBSD drivers 
 
It doesn't yet, netdde is based on Linux. rumpnet (on its way) will be based on 
BSD. 
 
Samuel 
 
> and knowing I'm very intimate with the 
> NetBSD network stack, how true is it that IFF_RUNNING reflects carrier state? 
> 
> On BSD, carrier state is part of if_data - if_link_state. 
> Every driver in every BSD supports this even if LINK_STATE_UNKNOWN. 
> 
> Some drivers (but not all) also report link state via MEDIA. 
> MacOS deviates from BSD here and does not have if_link_state and soely relies 
> on MEDIA. 
> 
> On every BSD IFF_RUNNING means that kernel resources have been allocated (ie 
> tx 
> /rx rings setup etc etc). 
> On Linux IFF_RUNNING means yes we have a carrier. 
> 
> Roy 
> 
> 
> 
> From: Martin-Éric Racine < mailto:[email protected] > 
> To: "Roy Marples"< mailto:[email protected] > 
> Date: Sun, 03 May 2026 09:31:19 +0100 
> Subject: Fwd: [PATCH 4/6] New GNU/Hurd backend 
> 
> 
>     ---------- Forwarded message --------- 
>     Lähettäjä: Joan Lledó <[1] mailto:[email protected] > 
>     Date: ma 6.4.2026 klo 16.00 
>     Subject: [PATCH 4/6] New GNU/Hurd backend 
>     To: <[2] mailto:[email protected] > 
>     Cc: <[3] mailto:[email protected] >, Joan Lledó <[4] 
> mailto:[email protected] > 
> 
> 
>     From: Joan Lledó <[5] mailto:[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 | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++ 
>     1 file changed, 448 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..00482af8 
>     --- /dev/null 
>     +++ b/src/if-hurd.c 
>     @@ -0,0 +1,448 @@ 
>     +/* SPDX-License-Identifier: BSD-2-Clause */ 
>     +/* 
>     + * libpcap interface driver for dhcpcd 
>     + * Copyright (c) 2025 Joan Lledó <[6] mailto:[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; 
>     + int flags; 
>     + int mtu; 
>     + int metric; 
>     + char padding[sizeof(int) * 4]; 
>     +}; 
>     + 
>     +int 
>     +if_getssid(__unused struct interface *ifp) { 
>     + /* Not supported. Returning -1 means interface is not wireless */ 
>     + errno = EOPNOTSUPP; 
>     + return -1; 
>     +} 
>     + 
>     +bool 
>     +if_roaming(__unused struct interface *ifp) { 
>     + /* Not supported */ 
>     + errno = EOPNOTSUPP; 
>     + 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_family = AF_INET; 
>     + dest->rt_ss_dest.sin.sin_len = sizeof(struct sockaddr_in); 
>     + dest->rt_ss_dest.sin.sin_addr.s_addr = src->dest; 
>     + dest->rt_ss_netmask.sin.sin_family = AF_INET; 
>     + dest->rt_ss_netmask.sin.sin_len = sizeof(struct sockaddr_in); 
>     + dest->rt_ss_netmask.sin.sin_addr.s_addr = src->mask; 
>     + dest->rt_ss_gateway.sin.sin_family = AF_INET; 
>     + dest->rt_ss_gateway.sin.sin_len = sizeof(struct sockaddr_in); 
>     + dest->rt_ss_gateway.sin.sin_addr.s_addr = src->gateway; 
>     + dest->rt_flags = (unsigned int)src->flags; 
>     + dest->rt_mtu = (unsigned int)src->mtu; 
>     +#ifdef HAVE_ROUTE_METRIC 
>     + dest->rt_metric = (unsigned int)src->metric; 
>     +#endif 
>     + 
>     + return 0; 
>     +} 
>     + 
>     +static int 
>     +if_copykrt(struct kroute *dst, const struct rt *src) { 
>     + memset(dst, 0, sizeof(struct kroute)); 
>     + 
>     + strncpy(dst->ifname, src->rt_ifp->name, IF_NAMESIZE); 
>     + dst->dest = src->rt_ss_dest.sin.sin_addr.s_addr;; 
>     + dst->mask = src->rt_ss_netmask.sin.sin_addr.s_addr; 
>     + dst->gateway = src->rt_ss_gateway.sin.sin_addr.s_addr; 
>     + dst->flags = (int)src->rt_flags; 
>     + dst->mtu = (int)src->rt_mtu; 
>     +#ifdef HAVE_ROUTE_METRIC 
>     + dst->metric = (int)src->rt_metric; 
>     +#endif 
>     + 
>     + /* Set flags. Required by pfinet, ignored by lwip */ 
>     + 
>     + /* All dhcp routes must be marked as static */ 
>     + dst->flags |= RTF_STATIC; 
>     + 
>     + /* Flag route type */ 
>     + if (dst->mask == INADDR_NONE) 
>     + /* All ones netmask means host route */ 
>     + dst->flags |= RTF_HOST; 
>     + else if (dst->mask != INADDR_ANY) 
>     + /* Subnet route */ 
>     + dst->flags &= ~RTF_HOST; 
>     + else if (dst->gateway != INADDR_ANY) 
>     + /* Netmask is any, and we got a gateway so default route */ 
>     + dst->flags |= RTF_GATEWAY; 
>     + 
>     + return 0; 
>     +} 
>     + 
>     +/* Get all routes for existing interfaces and add them to th RB tree */ 
>     +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) 
>     { 
>     + /* Used mostly for wlan. Not supported */ 
>     + errno = EOPNOTSUPP; 
>     + return -1; 
>     +} 
>     + 
>     +#ifdef INET 
>     +int 
>     +if_route(unsigned char cmd, const struct rt *rt) { 
>     + struct dhcpcd_ctx *ctx; 
>     + struct kroute krt; 
>     + 
>     + if (cmd == RTM_CHANGE) { 
>     + /* 
>     + * Asked to change route, the hurd doesn't provide a way to do this, 
>     + * available ioctls only allow adding and deleting. 
>     + * ESRCH is the expected error in this scenario. 
>     + */ 
>     + errno = ESRCH; 
>     + return -1; 
>     + } 
>     + 
>     + ctx = rt->rt_ifp->ctx; 
>     + 
>     + if_copykrt(&krt, rt); 
>     + return if_ioctl(ctx, cmd == RTM_DELETE ? SIOCDELRT : SIOCADDRT, 
>     &krt, sizeof(krt)); 
>     +} 
>     + 
>     +static int 
>     +if_ioctl_addr(struct dhcpcd_ctx *ctx, const char *ifname, 
>     + const struct in_addr *addr, ioctl_request_t cmd) { 
>     + struct ifreq ifr; 
>     + struct sockaddr_in *sin; 
>     + 
>     + memset(&ifr, 0, sizeof(ifr)); 
>     + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 
>     + sin = (struct sockaddr_in *)&ifr.ifr_addr; 
>     + sin->sin_family = AF_INET; 
>     + sin->sin_addr = *addr; 
>     + sin->sin_len = sizeof(struct sockaddr_in); 
>     + 
>     + return if_ioctl(ctx, cmd, &ifr, sizeof(ifr)); 
>     +} 
>     + 
>     +int 
>     +if_address(unsigned char cmd, const struct ipv4_addr *ia) { 
>     + int err; 
>     + struct in_addr addr; 
>     + 
>     + if (cmd == RTM_DELADDR) { 
>     + addr.s_addr = ia->addr.s_addr; 
>     + err = if_ioctl_addr(ia->iface->ctx, ia->iface->name, &addr, 
> SIOCDIFADDR); 
>     + if (err) { 
>     + logerr("%s: cannot delete address", __func__); 
>     + return -1; 
>     + } 
>     + 
>     + return 0; 
>     + } 
>     + 
>     + addr.s_addr = ia->addr.s_addr; 
>     + err = if_ioctl_addr(ia->iface->ctx, ia->iface->name, &addr, 
> SIOCSIFADDR); 
>     + if (err) { 
>     + logerr("%s: cannot set address", __func__); 
>     + return -1; 
>     + } 
>     + 
>     + addr.s_addr = ia->mask.s_addr; 
>     + err = if_ioctl_addr(ia->iface->ctx, ia->iface->name, &addr, 
>     SIOCSIFNETMASK); 
>     + if (err) { 
>     + logerr("%s: cannot set network mask", __func__); 
>     + return -1; 
>     + } 
>     + 
>     + addr.s_addr = ia->brd.s_addr; 
>     + err = if_ioctl_addr(ia->iface->ctx, ia->iface->name, &addr, 
>     SIOCSIFBRDADDR); 
>     + if (err && errno != ENOTTY) { 
>     + /* We have to accept ENOTTY as a success state here because lwip 
> doesn't 
>     + * accept setting a broadcast address, it's a computed value. 
>     + * About pfinet, we expect to get a ENOTTY only when it doesn't get an 
>     + * authorized user. Not possible here because in that case previous 
> calls 
>     + * would fail and the code wouldn't reach here. 
>     + */ 
>     + logerr("%s: cannot set broadcast address", __func__); 
>     + return -1; 
>     + } 
>     + 
>     + return 0; 
>     +} 
>     + 
>     +int 
>     +if_addrflags(__unused const struct interface *ifp, __unused const 
>     struct in_addr *addr, 
>     + __unused const char *alias) { 
>     + /* Not supported. Set flags to 0 */ 
>     + return 0; 
>     +} 
>     +#endif 
>     + 
>     +#ifdef INET6 
>     +int 
>     +if_address6(__unused unsigned char cmd, __unused const struct ipv6_addr 
>     *ia) { 
>     + /* TODO */ 
>     + return -1; 
>     +} 
>     + 
>     +int 
>     +if_getlifetime6(__unused struct ipv6_addr *addr) { 
>     + /* TODO */ 
>     + return -1; 
>     +} 
>     + 
>     +int 
>     +if_addrflags6(__unused const struct interface *ifp, __unused const 
>     struct in6_addr *addr, 
>     + __unused const char *alias) { 
>     + /* TODO */ 
>     + return -1; 
>     +} 
>     + 
>     +void 
>     +if_setup_inet6(__unused const struct interface *ifp) { 
>     + /* TODO */ 
>     +} 
>     + 
>     +int 
>     +if_applyra(__unused const struct ra *rap) { 
>     + /* TODO */ 
>     + return -1; 
>     +} 
>     +#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 -1; 
>     +} 
>     +#endif 
>     + 
>     +int 
>     +os_init(void) { 
>     + /* Not needed for now */ 
>     + return 0; 
>     +} 
>     + 
>     +/* 
>     + * Initialize the given interface. 
>     + * Try to get its MAC address and index 
>     + */ 
>     +int 
>     +if_init(struct interface *ifp) { 
>     + struct ifreq ifr; 
>     + 
>     + memset(&ifr, 0, sizeof(ifr)); 
>     + strlcpy(ifr.ifr_name, ifp->name, IFNAMSIZ); 
>     + 
>     + /* Don't try to get HW address for loopback interfaces */ 
>     + if ((ifp->flags & IFF_LOOPBACK) == 0) { 
>     + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFHWADDR, &ifr) == -1) { 
>     + logerr("%s: SIOCGIFHWADDR", ifp->name); 
>     + return -1; 
>     + } 
>     + memcpy(ifp->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_HWADDR_LEN); 
>     + ifp->hwtype = ifr.ifr_hwaddr.sa_family; 
>     + ifp->hwlen = ifr.ifr_hwaddr.sa_len; 
>     + } 
>     + 
>     + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFINDEX, &ifr) == -1) { 
>     + logerr("%s: SIOCGIFINDEX", ifp->name); 
>     + return -1; 
>     + } 
>     + ifp->index = (unsigned int)ifr.ifr_ifindex; 
>     + 
>     + return 0; 
>     +} 
>     + 
>     +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) { 
>     + errno = EOPNOTSUPP; 
>     + return -1; 
>     +} 
>     + 
>     +unsigned short 
>     +if_vlanid(__unused const struct interface *ifp) { 
>     + /* We don't support vlans for now */ 
>     + return 0; 
>     +} 
>     + 
>     +int 
>     +if_carrier(struct interface *ifp, __unused const void *ifadata) { 
>     + struct ifreq ifr; 
>     + 
>     + memset(&ifr, 0, sizeof(ifr)); 
>     + strlcpy(ifr.ifr_name, ifp->name, IFNAMSIZ); 
>     + 
>     + if (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1) { 
>     + logerr("%s: SIOCGIFFLAGS", ifp->name); 
>     + return LINK_UNKNOWN; 
>     + } 
>     + 
>     + if (ifr.ifr_flags & IFF_RUNNING) 
>     + return LINK_UP; 
>     + 
>     + return LINK_DOWN; 
>     +} 
>     + 
>     +bool 
>     +if_ignore(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname) 
> { 
>     + /* 
>     + * Return `true` for interfaces we want dhcpcd to ignore. 
>     + * 
>     + * Other OSs use this to ignore taps, bridges, and other kinds of 
>     + * virtual interfaces. 
>     + * 
>     + * We don't use this for now. 
>     + */ 
>     + return false; 
>     +} 
>     + 
>     +int 
>     +if_machinearch(__unused char *str, __unused size_t len) { 
>     + /* 
>     + * dhcpcd already has uname info. 
>     + * We don't need to add extra architecture info for now. 
>     + */ 
>     + return 0; 
>     +} 
>     -- 
>     2.50.1

Reply via email to