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
