Hi,

On 10/13/25 06:50, Amos Jeffries wrote:
On 13/10/2025 05:00, Joan Lledó wrote:
From: Joan Lledó

* 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.

Might be better to go the other way and "only" support IPv6.

IPv4 addresses are a subset of IPv6 address space, and both can be passed around as generic struct sockddr_storage objects or struct sockaddr* pointers. Any code that needs more detail than "its an IP" can use their *_family member to select logic paths.


Yeah, it was just a decision to make it easier for me, like "I'll worry about IPv6 later on". I thought IPv6 would need more time to investigate and decided to stick to IPv4 which I'm more familiar with.

+#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;
+}

It is not clear why the above is IPv4-specific. Seems to me the logic is shared by all IP versions (past, present, or future).


Yeah, I think this particular function is valid for IPv6 and probably won't need any change when I add support for IPv6.

+
+int
+if_address(unsigned char cmd, const struct ipv4_addr *ia) {

Is "ipv4_addr" actually needed?
 anything it provides that the standrd in_addr or sockaddr_in types do not?

Perhape consider making this a struct sockaddr_storage or struct addrinfo parameter. The logic of this function can be IP-version agnostic if care is taken regarding the memory allocations.


This is defined by dhcpcd and platforms are supposed to overwrite it if needed:

https://github.com/NetworkConfiguration/dhcpcd/blob/a8cdc28cc96f799a3c5c3b4f0b858ebeb2c5d97d/src/if.h#L260

So we can't change the function signature.

+    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 */


FWIW, It may be better to refactor the existing IOCTLs to handle struct ifreq with non-IPv4 sin_family.

The struct ifreq is defined with sockaddr members. In modern IP code "struct sockaddr" should be treated as a way to access the *_family member of a struct sockaddr_storage, sockaddr_in, or sockaddr_in6 to determine which one the rest of the 'char data[]' represents.


Be aware that all IP-type interfaces can nowdays be assigned multiple IPs. IPv4 used to only allow one address, but has been extended for compatibility with IPv6.

For example;

2: enp2s0: ...
     inet 192.168.1.7/24 brd 192.168.1.255 scope global dynamic
     inet 10.0.1.7/24 brd 10.0.1.255 scope global dynamic
     inet6 fd58:bad4:.../64 scope global dynamic
     inet6 fe80::.../64 scope link

(note 2x IPv4 and 2x IPv6)

Of course, I'm open to suggestions on how to proceed.


+/*
+ * 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]);


I expect this will have issues on 64-bit build where "int" is more than 32-bits long.

I think in that case the whole MAC would fit in one integer and the second integer would be unused, but I don't think it would fail.

Also, modern networking has EUI-64 that may occur in place of the EUI-48 (aka "MAC" or "L2" address). It would be good to check for such instead of assuming EUI-48/MAC is always given.

This is not supported by lwip which assumes 48bit MACs


+
+    /* 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;
+}
+



Thanks for your comments!



Reply via email to