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