// SPDX-License-Identifier: GPL-2.0-or-later // // rtnetlink, enough of it that getifaddrs(3) works. // // Why this exists rather than a patch to whatever program wanted it: asking // "what interfaces does this machine have, and what addresses do they carry" is // something every networked program does, and on Linux there is exactly one // answer -- a NETLINK_ROUTE socket. musl implements getifaddrs() over it, so a // libc call that looks like it should be a syscall is really a small protocol // conversation, and without this side of it every caller fails identically. // // It is a *responder*, not a socket family in the general sense. There is no // kernel-to-userspace event stream, no multicast groups, nothing subscribes to // link state. A request arrives, the whole answer is generated into the // socket's buffer, and reads drain it. That shape is chosen deliberately: // getifaddrs sends a dump request and reads until NLMSG_DONE, so generating the // answer at send time means a read never has to block and MSG_DONTWAIT -- which // musl passes -- is trivially honoured. // // The information is not this file's. Interfaces come from the netdev class // (include/dev/netdev.h) and addresses from lwIP's netifs, exactly as /proc/netif // reads them. Nothing here knows what an e1000 is. #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/mmu.h" #include "core/proc.h" #include "fs/fs.h" #include "core/spinlock.h" #include "core/sleeplock.h" #include "fs/file.h" #include "uapi/errno.h" #include "dev/device.h" #include "dev/netdev.h" #define _KERNEL_SOCKET_INTERNALS #include "net/socket.h" #include "lwip/netif.h" #include "lwip/ip4_addr.h" // Writing into a fixed buffer, with every append bounds-checked against the // same `cap`. A netlink dump is the kind of code where a length field and a // memmove disagree quietly, so there is one helper and everything goes through // it. struct nlbuf { char *p; uint len; uint cap; int full; }; static void * nl_take(struct nlbuf *b, uint n) { if (b->full || b->len + n > b->cap) { b->full = 1; return 0; } void *at = b->p + b->len; memset(at, 0, n); b->len += n; return at; } // One attribute: header, payload, padding to a 4-byte boundary. The header's // length counts itself and the payload but *not* the padding -- getting that // backwards is the classic netlink bug, and it desynchronises the reader's walk // rather than failing outright. static void nl_attr(struct nlbuf *b, uint16 type, const void *val, uint vlen) { uint total = RTA_ALIGN(sizeof(struct rtattr) + vlen); struct rtattr *a = nl_take(b, total); if (!a) return; a->rta_type = type; a->rta_len = (uint16)(sizeof(struct rtattr) + vlen); memmove((char *)a + sizeof(struct rtattr), val, vlen); } // Start a message, returning where its length must be written once the // attributes are known. static struct nlmsghdr * nl_msg(struct nlbuf *b, uint16 type, uint32 seq, uint body) { struct nlmsghdr *h = nl_take(b, sizeof(*h) + NLMSG_ALIGN(body)); if (!h) return 0; h->nlmsg_type = type; h->nlmsg_flags = NLM_F_MULTI; h->nlmsg_seq = seq; h->nlmsg_pid = 0; return h; } static void nl_done(struct nlbuf *b, uint32 seq) { struct nlmsghdr *h = nl_take(b, sizeof(*h)); if (!h) return; h->nlmsg_len = sizeof(*h); h->nlmsg_type = NLMSG_DONE; h->nlmsg_seq = seq; } // Interface index. Linux numbers from 1 and reserves nothing, but getifaddrs // matches RTM_NEWADDR records to RTM_NEWLINK records *by index*, so the two // dumps below have to agree. Loopback is 1 and the netdevs follow. #define NL_LO_INDEX 1 #define NL_DEV_BASE 2 static void dump_links(struct nlbuf *b, uint32 seq) { // Loopback first, and it is not a netdev: it has no driver, no MAC and no // entry in the device model. It is a netif lwIP creates, and a program // enumerating interfaces still expects to see it. { struct nlmsghdr *h = nl_msg(b, RTM_NEWLINK, seq, sizeof(struct ifinfomsg)); if (!h) return; struct ifinfomsg *ifi = (struct ifinfomsg *)((char *)h + sizeof(*h)); ifi->ifi_family = 0; /* AF_UNSPEC */ ifi->ifi_type = ARPHRD_LOOPBACK; ifi->ifi_index = NL_LO_INDEX; ifi->ifi_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING; nl_attr(b, IFLA_IFNAME, "lo", 3); h->nlmsg_len = b->len - (uint)((char *)h - b->p); } for (int i = 0; i < netdev_count(); i++) { struct device *d = netdev_at(i); if (!d) continue; struct nlmsghdr *h = nl_msg(b, RTM_NEWLINK, seq, sizeof(struct ifinfomsg)); if (!h) return; struct ifinfomsg *ifi = (struct ifinfomsg *)((char *)h + sizeof(*h)); ifi->ifi_family = 0; ifi->ifi_type = ARPHRD_ETHER; ifi->ifi_index = NL_DEV_BASE + i; ifi->ifi_flags = IFF_BROADCAST | IFF_MULTICAST | (netdev_link_up(d) ? (IFF_UP | IFF_RUNNING) : 0); nl_attr(b, IFLA_IFNAME, d->name, (uint)strlen(d->name) + 1); const unsigned char *mac = netdev_mac(d); if (mac) nl_attr(b, IFLA_ADDRESS, mac, 6); h->nlmsg_len = b->len - (uint)((char *)h - b->p); } } // How many leading ones are in a netmask. getifaddrs does not use the prefix // length itself -- it reconstructs the netmask from it -- so an answer that // disagrees with the netmask is a wrong answer nobody notices until something // computes a broadcast address. static uchar prefix_of(uint32 mask_net_order) { uint32 m = ntohl_u32(mask_net_order); uchar n = 0; while (m & 0x80000000u) { n++; m <<= 1; } return n; } static void addr_msg(struct nlbuf *b, uint32 seq, int index, uint32 addr, uchar prefix, const char *label, int loopback) { struct nlmsghdr *h = nl_msg(b, RTM_NEWADDR, seq, sizeof(struct ifaddrmsg)); if (!h) return; struct ifaddrmsg *ifa = (struct ifaddrmsg *)((char *)h + sizeof(*h)); ifa->ifa_family = AF_INET; ifa->ifa_prefixlen = prefix; ifa->ifa_flags = 0; ifa->ifa_scope = loopback ? 254 : 0; /* RT_SCOPE_HOST : UNIVERSE */ ifa->ifa_index = (uint32)index; // IFA_ADDRESS and IFA_LOCAL are the same on anything that is not a // point-to-point link. musl reads whichever arrives and treats a second one // as the peer address, so sending both in this order is what makes it record // the address rather than a destination. nl_attr(b, IFA_ADDRESS, &addr, 4); nl_attr(b, IFA_LOCAL, &addr, 4); nl_attr(b, IFA_LABEL, label, (uint)strlen(label) + 1); h->nlmsg_len = b->len - (uint)((char *)h - b->p); } static void dump_addrs(struct nlbuf *b, uint32 seq) { addr_msg(b, seq, NL_LO_INDEX, htonl_u32(0x7F000001), 8, "lo", 1); for (int i = 0; i < netdev_count(); i++) { struct device *d = netdev_at(i); if (!d) continue; struct netif *nif = net_eth_netif(i); if (!nif) continue; uint32 a = netif_addr4(nif); if (a == 0) continue; /* no lease yet: not an address */ addr_msg(b, seq, NL_DEV_BASE + i, a, prefix_of(netif_mask4(nif)), d->name, 0); } } // A request has arrived. Generate the whole answer now; see the note at the top // about why this is not done lazily on read. // // Returns the number of bytes of `req` consumed, which is all of them: a // malformed request is answered with an empty dump rather than an error, // because the caller's next move is a read either way and an error message it // does not expect is worse than nothing to read. int netlink_request(const char *req, uint reqlen, char *out, uint outcap) { struct nlbuf b = { .p = out, .len = 0, .cap = outcap, .full = 0 }; if (reqlen < sizeof(struct nlmsghdr)) return 0; const struct nlmsghdr *h = (const struct nlmsghdr *)req; uint32 seq = h->nlmsg_seq; switch (h->nlmsg_type) { case RTM_GETLINK: dump_links(&b, seq); break; case RTM_GETADDR: dump_addrs(&b, seq); break; default: /* nothing to say about it */ break; } nl_done(&b, seq); // Truncation would hand the reader a half-written message and it would walk // off the end of it. Better to answer with only the terminator: an empty // interface list is a legible answer, a corrupt one is not. if (b.full) { b.len = 0; b.full = 0; nl_done(&b, seq); } return (int)b.len; }