From: David van Moolenbroek Date: Fri, 17 Jun 2016 14:03:27 +0000 (+0000) Subject: INET/LWIP: minimal net.route sysctl support X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=refs%2Fchanges%2F27%2F3327%2F1;p=minix.git INET/LWIP: minimal net.route sysctl support At a point not too far in the future, we will be switching from the hardcoded MINIX3 implementation of the getifaddrs(3) libc routine to the proper NetBSD implementation. The latter uses the net.route.rtable sysctl functionality to obtain its information. In order make the transition as painless as possible, this patch adds basic support for that net.route.rtable functionality to INET and LWIP, using the remote MIB (RMIB) facility. Change-Id: I54f5cea7985f6606e317c73a5e6be3a5d07bc7dc --- diff --git a/minix/net/inet/Makefile b/minix/net/inet/Makefile index 0b2ced881..85a47f5a1 100644 --- a/minix/net/inet/Makefile +++ b/minix/net/inet/Makefile @@ -8,7 +8,7 @@ SRCS= buf.c clock.c inet.c inet_config.c \ ipr.c rand256.c tcp.c tcp_lib.c \ tcp_recv.c tcp_send.c ip_eth.c \ ip_ps.c psip.c \ - queryparam.c version.c + queryparam.c version.c rtinfo.c MAN= inet.8 FILES=${PROG}.conf diff --git a/minix/net/inet/inet.c b/minix/net/inet/inet.c index 76df5b5b4..fb316673c 100644 --- a/minix/net/inet/inet.c +++ b/minix/net/inet/inet.c @@ -12,6 +12,7 @@ Copyright 1995 Philip Homburg #include #include #include +#include #include #include @@ -120,6 +121,10 @@ int main(int argc, char *argv[]) mess.m_source); } } + else if (mess.m_source == MIB_PROC_NR) + { + rmib_process(&mess, ipc_status); + } else if (mess.m_type == DL_CONF_REPLY || mess.m_type == DL_TASK_REPLY || mess.m_type == DL_STAT_REPLY) @@ -242,6 +247,9 @@ static int sef_cb_init_fresh(int type, sef_init_info_t *info) */ chardriver_announce(); + /* Register net.route RMIB subtree with the MIB service. */ + rtinfo_init(); + return(OK); } diff --git a/minix/net/inet/proto.h b/minix/net/inet/proto.h index 394ddacf8..17decabf8 100644 --- a/minix/net/inet/proto.h +++ b/minix/net/inet/proto.h @@ -15,6 +15,10 @@ void clck_tick(message *mess); void eth_rec(message *m); void eth_check_driver(char *label, endpoint_t endpoint); +/* rtinfo.c */ + +void rtinfo_init(void); + /* sr.c */ void sr_rec(message *m, int ipc_status); diff --git a/minix/net/inet/rtinfo.c b/minix/net/inet/rtinfo.c new file mode 100644 index 000000000..62f3880b0 --- /dev/null +++ b/minix/net/inet/rtinfo.c @@ -0,0 +1,461 @@ +/* + * Mock net.route sysctl(2) subtree implementation using RMIB. This code + * serves as a temporary bridge to allow libc to switch from the original, + * native MINIX3 getifaddrs(3) to the NetBSD getifaddrs(3). As such, it + * implements only a small subset of NetBSD's full net.route functionality, + * although also more than needed only to imitate the MINIX3 getifaddrs(3). + */ + +#include "inet.h" +#include "generic/type.h" +#include "generic/buf.h" +#include "generic/event.h" +#include "generic/ip_int.h" +#include "osdep_eth.h" +#include "generic/eth_int.h" +#include +#include +#include +#include +#include +#include +#include + +/* Max. number of bytes for a full sockaddr_dl structure, including data. */ +#define SDL_BUFSIZE (sizeof(struct sockaddr_dl) + 32) + +static const char padbuf[RT_ROUNDUP(0)] = { 0 }; + +/* + * Copy out a vector of data to the sysctl(2) caller. TODO: decide what to do + * with this. We could implement this as a vectored-safecopy operation in + * RMIB. We could also copy everything into a single buffer first. The + * current implementation is probably the worst among the possibilities. + */ +static ssize_t +rmib_vcopyout(struct rmib_oldp * oldp, size_t off, const iovec_t * iov, + unsigned int iovcnt) +{ + unsigned int i; + ssize_t r, len; + + len = 0; + + for (i = 0; i < iovcnt; i++) { + if ((r = rmib_copyout(oldp, off + len, + (const void *)iov[i].iov_addr, iov[i].iov_size)) < 0) + return r; + + len += r; + } + + return len; +} + +/* + * Compute the length for, and possibly copy out, an interface information or + * interface address record with an associated set of zero or more routing + * table addresses. The addresses are padded as necessary. Store the full + * record length and the address bitmap before copying out the entire record. + */ +static ssize_t +copyout_rta(void * hdr, size_t size, u_short * msglen, int * addrs, + void * rta_map[RTAX_MAX], size_t rta_len[RTAX_MAX], + struct rmib_oldp * oldp, ssize_t off) +{ + iovec_t iov[1 + RTAX_MAX * 2]; + size_t len, total, padlen; + unsigned int i, iovcnt; + int mask; + + iovcnt = 0; + iov[iovcnt].iov_addr = (vir_bytes)hdr; + iov[iovcnt++].iov_size = size; + + total = size; + mask = 0; + + /* + * Any addresses in the given map should be stored in the numbering + * order of the map. + */ + for (i = 0; i < RTAX_MAX; i++) { + if (rta_map[i] == NULL) + continue; + + assert(iovcnt < __arraycount(iov)); + iov[iovcnt].iov_addr = (vir_bytes)rta_map[i]; + iov[iovcnt++].iov_size = len = rta_len[i]; + + padlen = RT_ROUNDUP(len) - len; + if (padlen > 0) { + assert(iovcnt < __arraycount(iov)); + iov[iovcnt].iov_addr = (vir_bytes)padbuf; + iov[iovcnt++].iov_size = padlen; + } + + total += len + padlen; + mask |= (1 << i); + } + + /* If only the length was requested, return it now. */ + if (oldp == NULL) + return total; + + /* + * Casting 'hdr' would violate C99 strict aliasing rules, so store the + * computed header values through direct pointers. Bah. + */ + *msglen = total; + *addrs = mask; + + return rmib_vcopyout(oldp, off, iov, iovcnt); +} + +/* + * Given an INET ip-layer datalink type, return a matching BSD interface type. + */ +static unsigned char +ipdl_to_ift(int ip_dl_type) +{ + + switch (ip_dl_type) { + case IPDL_ETH: + return IFT_ETHER; + case IPDL_PSIP: + return IFT_LOOP; + default: + return IFT_OTHER; + } +} + +/* + * Compute the length for, and possibly generate, a sockaddr_dl structure for + * the given interface. The complication here is that the structure contains + * various field packed together dynamically, making it variable sized. + */ +static size_t +make_sdl(const ip_port_t * ip_port, int ndx, char * buf, size_t max) +{ + const eth_port_t *eth_port; + struct sockaddr_dl sdl; + static char namebuf[8]; + const void *addrptr; + size_t hdrlen, namelen, addrlen, padlen, len; + + /* Normally the interface name would be pregenerated somewhere. */ + snprintf(namebuf, sizeof(namebuf), "ip%u", ip_port->ip_port); + namelen = strlen(namebuf); + + addrlen = 0; + if (ip_port->ip_dl_type == IPDL_ETH) { + eth_port = ð_port_table[ip_port->ip_dl.dl_eth.de_port]; + if (eth_port->etp_flags & EPF_GOT_ADDR) { + addrptr = ð_port->etp_ethaddr; + addrlen = sizeof(eth_port->etp_ethaddr); + } + } + + /* + * Compute the unpadded and padded length of the structure. We pad the + * structure ourselves here, even though the caller will otherwise pad + * it later, because it is easy to do so and saves on a vector element. + */ + hdrlen = offsetof(struct sockaddr_dl, sdl_data); + len = hdrlen + namelen + addrlen; + padlen = RT_ROUNDUP(len) - len; + assert(len + padlen <= max); + + /* If we are asked not to generate the actual data, stop here. */ + if (buf == NULL) + return len + padlen; + + /* + * Fill the sockaddr_dl structure header. The C99 strict aliasing + * rules prevent us from filling 'buf' through a pointer structure + * directly. + */ + memset(&sdl, 0, hdrlen); + sdl.sdl_len = len; + sdl.sdl_family = AF_LINK; + sdl.sdl_index = ndx; + sdl.sdl_type = ipdl_to_ift(ip_port->ip_dl_type); + sdl.sdl_nlen = namelen; + sdl.sdl_alen = addrlen; + sdl.sdl_slen = 0; + + /* + * Generate the full sockaddr_dl structure in the given buffer. These + * memory sizes are typically small, so the extra memory copies are not + * too expensive. The advantage of generating a single sockaddr_dl + * structure buffer is that we can use copyout_rta() on it. + */ + memcpy(buf, &sdl, hdrlen); + if (namelen > 0) + memcpy(&buf[hdrlen], namebuf, namelen); + if (addrlen > 0) + memcpy(&buf[hdrlen + namelen], addrptr, addrlen); + if (padlen > 0) + memset(&buf[len], 0, padlen); + + return len + padlen; +} + +/* + * Compute the length for, and possibly generate, an interface information + * record for the given interface. + */ +static ssize_t +gen_ifm(const ip_port_t * ip_port, int ndx, int is_up, struct rmib_oldp * oldp, + ssize_t off) +{ + struct if_msghdr ifm; + char buf[SDL_BUFSIZE]; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX], size; + + if (oldp != NULL) { + memset(&ifm, 0, sizeof(ifm)); + ifm.ifm_version = RTM_VERSION; + ifm.ifm_type = RTM_IFINFO; + ifm.ifm_flags = (is_up) ? (IFF_UP | IFF_RUNNING) : 0; + ifm.ifm_index = ndx; + ifm.ifm_data.ifi_type = ipdl_to_ift(ip_port->ip_dl_type); + if (ifm.ifm_data.ifi_type == IFT_LOOP) + ifm.ifm_flags |= IFF_LOOPBACK; + /* TODO: other ifm_flags, other ifm_data fields, etc. */ + } + + /* + * Note that we add padding even in this case, to ensure that the + * following structures are properly aligned as well. + */ + size = make_sdl(ip_port, ndx, (oldp != NULL) ? buf : NULL, + sizeof(buf)); + + memset(rta_map, 0, sizeof(rta_map)); + rta_map[RTAX_IFP] = buf; + rta_len[RTAX_IFP] = size; + + return copyout_rta(&ifm, sizeof(ifm), &ifm.ifm_msglen, &ifm.ifm_addrs, + rta_map, rta_len, oldp, off); +} + +/* + * Compute the length for, and possibly generate, an AF_LINK-family interface + * address record. + */ +static ssize_t +gen_ifam_dl(const ip_port_t * ip_port, int ndx, int is_up, + struct rmib_oldp * oldp, ssize_t off) +{ + struct ifa_msghdr ifam; + char buf[SDL_BUFSIZE]; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX], size; + + if (oldp != NULL) { + memset(&ifam, 0, sizeof(ifam)); + ifam.ifam_version = RTM_VERSION; + ifam.ifam_type = RTM_NEWADDR; + ifam.ifam_index = ndx; + ifam.ifam_metric = 0; /* unknown and irrelevant */ + } + + size = make_sdl(ip_port, ndx, (oldp != NULL) ? buf : NULL, + sizeof(buf)); + + /* + * We do not generate a netmask. NetBSD seems to generate a netmask + * with all-one bits for the number of bytes equal to the name length, + * for reasons unknown to me. If we did the same, we would end up with + * a conflict on the static 'namebuf' buffer. + */ + memset(rta_map, 0, sizeof(rta_map)); + rta_map[RTAX_IFA] = buf; + rta_len[RTAX_IFA] = size; + + return copyout_rta(&ifam, sizeof(ifam), &ifam.ifam_msglen, + &ifam.ifam_addrs, rta_map, rta_len, oldp, off); +} + +/* + * Compute the length for, and possibly generate, an AF_INET-family interface + * address record. + */ +static ssize_t +gen_ifam_inet(const ip_port_t * ip_port, int ndx, int is_up, + struct rmib_oldp * oldp, ssize_t off) +{ + struct ifa_msghdr ifam; + struct sockaddr_in ipaddr, netmask; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX]; + + if (oldp != NULL) { + memset(&ifam, 0, sizeof(ifam)); + ifam.ifam_msglen = sizeof(ifam); + ifam.ifam_version = RTM_VERSION; + ifam.ifam_type = RTM_NEWADDR; + ifam.ifam_addrs = 0; + ifam.ifam_index = ndx; + ifam.ifam_metric = 0; /* unknown and irrelevant */ + } + + memset(rta_map, 0, sizeof(rta_map)); + + if (ip_port->ip_flags & IPF_IPADDRSET) { + if (oldp != NULL) { + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.sin_family = AF_INET; + ipaddr.sin_len = sizeof(ipaddr); + ipaddr.sin_addr.s_addr = ip_port->ip_ipaddr; + } + + rta_map[RTAX_IFA] = &ipaddr; + rta_len[RTAX_IFA] = sizeof(ipaddr); + } + + if (ip_port->ip_flags & IPF_NETMASKSET) { + /* + * TODO: BSD goes through the trouble of compressing the + * netmask for some reason. We need to figure out if + * compression is actually required by any part of userland. + */ + if (oldp != NULL) { + memset(&netmask, 0, sizeof(netmask)); + netmask.sin_family = AF_INET; + netmask.sin_len = sizeof(netmask); + netmask.sin_addr.s_addr = ip_port->ip_subnetmask; + } + + rta_map[RTAX_NETMASK] = &netmask; + rta_len[RTAX_NETMASK] = sizeof(netmask); + } + + return copyout_rta(&ifam, sizeof(ifam), &ifam.ifam_msglen, + &ifam.ifam_addrs, rta_map, rta_len, oldp, off); +} + +/* + * Compute the size needed for, and optionally copy out, the interface and + * address information for the given interface. + */ +static ssize_t +do_one_if(const ip_port_t * ip_port, int ndx, struct rmib_oldp * oldp, + ssize_t off, int filter) +{ + ssize_t r, len; + int is_up; + + /* + * If the interface is not configured, we mark it as down and do not + * provide IP address information. + */ + is_up = (ip_port->ip_flags & IPF_IPADDRSET); + + len = 0; + + /* There is always a full interface information record. */ + if ((r = gen_ifm(ip_port, ndx, is_up, oldp, off)) < 0) + return r; + len += r; + + /* If not filtered, there is a datalink address record. */ + if (filter == 0 || filter == AF_LINK) { + if ((r = gen_ifam_dl(ip_port, ndx, is_up, oldp, + off + len)) < 0) + return r; + len += r; + } + + /* If configured and not filtered, there is an IPv4 address record. */ + if (is_up && (filter == 0 || filter == AF_INET)) { + if ((r = gen_ifam_inet(ip_port, ndx, is_up, oldp, + off + len)) < 0) + return r; + len += r; + } + + /* + * Whether or not anything was copied out, upon success we return the + * full length of the data. + */ + return len; +} + +/* + * Remote MIB implementation of CTL_NET PF_ROUTE 0. This function handles all + * queries on the "net.route.rtable" sysctl(2) node. + */ +static ssize_t +net_route_rtable(struct rmib_call * call, struct rmib_node * node __unused, + struct rmib_oldp * oldp, struct rmib_newp * newp __unused) +{ + const ip_port_t *ip_port; + ssize_t r, off; + int i, filter, ndx; + + if (call->call_namelen != 3) + return EINVAL; + + /* We only support listing interfaces for now. */ + if (call->call_name[1] != NET_RT_IFLIST) + return EOPNOTSUPP; + + filter = call->call_name[0]; + ndx = call->call_name[2]; + + off = 0; + + for (i = 0, ip_port = ip_port_table; i < ip_conf_nr; i++, ip_port++) { + if (!(ip_port->ip_flags & IPF_CONFIGURED)) + continue; + + /* + * If information about a specific interface index is requested + * then skip all other entries. Interface indices must be + * nonzero, so we shift the numbers by one. We can avoid going + * through the loop altogether here, but getifaddrs(3) does not + * query specific interfaces anyway. + */ + if (ndx != 0 && ndx != ip_port->ip_port + 1) + continue; + + /* Avoid generating results that are never copied out. */ + if (oldp != NULL && !rmib_inrange(oldp, off)) + oldp = NULL; + + if ((r = do_one_if(ip_port, ip_port->ip_port + 1, oldp, off, + filter)) < 0) + return r; + + off += r; + } + + return off; +} + +/* The CTL_NET PF_ROUTE subtree. */ +static struct rmib_node net_route_table[] = { + [0] = RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0, net_route_rtable, + "rtable", "Routing table information") +}; + +/* The CTL_NET PF_ROUTE node. */ +static struct rmib_node net_route_node = + RMIB_NODE(RMIB_RO, net_route_table, "route", "PF_ROUTE information"); + +/* + * Register the net.route RMIB subtree with the MIB service. Since inet does + * not support clean shutdowns, there is no matching cleanup function. + */ +void +rtinfo_init(void) +{ + const int mib[] = { CTL_NET, PF_ROUTE }; + int r; + + if ((r = rmib_register(mib, __arraycount(mib), &net_route_node)) != OK) + panic("unable to register remote MIB tree: %d", r); +} diff --git a/minix/net/lwip/Makefile b/minix/net/lwip/Makefile index a11bbd82e..d82950a25 100644 --- a/minix/net/lwip/Makefile +++ b/minix/net/lwip/Makefile @@ -8,7 +8,8 @@ SRCS= lwip.c \ tcp.c \ raw_ip.c \ inet_config.c \ - eth.c + eth.c \ + rtinfo.c FILES=${PROG}.conf FILESNAME=${PROG} diff --git a/minix/net/lwip/driver.c b/minix/net/lwip/driver.c index 4d6f286f4..71a09b3e6 100644 --- a/minix/net/lwip/driver.c +++ b/minix/net/lwip/driver.c @@ -102,6 +102,15 @@ static struct nic * lookup_nic_default(void) return NULL; } +struct nic * nic_get(int i) +{ + + if (i < 0 || i >= MAX_DEVS || devices[i].drv_ep == NONE) + return NULL; + + return &devices[i]; +} + void nic_init_all(void) { int i; diff --git a/minix/net/lwip/lwip.c b/minix/net/lwip/lwip.c index 9724d291a..ac00f72ff 100644 --- a/minix/net/lwip/lwip.c +++ b/minix/net/lwip/lwip.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "proto.h" @@ -132,6 +133,9 @@ static int sef_cb_init_fresh(__unused int type, __unused sef_init_info_t *info) */ chardriver_announce(); + /* Register net.route RMIB subtree with the MIB service. */ + rtinfo_init(); + return(OK); } @@ -276,7 +280,9 @@ int main(__unused int argc, __unused char ** argv) m.m_source); continue; } - } else + } else if (m.m_source == MIB_PROC_NR) + rmib_process(&m, ipc_status); + else /* all other request can be from drivers only */ driver_request(&m); } diff --git a/minix/net/lwip/proto.h b/minix/net/lwip/proto.h index ce967a02f..35ece8eed 100644 --- a/minix/net/lwip/proto.h +++ b/minix/net/lwip/proto.h @@ -24,6 +24,7 @@ void nic_assign_driver(const char * dev_type, const char * driver_name, unsigned int instance, int is_default); +struct nic *nic_get(int); void nic_init_all(void); void driver_request(message * m); void driver_up(const char * label, endpoint_t ep); @@ -55,4 +56,7 @@ static inline int copy_to_user(endpoint_t proc, return sys_safecopyto(proc, gid, offset, (vir_bytes)src_ptr, size); } +/* rtinfo.c */ +void rtinfo_init(void); + #endif /* __LWIP_PROTO_H__ */ diff --git a/minix/net/lwip/rtinfo.c b/minix/net/lwip/rtinfo.c new file mode 100644 index 000000000..878fb3875 --- /dev/null +++ b/minix/net/lwip/rtinfo.c @@ -0,0 +1,423 @@ +/* + * Mock net.route sysctl(2) subtree implementation using RMIB. This code + * serves as a temporary bridge to allow libc to switch from the original, + * native MINIX3 getifaddrs(3) to the NetBSD getifaddrs(3). As such, it + * implements only a small subset of NetBSD's full net.route functionality, + * although also more than needed only to imitate the MINIX3 getifaddrs(3). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proto.h" +#include "driver.h" + +/* Max. number of bytes for a full sockaddr_dl structure, including data. */ +#define SDL_BUFSIZE (sizeof(struct sockaddr_dl) + 32) + +static const char padbuf[RT_ROUNDUP(0)] = { 0 }; + +/* + * Copy out a vector of data to the sysctl(2) caller. TODO: decide what to do + * with this. We could implement this as a vectored-safecopy operation in + * RMIB. We could also copy everything into a single buffer first. The + * current implementation is probably the worst among the possibilities. + */ +static ssize_t +rmib_vcopyout(struct rmib_oldp * oldp, size_t off, const iovec_t * iov, + unsigned int iovcnt) +{ + unsigned int i; + ssize_t r, len; + + len = 0; + + for (i = 0; i < iovcnt; i++) { + if ((r = rmib_copyout(oldp, off + len, + (const void *)iov[i].iov_addr, iov[i].iov_size)) < 0) + return r; + + len += r; + } + + return len; +} + +/* + * Compute the length for, and possibly copy out, an interface information or + * interface address record with an associated set of zero or more routing + * table addresses. The addresses are padded as necessary. Store the full + * record length and the address bitmap before copying out the entire record. + */ +static ssize_t +copyout_rta(void * hdr, size_t size, u_short * msglen, int * addrs, + void * rta_map[RTAX_MAX], size_t rta_len[RTAX_MAX], + struct rmib_oldp * oldp, ssize_t off) +{ + iovec_t iov[1 + RTAX_MAX * 2]; + size_t len, total, padlen; + unsigned int i, iovcnt; + int mask; + + iovcnt = 0; + iov[iovcnt].iov_addr = (vir_bytes)hdr; + iov[iovcnt++].iov_size = size; + + total = size; + mask = 0; + + /* + * Any addresses in the given map should be stored in the numbering + * order of the map. + */ + for (i = 0; i < RTAX_MAX; i++) { + if (rta_map[i] == NULL) + continue; + + assert(iovcnt < __arraycount(iov)); + iov[iovcnt].iov_addr = (vir_bytes)rta_map[i]; + iov[iovcnt++].iov_size = len = rta_len[i]; + + padlen = RT_ROUNDUP(len) - len; + if (padlen > 0) { + assert(iovcnt < __arraycount(iov)); + iov[iovcnt].iov_addr = (vir_bytes)padbuf; + iov[iovcnt++].iov_size = padlen; + } + + total += len + padlen; + mask |= (1 << i); + } + + /* If only the length was requested, return it now. */ + if (oldp == NULL) + return total; + + /* + * Casting 'hdr' would violate C99 strict aliasing rules, so store the + * computed header values through direct pointers. Bah. + */ + *msglen = total; + *addrs = mask; + + return rmib_vcopyout(oldp, off, iov, iovcnt); +} + +/* + * Compute the length for, and possibly generate, a sockaddr_dl structure for + * the given interface. The complication here is that the structure contains + * various field packed together dynamically, making it variable sized. + */ +static size_t +make_sdl(const struct nic * nic, int ndx, char * buf, size_t max) +{ + struct sockaddr_dl sdl; + size_t hdrlen, namelen, addrlen, padlen, len; + + namelen = strlen(nic->name); + addrlen = sizeof(nic->netif.hwaddr); + + /* + * Compute the unpadded and padded length of the structure. We pad the + * structure ourselves here, even though the caller will otherwise pad + * it later, because it is easy to do so and saves on a vector element. + */ + hdrlen = offsetof(struct sockaddr_dl, sdl_data); + len = hdrlen + namelen + addrlen; + padlen = RT_ROUNDUP(len) - len; + assert(len + padlen <= max); + + /* If we are asked not to generate the actual data, stop here. */ + if (buf == NULL) + return len + padlen; + + /* + * Fill the sockaddr_dl structure header. The C99 strict aliasing + * rules prevent us from filling 'buf' through a pointer structure + * directly. + */ + memset(&sdl, 0, hdrlen); + sdl.sdl_len = len; + sdl.sdl_family = AF_LINK; + sdl.sdl_index = ndx; + sdl.sdl_type = IFT_ETHER; + sdl.sdl_nlen = namelen; + sdl.sdl_alen = addrlen; + sdl.sdl_slen = 0; + + /* + * Generate the full sockaddr_dl structure in the given buffer. These + * memory sizes are typically small, so the extra memory copies are not + * too expensive. The advantage of generating a single sockaddr_dl + * structure buffer is that we can use copyout_rta() on it. + */ + memcpy(buf, &sdl, hdrlen); + if (namelen > 0) + memcpy(&buf[hdrlen], nic->name, namelen); + if (addrlen > 0) + memcpy(&buf[hdrlen + namelen], nic->netif.hwaddr, addrlen); + if (padlen > 0) + memset(&buf[len], 0, padlen); + + return len + padlen; +} + +/* + * Compute the length for, and possibly generate, an interface information + * record for the given interface. + */ +static ssize_t +gen_ifm(const struct nic * nic, int ndx, int is_up, struct rmib_oldp * oldp, + ssize_t off) +{ + struct if_msghdr ifm; + char buf[SDL_BUFSIZE]; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX], size; + + if (oldp != NULL) { + memset(&ifm, 0, sizeof(ifm)); + ifm.ifm_version = RTM_VERSION; + ifm.ifm_type = RTM_IFINFO; + ifm.ifm_flags = (is_up) ? (IFF_UP | IFF_RUNNING) : 0; + ifm.ifm_index = ndx; + ifm.ifm_data.ifi_type = IFT_ETHER; + /* TODO: other ifm_flags, other ifm_data fields, etc. */ + } + + /* + * Note that we add padding even in this case, to ensure that the + * following structures are properly aligned as well. + */ + size = make_sdl(nic, ndx, (oldp != NULL) ? buf : NULL, sizeof(buf)); + + memset(rta_map, 0, sizeof(rta_map)); + rta_map[RTAX_IFP] = buf; + rta_len[RTAX_IFP] = size; + + return copyout_rta(&ifm, sizeof(ifm), &ifm.ifm_msglen, &ifm.ifm_addrs, + rta_map, rta_len, oldp, off); +} + +/* + * Compute the length for, and possibly generate, an AF_LINK-family interface + * address record. + */ +static ssize_t +gen_ifam_dl(const struct nic * nic, int ndx, int is_up, + struct rmib_oldp * oldp, ssize_t off) +{ + struct ifa_msghdr ifam; + char buf[SDL_BUFSIZE]; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX], size; + + if (oldp != NULL) { + memset(&ifam, 0, sizeof(ifam)); + ifam.ifam_version = RTM_VERSION; + ifam.ifam_type = RTM_NEWADDR; + ifam.ifam_index = ndx; + ifam.ifam_metric = 0; /* unknown and irrelevant */ + } + + size = make_sdl(nic, ndx, (oldp != NULL) ? buf : NULL, sizeof(buf)); + + /* + * We do not generate a netmask. NetBSD seems to generate a netmask + * with all-one bits for the number of bytes equal to the name length, + * for reasons unknown to me. If we did the same, we would end up with + * a conflict on the static 'namebuf' buffer. + */ + memset(rta_map, 0, sizeof(rta_map)); + rta_map[RTAX_IFA] = buf; + rta_len[RTAX_IFA] = size; + + return copyout_rta(&ifam, sizeof(ifam), &ifam.ifam_msglen, + &ifam.ifam_addrs, rta_map, rta_len, oldp, off); +} + +/* + * Compute the length for, and possibly generate, an AF_INET-family interface + * address record. + */ +static ssize_t +gen_ifam_inet(const struct nic * nic, int ndx, int is_up, + struct rmib_oldp * oldp, ssize_t off) +{ + struct ifa_msghdr ifam; + struct sockaddr_in ipaddr, netmask; + void *rta_map[RTAX_MAX]; + size_t rta_len[RTAX_MAX]; + + if (oldp != NULL) { + memset(&ifam, 0, sizeof(ifam)); + ifam.ifam_msglen = sizeof(ifam); + ifam.ifam_version = RTM_VERSION; + ifam.ifam_type = RTM_NEWADDR; + ifam.ifam_addrs = 0; + ifam.ifam_index = ndx; + ifam.ifam_metric = 0; /* unknown and irrelevant */ + } + + memset(rta_map, 0, sizeof(rta_map)); + + if (!ip_addr_isany(&nic->netif.ip_addr)) { + if (oldp != NULL) { + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.sin_family = AF_INET; + ipaddr.sin_len = sizeof(ipaddr); + ipaddr.sin_addr.s_addr = + ip4_addr_get_u32(&nic->netif.ip_addr); + } + + rta_map[RTAX_IFA] = &ipaddr; + rta_len[RTAX_IFA] = sizeof(ipaddr); + } + + if (!ip_addr_isany(&nic->netif.netmask)) { + /* + * TODO: BSD goes through the trouble of compressing the + * netmask for some reason. We need to figure out if + * compression is actually required by any part of userland. + */ + if (oldp != NULL) { + memset(&netmask, 0, sizeof(netmask)); + netmask.sin_family = AF_INET; + netmask.sin_len = sizeof(netmask); + netmask.sin_addr.s_addr = + ip4_addr_get_u32(&nic->netif.netmask); + } + + rta_map[RTAX_NETMASK] = &netmask; + rta_len[RTAX_NETMASK] = sizeof(netmask); + } + + return copyout_rta(&ifam, sizeof(ifam), &ifam.ifam_msglen, + &ifam.ifam_addrs, rta_map, rta_len, oldp, off); +} + +/* + * Compute the size needed for, and optionally copy out, the interface and + * address information for the given interface. + */ +static ssize_t +do_one_if(const struct nic * nic, int ndx, struct rmib_oldp * oldp, + ssize_t off, int filter) +{ + ssize_t r, len; + int is_up; + + /* + * If the interface is not configured, we mark it as down and do not + * provide IP address information. + */ + is_up = !ip_addr_isany(&nic->netif.ip_addr); + + len = 0; + + /* There is always a full interface information record. */ + if ((r = gen_ifm(nic, ndx, is_up, oldp, off)) < 0) + return r; + len += r; + + /* If not filtered, there is a datalink address record. */ + if (filter == 0 || filter == AF_LINK) { + if ((r = gen_ifam_dl(nic, ndx, is_up, oldp, off + len)) < 0) + return r; + len += r; + } + + /* If configured and not filtered, there is an IPv4 address record. */ + if (is_up && (filter == 0 || filter == AF_INET)) { + if ((r = gen_ifam_inet(nic, ndx, is_up, oldp, off + len)) < 0) + return r; + len += r; + } + + /* + * Whether or not anything was copied out, upon success we return the + * full length of the data. + */ + return len; +} + +/* + * Remote MIB implementation of CTL_NET PF_ROUTE 0. This function handles all + * queries on the "net.route.rtable" sysctl(2) node. + */ +static ssize_t +net_route_rtable(struct rmib_call * call, struct rmib_node * node __unused, + struct rmib_oldp * oldp, struct rmib_newp * newp __unused) +{ + const struct nic *nic; + ssize_t r, off; + int i, filter, ndx; + + if (call->call_namelen != 3) + return EINVAL; + + /* We only support listing interfaces for now. */ + if (call->call_name[1] != NET_RT_IFLIST) + return EOPNOTSUPP; + + filter = call->call_name[0]; + ndx = call->call_name[2]; + + off = 0; + + for (i = 0; i < MAX_DEVS; i++) { + if (!(nic = nic_get(i))) + continue; + + /* + * If information about a specific interface index is requested + * then skip all other entries. Interface indices must be + * nonzero, so we shift the numbers by one. We can avoid going + * through the loop altogether here, but getifaddrs(3) does not + * query specific interfaces anyway. + */ + if (ndx != 0 && ndx != i + 1) + continue; + + /* Avoid generating results that are never copied out. */ + if (oldp != NULL && !rmib_inrange(oldp, off)) + oldp = NULL; + + if ((r = do_one_if(nic, i + 1, oldp, off, filter)) < 0) + return r; + + off += r; + } + + return off; +} + +/* The CTL_NET PF_ROUTE subtree. */ +static struct rmib_node net_route_table[] = { + [0] = RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0, net_route_rtable, + "rtable", "Routing table information") +}; + +/* The CTL_NET PF_ROUTE node. */ +static struct rmib_node net_route_node = + RMIB_NODE(RMIB_RO, net_route_table, "route", "PF_ROUTE information"); + +/* + * Register the net.route RMIB subtree with the MIB service. Since inet does + * not support clean shutdowns, there is no matching cleanup function. + */ +void +rtinfo_init(void) +{ + const int mib[] = { CTL_NET, PF_ROUTE }; + int r; + + if ((r = rmib_register(mib, __arraycount(mib), &net_route_node)) != OK) + panic("unable to register remote MIB tree: %d", r); +} diff --git a/minix/usr.bin/trace/service/mib.c b/minix/usr.bin/trace/service/mib.c index a56df1676..9734a0e03 100644 --- a/minix/usr.bin/trace/service/mib.c +++ b/minix/usr.bin/trace/service/mib.c @@ -240,7 +240,7 @@ put_kern_boottime(struct trace_proc * proc, const char * name, */ static int put_kern_sysvipc_info(struct trace_proc * proc, const char * name, - int type, const void * ptr __unused, vir_bytes addr, size_t size) + int type, const void * ptr, vir_bytes addr, size_t size) { const int *mib; const char *text; @@ -328,10 +328,78 @@ static const struct sysctl_tab vm_tab[] = { PROC(VM_LOADAVG, sizeof(struct loadavg), put_vm_loadavg), }; +/* + * Print CTL_NET PF_ROUTE 0. + */ +static int +put_net_route_rtable(struct trace_proc * proc, const char * name, + int type, const void * ptr, vir_bytes addr, size_t size) +{ + const int *mib; + const char *text; + unsigned int i; + + /* + * TODO: print the obtained structure(s). For now we are just + * concerned with the name components. + */ + if (type != ST_NAME) { + put_ptr(proc, name, addr); + + return TRUE; + } + + mib = (const int *)ptr; + + for (i = 0; i < size; i++) { + text = NULL; + + switch (i) { + case 0: + switch (mib[i]) { + case AF_UNSPEC: text = ""; break; + case AF_LINK: text = ""; break; + case AF_INET: text = ""; break; + case AF_INET6: text = ""; break; + /* TODO: add more address families here */ + } + break; + case 1: + switch (mib[i]) { + case NET_RT_DUMP: text = ""; break; + case NET_RT_FLAGS: text = ""; break; + case NET_RT_IFLIST: text = ""; break; + } + break; + case 2: + if (mib[1] == NET_RT_IFLIST && mib[i] == 0) + text = ""; + } + + if (!valuesonly && text != NULL) + put_field(proc, NULL, text); + else + put_value(proc, NULL, "%d", mib[i]); + } + + return 0; +} + +/* The CTL_NET PF_ROUTE table. */ +static const struct sysctl_tab net_route_tab[] = { + PROC(0, 0, put_net_route_rtable), +}; + +/* The CTL_NET table. */ +static const struct sysctl_tab net_tab[] = { + NODE(PF_ROUTE, net_route_tab), +}; + /* The top-level table, which is indexed by identifier. */ static const struct sysctl_tab root_tab[] = { [CTL_KERN] = NODE(0, kern_tab), [CTL_VM] = NODE(0, vm_tab), + [CTL_NET] = NODE(0, net_tab), }; /*