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 | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 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..637e7475 --- /dev/null +++ b/src/if-hurd.c @@ -0,0 +1,376 @@ +/* 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 */ + 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_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) { + /* Used mostly for wlan. Not supported */ + errno = EOPNOTSUPP; + return -1; +} + +#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) { + /* 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
