/* LWIP service - ifdev.c - network interface devices */ #include "lwip.h" #include "mcast.h" #include "ifaddr.h" #include "rtsock.h" #include "route.h" #include "bpfdev.h" #include /* * The highest possible interface index number, plus one. We currently let * lwIP choose the interface index. lwIP will generate a number between 1 and * 255 inclusive. For efficiency, we use an array to look up an interface * device object by its index. Thus, this array must be large enough to be * indexed by the largest possible index number generated by lwIP. lwIP uses * an unsigned 8-bit field to store the index number. */ #define MAX_IFDEV (UINT8_MAX + 1) /* The table is indexed by the interface index minus one. */ static struct ifdev *ifdev_table[MAX_IFDEV]; /* index-based lookup table */ static TAILQ_HEAD(, ifdev) ifdev_list; /* list of active interfaces */ static struct ifdev *ifdev_loopback; /* loopback interface */ /* * The maximum number of virtual interface types--that is, interface types for * which interfaces may be created and destroyed dynamically. The BSDs call * these "clones". There should be enough slots for all types, which are * registered by their respective modules through ifdev_register(). Increase * as necessary. */ #define MAX_VTYPE 4 static struct { const char *ifvt_name; /* interface name without digits (e.g. "lo") */ size_t ifvt_namelen; /* length of the name, excluding null term. */ int (*ifvt_create)(const char *); /* ifdev create function */ } ifdev_vtype[MAX_VTYPE]; static unsigned int ifdev_vtypes; /* number of in-use vtype slots */ #define IFDEV_MIN_MTU 1280 /* minimum interface MTU, required by IPv6 */ /* * Initialize the network interface devices module. This call must be issued * before any virtual interfaces are initialized, because the virtual types * array is initialized here. */ void ifdev_init(void) { memset(ifdev_table, 0, sizeof(ifdev_table)); TAILQ_INIT(&ifdev_list); memset(ifdev_vtype, 0, sizeof(ifdev_vtype)); ifdev_vtypes = 0; } /* * Check all active interfaces to see if any tasks need to be performed. This * function is called as part of each message loop iteration. */ void ifdev_poll(void) { struct ifdev *ifdev; /* * Call the polling function of the active interfaces. Note that * interfaces may not remove themselves as a result of polling! */ TAILQ_FOREACH(ifdev, &ifdev_list, ifdev_next) { if (ifdev->ifdev_ops->iop_poll != NULL) ifdev->ifdev_ops->iop_poll(ifdev); } } /* * Handle an incoming packet on an interface. This function assumes ownership * of the packet buffers: the caller must no longer refer to it afterward. For * packets looped back for a non-loopback interface, 'ifdev' is the loopback * interface and 'netif' is the original (non-loopback) interface's netif. For * other packets, 'ifdev' is the actual interface and 'netif' is NULL. The * packet is passed to BPF devices only if 'to_bpf' is set. */ void ifdev_input(struct ifdev * ifdev, struct pbuf * pbuf, struct netif * netif, int to_bpf) { struct bpfdev_link *bpfl; err_t err; /* * Looped-back packets are captured on the loopback device, not on the * original interface. Similarly, we account the traffic to the * loopback interface. This is a policy decision (inspired by NetBSD's * behavior) and may be changed later. */ if (to_bpf) { TAILQ_FOREACH(bpfl, &ifdev->ifdev_bpf, bpfl_next) bpfdev_input(bpfl, pbuf); } ifdev->ifdev_data.ifi_ipackets++; ifdev->ifdev_data.ifi_ibytes += pbuf->tot_len; if (pbuf->flags & PBUF_FLAG_LLMCAST) ifdev->ifdev_data.ifi_imcasts++; /* * For looped-back packets, we must bypass the regular netif input * function (as that one is for link-layer packet handling) and instead * pass it directly to the IP-layer packet handling function of lwIP. */ if (netif != NULL) err = ip_input(pbuf, netif); else err = ifdev->ifdev_netif.input(pbuf, &ifdev->ifdev_netif); if (err != ERR_OK) pbuf_free(pbuf); } /* * Handle an outgoing packet on an interface. Return ERR_OK if the packet was * transmitted or another lwIP ERR_ error code upon failure. Either way, the * caller is responsible for freeing the packet buffers. If the packet is * to be looped back to a non-loopback interface (because its destination is a * local address), 'ifdev' is the loopback interface and 'netif' is set to the * original interface's netif. In all other cases, 'ifdev' is the packet's * source interface and 'netif' is NULL. The packet is passed to attached BPF * devices only if 'to_bpf' is set. If 'hdrcmplt' is set, the source address * of the data link header is already filled in; otherwise, the source address * must be set to the device's source address, if applicable. */ err_t ifdev_output(struct ifdev * ifdev, struct pbuf * pbuf, struct netif * netif, int to_bpf, int hdrcmplt) { struct bpfdev_link *bpfl; /* * If the interface and/or the link is down, discard the packet without * reporting it to BPF or the actual interface module. */ if (!ifdev_is_up(ifdev) || !ifdev_is_link_up(ifdev)) return ERR_IF; /* this should translate to ENETDOWN */ /* * If the link-layer header is not yet complete, fill in the source * address now. This exception applies to BPF-generated packets only. * Complete the header before passing the packet back to BPF, which * should see the completed version of the packet. */ if (!hdrcmplt && ifdev->ifdev_ops->iop_hdrcmplt != NULL) ifdev->ifdev_ops->iop_hdrcmplt(ifdev, pbuf); /* * As in ifdev_input(), we use the loopback interface for BPF and * statistics even if the packet originates from a non-loopback device. */ if (to_bpf) { TAILQ_FOREACH(bpfl, &ifdev->ifdev_bpf, bpfl_next) bpfdev_output(bpfl, pbuf); } ifdev->ifdev_data.ifi_opackets++; ifdev->ifdev_data.ifi_obytes += pbuf->tot_len; /* * TODO: this is rather imprecise, because it works only when we set * the pbuf flag explicitly ourselves. That happens only for UDP/RAW * packets, and not for (e.g.) ND6 multicast traffic. We have reasons * to set the flags ourselves anyway, namely to support MSG_MCAST and * MSG_BCAST on loopback interfaces, but they should be complemented by * additional checks here on, say, the destination ethernet address. */ if (pbuf->flags & PBUF_FLAG_LLMCAST) ifdev->ifdev_data.ifi_omcasts++; return ifdev->ifdev_ops->iop_output(ifdev, pbuf, netif); } /* * Transmit an IPv4 packet on an interface, as requested by lwIP. Pass on the * packet to the interface's link processor (e.g., etharp), unless the packet * should be rejected or blackholed according to route information, or it is to * be looped back into the interface. The latter may occur if the destination * address belongs to the interface. In that case, we send the packet over a * loopback interface instead. In addition, if this is a multicast packet that * should be looped back, send a copy over a loopback interface as well. * Loopback interfaces themselves are exempt from these special cases. */ static err_t ifdev_output_v4(struct netif * netif, struct pbuf * pbuf, const ip4_addr_t * ipaddr) { struct ifdev *ifdev = netif_get_ifdev(netif); err_t err; assert(ifdev_loopback != NULL); /* Check for reject/blackhole routes. */ if (!route_output_v4(ifdev, ipaddr, &err)) return err; /* Handle looping of multicast packets on non-loopback interfaces. */ if (!ifdev_is_loopback(ifdev) && (pbuf->flags & PBUF_FLAG_MCASTLOOP)) (void)ifdev_output(ifdev_loopback, pbuf, netif, FALSE /*to_bpf*/, TRUE /*hdrcmplt*/); /* Divert packets sent to the local interface address. */ if (!ifdev_is_loopback(ifdev) && ifdev->ifdev_v4set && ip4_addr_cmp(netif_ip4_addr(&ifdev->ifdev_netif), ipaddr)) ifdev = ifdev_loopback; else netif = NULL; if (ifdev->ifdev_ops->iop_output_v4 != NULL) return ifdev->ifdev_ops->iop_output_v4(ifdev_get_netif(ifdev), pbuf, ipaddr); else return ifdev_output(ifdev, pbuf, netif, TRUE /*to_bpf*/, TRUE /*hdrcmplt*/); } /* * Transmit an IPv6 packet on an interface, as requested by lwIP. As for IPv4. */ static err_t ifdev_output_v6(struct netif * netif, struct pbuf * pbuf, const ip6_addr_t * ipaddr) { struct ifdev *ifdev = netif_get_ifdev(netif); err_t err; assert(ifdev_loopback != NULL); /* Check for reject/blackhole routes. */ if (!route_output_v6(ifdev, ipaddr, &err)) return err; /* Handle looping of multicast packets on non-loopback interfaces. */ if (!ifdev_is_loopback(ifdev) && (pbuf->flags & PBUF_FLAG_MCASTLOOP)) (void)ifdev_output(ifdev_loopback, pbuf, netif, FALSE /*to_bpf*/, TRUE /*hdrcmplt*/); /* Divert packets sent to the local interface address. */ if (!ifdev_is_loopback(ifdev) && (netif_get_ip6_addr_match(&ifdev->ifdev_netif, ipaddr) != -1 || ip6_addr_ismulticast_iflocal(ipaddr))) ifdev = ifdev_loopback; else netif = NULL; if (ifdev->ifdev_ops->iop_output_v6 != NULL) return ifdev->ifdev_ops->iop_output_v6(ifdev_get_netif(ifdev), pbuf, ipaddr); else return ifdev_output(ifdev, pbuf, netif, TRUE /*to_bpf*/, TRUE /*hdrcmplt*/); } /* * Status callback function, called by lwIP whenever certain status changes are * made on the netif. These changes may be initiated either by lwIP itself or * by us. We use this callback to check lwIP-initiated state changes on local * IPv6 addresses, using shadow state to filter out self-initiated changes. * * One day we might switch to the extended netif callback mechanism offered by * lwIP. Currently, netif state changes are rare and it takes us little effort * to find out whether anything changed, so there is no immediate need. */ static void ifdev_status_callback(struct netif * netif) { struct ifdev *ifdev = netif_get_ifdev(netif); ifaddr_v6_check(ifdev); } /* * Initialize the netif structure for a new interface. Most of this is handled * by the specific interface module. */ static err_t ifdev_init_netif(struct netif * netif) { struct ifdev *ifdev = netif_get_ifdev(netif); assert(ifdev != NULL); netif->output = ifdev_output_v4; netif->output_ip6 = ifdev_output_v6; netif->hwaddr_len = ifdev->ifdev_data.ifi_addrlen; netif->mtu = ifdev->ifdev_data.ifi_mtu; netif_set_status_callback(netif, ifdev_status_callback); return ifdev->ifdev_ops->iop_init(ifdev, netif); } /* * Retrieve an interface device by its interface index. Return a pointer to * the interface device if found, or NULL otherwise. If the given interface * index is zero, this function will always return NULL. */ struct ifdev * ifdev_get_by_index(uint32_t ifindex) { if (ifindex >= __arraycount(ifdev_table)) return NULL; return ifdev_table[ifindex]; } /* * Find an interface device by its name. Return a pointer to the interface * device if found, or NULL otherwise. */ struct ifdev * ifdev_find_by_name(const char * name) { struct ifdev *ifdev; TAILQ_FOREACH(ifdev, &ifdev_list, ifdev_next) { if (!strcmp(ifdev->ifdev_name, name)) return ifdev; } return NULL; } /* * Given either NULL or a previously returned interface device object pointer, * return the first or next interface device object pointer, or NULL if there * are no more. */ struct ifdev * ifdev_enum(struct ifdev * last) { if (last == NULL) return TAILQ_FIRST(&ifdev_list); else return TAILQ_NEXT(last, ifdev_next); } /* * Attach a BPF device as listener to this interface. */ void ifdev_attach_bpf(struct ifdev * ifdev, struct bpfdev_link * bpfl) { TAILQ_INSERT_TAIL(&ifdev->ifdev_bpf, bpfl, bpfl_next); } /* * Detach a previously attached BPF device from this interface. */ void ifdev_detach_bpf(struct ifdev * ifdev, struct bpfdev_link * bpfl) { TAILQ_REMOVE(&ifdev->ifdev_bpf, bpfl, bpfl_next); } /* * Register the calling party as interested in putting the interface in * promiscuous mode. There may be multiple such parties, each of which can * call this function once, after which they must call ifdev_clear_promisc() * later. If possible, the interface is put in promiscuous mode if there is at * least one interested party. Return TRUE on success, or FALSE on failure. */ int ifdev_set_promisc(struct ifdev * ifdev) { /* * A bit silly, but we want to retain the ability to fail this call for * other reasons in the future, with BPF handling that case properly. */ if (ifdev->ifdev_promisc == UINT_MAX) return FALSE; if (ifdev->ifdev_promisc++ == 0) { ifdev_update_ifflags(ifdev, ifdev->ifdev_ifflags | IFF_PROMISC); if (ifdev->ifdev_ops->iop_set_promisc != NULL) ifdev->ifdev_ops->iop_set_promisc(ifdev, TRUE); } return TRUE; } /* * Deregister a previously registered party interested in putting the interface * in promiscuous mode. Once the last party deregisters, the device is pulled * out of promiscuous mode. */ void ifdev_clear_promisc(struct ifdev * ifdev) { assert(ifdev->ifdev_promisc > 0); if (--ifdev->ifdev_promisc == 0) { if (ifdev->ifdev_ops->iop_set_promisc != NULL) ifdev->ifdev_ops->iop_set_promisc(ifdev, FALSE); ifdev_update_ifflags(ifdev, ifdev->ifdev_ifflags & ~IFF_PROMISC); } } /* * Set NetBSD-style interface flags (IFF_) for an interface. */ int ifdev_set_ifflags(struct ifdev * ifdev, unsigned int ifflags) { int r; /* Check and update only the subset of flags that may be changed. */ ifflags &= ~(IFF_CANTCHANGE | IFF_LOOPBACK); /* * Important: the callback function may call ifdev_update_ifflags() * itself immediately, to update read-only flags such as IFF_RUNNING * based on read-write flags such as IFF_UP. So as to make that work.. * * 1) this function MUST succeed if the callback function succeeds; * 2) this function MUST NOT make assumptions about the ifdev_ifflags * field across the callback invocation. * * Conversely, the callback function should be aware that the flags * field will still be updated with the flags. In this model, it is * not possible for the callback function to silently change any of the * given flags. If that is ever necessary, API changes are needed. */ if ((r = ifdev->ifdev_ops->iop_set_ifflags(ifdev, ifflags)) != OK) return r; /* * On success, merge the updated subset with the subset that may not be * changed. */ ifflags |= ifdev->ifdev_ifflags & (IFF_CANTCHANGE | IFF_LOOPBACK); ifdev_update_ifflags(ifdev, ifflags); return OK; } /* * Update NetBSD-style interface flags (IFF_) for an interface, and perform any * required operations as a result of certain flags changing. This function * bypasses all input checks and directly changes the flags field to exactly * the given set of flags. */ void ifdev_update_ifflags(struct ifdev * ifdev, unsigned int ifflags) { struct netif *netif; /* * First update the flags field itself. The new value should be * visible in the routing messages generated below, for example. */ ifdev->ifdev_ifflags = ifflags; /* * Then perform operations as a result of the flags field changing. * For now, this is relevant for IFF_UP only. */ netif = ifdev_get_netif(ifdev); if ((ifflags & IFF_UP) && !netif_is_up(netif)) { netif_set_up(netif); rtsock_msg_ifinfo(ifdev); /* * Check if all conditions are now met for link-local IPv6 * address assignment. */ ifaddr_v6_set_linklocal(ifdev); /* See if we should also reset address states now. */ if (netif_is_link_up(netif)) ifaddr_v6_set_up(ifdev); } else if (!(ifflags & IFF_UP) && netif_is_up(netif)) { netif_set_down(netif); rtsock_msg_ifinfo(ifdev); } } /* * Retrieve NetBSD-style interface capabilities (IFCAP_) for an interface: both * the supported and the enabled capabilities. */ void ifdev_get_ifcap(struct ifdev * ifdev, uint64_t * ifcap, uint64_t * ifena) { *ifcap = 0; *ifena = 0; if (ifdev->ifdev_ops->iop_get_ifcap != NULL) ifdev->ifdev_ops->iop_get_ifcap(ifdev, ifcap, ifena); } /* * Set enabled NetBSD-style interface capabilities (IFCAP_) for an interface. */ int ifdev_set_ifcap(struct ifdev * ifdev, uint64_t ifena) { if (ifdev->ifdev_ops->iop_set_ifcap != NULL) return ifdev->ifdev_ops->iop_set_ifcap(ifdev, ifena); else return EINVAL; } /* * Retrieve NetBSD-style media type (IFM_) for an interface. Return OK on * success, with the current media type selection stored in 'ifcurrent', the * driver-reported active media type in 'ifactive', and the link status in * 'ifstatus'. Return a negative error code on failure. */ int ifdev_get_ifmedia(struct ifdev * ifdev, int * ifcurrent, int * ifactive) { if (ifdev->ifdev_ops->iop_get_ifmedia == NULL) return ENOTTY; ifdev->ifdev_ops->iop_get_ifmedia(ifdev, ifcurrent, ifactive); return OK; } /* * Set NetBSD-style media type (IFM_) for an interface. Return OK on success, * or a negative error code on failure. */ int ifdev_set_ifmedia(struct ifdev * ifdev, int ifmedia) { if (ifdev->ifdev_ops->iop_set_ifmedia == NULL) return ENOTTY; if (ifmedia < 0) return EINVAL; return ifdev->ifdev_ops->iop_set_ifmedia(ifdev, ifmedia); } /* * Set the Maximum Transmission Unit for an interface. Return OK on success, * or a negative error code on failure. */ int ifdev_set_mtu(struct ifdev * ifdev, unsigned int mtu) { if (ifdev->ifdev_ops->iop_set_mtu == NULL) return ENOTTY; if (mtu < IFDEV_MIN_MTU || mtu > UINT16_MAX || !ifdev->ifdev_ops->iop_set_mtu(ifdev, mtu)) return EINVAL; ifdev->ifdev_data.ifi_mtu = mtu; ifdev->ifdev_netif.mtu = mtu; return OK; } /* * Set IPv6 Neighbor Discovery related flags. */ int ifdev_set_nd6flags(struct ifdev * ifdev, uint32_t nd6flags) { /* For now, refuse setting any flags that are not even known. */ if ((nd6flags & ~(ND6_IFF_PERFORMNUD | ND6_IFF_ACCEPT_RTADV | ND6_IFF_IFDISABLED | ND6_IFF_OVERRIDE_RTADV | ND6_IFF_AUTO_LINKLOCAL)) != 0) return EINVAL; /* * Unfortunately, the mismatch between NetBSD and lwIP requires us to * support but butcher ND6 flags. The current status is as follows: * * - ND6_IFF_PERFORMNUD: set by default as lwIP always implements NUD; * changes are disregarded but possible, for dhcpcd(8). * - ND6_IFF_ACCEPT_RTADV: disregarded but settable, for dhcpcd(8); in * our case, lwIP always processes router advertisements but never * autoconfigures addresses, so this flag has no meaning for us. * - ND6_IFF_IFDISABLED: not supported; can only be cleared; we could * probably do detection of link-local address collision and set this * flag (and disable the interface if set) when that happens; TODO. * - ND6_IFF_OVERRIDE_RTADV: same as _ACCEPT_ above. * - ND6_IFF_AUTO_LINKLOCAL: supported, but not initialized based on * the corresponding sysctl(7) flag for reasons mentioned in ifaddr. */ if (nd6flags & ND6_IFF_IFDISABLED) return EINVAL; ifdev->ifdev_nd6flags = nd6flags; return OK; } /* * Report an update to the interface's active hardware address that is *not* * the result of a user action. If the 'is_factory' flag is set, the address * is the factory (driver-given) address. This function is for use by * interface modules, to update the internal state to their current external * state. */ void ifdev_update_hwaddr(struct ifdev * ifdev, const uint8_t * hwaddr, int is_factory) { return ifaddr_dl_update(ifdev, hwaddr, is_factory); } /* * Insert a new interface device into the list of interface devices, at a * location determined by policy. */ static void ifdev_insert(struct ifdev * ifdev) { struct ifdev *ifdev2; const char *p; unsigned int unit, unit2; size_t namelen; int found; /* * While NetBSD can set up all interfaces in the order it wants them to * appear in, we do not have such luxury: network device drivers come * up and report to us in no particular predefined order, and we have * no way to know how many and which will appear. The result is that * we always have to create the loopback device first, something that * is explicitly said to be bad in NetBSD. Instead, we create an * illusion of a reasonable order by performing insertion sort on the * interface list, using (for now) these rules, ordered by priority: * * 1. same-named devices are sorted by their unit number; * 2. loopback interfaces are inserted after all other interfaces; * 3. new devices are added at the end of their type category. * * In the future, other forms of real-vs-virtual sorting may be added. */ /* First check for same-named devices (#1). */ for (p = ifdev->ifdev_name; *p != '\0' && (*p < '0' || *p > '9'); p++); namelen = (size_t)(p - ifdev->ifdev_name); for (unit = 0; *p >= '0' && *p <= '9'; p++) unit = unit * 10 + *p - '0'; found = FALSE; TAILQ_FOREACH(ifdev2, &ifdev_list, ifdev_next) { if (!strncmp(ifdev->ifdev_name, ifdev2->ifdev_name, namelen) && *(p = &ifdev2->ifdev_name[namelen]) >= '0' && *p <= '9') { for (unit2 = 0; *p >= '0' && *p <= '9'; p++) unit2 = unit2 * 10 + *p - '0'; assert(unit != unit2); found = TRUE; if (unit2 > unit) break; } else if (found) break; } if (found) { if (ifdev2 != NULL) TAILQ_INSERT_BEFORE(ifdev2, ifdev, ifdev_next); else TAILQ_INSERT_TAIL(&ifdev_list, ifdev, ifdev_next); return; } /* * No same-named device found. Is this a loopback interface? If not, * insert before the first loopback device, if any. */ if (!ifdev_is_loopback(ifdev)) { TAILQ_FOREACH(ifdev2, &ifdev_list, ifdev_next) { if (ifdev_is_loopback(ifdev2)) { TAILQ_INSERT_BEFORE(ifdev2, ifdev, ifdev_next); return; } } } /* * The given device is not a loopback device, or there was no loopback * device in the list, possibly because it was empty. Add to the tail. */ TAILQ_INSERT_TAIL(&ifdev_list, ifdev, ifdev_next); } /* * Add and initialize an interface device. */ void ifdev_add(struct ifdev * ifdev, const char * name, unsigned int ifflags, unsigned int iftype, size_t hdrlen, size_t addrlen, unsigned int dlt, unsigned int mtu, uint32_t nd6flags, const struct ifdev_ops * iop) { unsigned int ifindex; ip4_addr_t ip4addr_any, ip4addr_none; /* * Since the call to netif_add() may end up invoking some of our * callbacks (the add-multicast-address ones in particular), make sure * that everything else is set up first. We cannot set up the index * mapping until netif_add() returns, but this is currently no problem. */ strlcpy(ifdev->ifdev_name, name, sizeof(ifdev->ifdev_name)); ifdev->ifdev_ifflags = 0; /* will be updated below */ ifdev->ifdev_dlt = dlt; ifdev->ifdev_nd6flags = nd6flags; ifdev->ifdev_ops = iop; memset(&ifdev->ifdev_data, 0, sizeof(ifdev->ifdev_data)); assert(addrlen <= NETIF_MAX_HWADDR_LEN); assert(mtu >= IFDEV_MIN_MTU && mtu <= UINT16_MAX); ifdev->ifdev_data.ifi_type = iftype; ifdev->ifdev_data.ifi_hdrlen = hdrlen; ifdev->ifdev_data.ifi_addrlen = addrlen; ifdev->ifdev_data.ifi_link_state = LINK_STATE_UNKNOWN; ifdev->ifdev_data.ifi_mtu = mtu; TAILQ_INIT(&ifdev->ifdev_bpf); ifaddr_init(ifdev); /* * We have to assign an IPv4 address at netif addition time, but we may * not have one yet, so pass in an "any" address for now. Hopefully * lwIP will not mistake this for a real IPv4 address if we happen to * enable the interface with only an IPv6 address later on. */ ip4_addr_set_any(&ip4addr_any); ip4_addr_set_u32(&ip4addr_none, PP_HTONL(INADDR_NONE)); /* * Insert the new interface device into a sensible place in the current * list of interfaces. */ ifdev_insert(ifdev); /* * netif_add() can fail only as a result of the initialization callback * failing, which is something that should never happen in our case. */ if (netif_add(&ifdev->ifdev_netif, &ip4addr_any, &ip4addr_none, &ip4addr_any, ifdev, ifdev_init_netif, iop->iop_input) == NULL) panic("unable to add netif"); /* * Set up the index mapping. Since interface index zero never * generated, table slot zero is always NULL. We could shift all * elements by one to save four bytes, but there's no real point. */ ifindex = netif_get_index(&ifdev->ifdev_netif); if (ifindex == 0 || ifindex >= __arraycount(ifdev_table)) panic("invalid lwIP-generated interface index %u", ifindex); ifdev_table[ifindex] = ifdev; /* * Set the initial interface flags. Use the regular procedure for this * just in case the interface module is crazy enough to set the * interface up right away (which is never a good idea but still). */ ifdev_update_ifflags(ifdev, ifflags); /* * If this is the first loopback interface to be registered, save it as * the loopback interface that we will use to loop back self-destined * packets on other interfaces. Do this after setting the interface * flags, since those are what we use to perform this loopback check. */ if (ifdev_loopback == NULL && ifdev_is_loopback(ifdev)) ifdev_loopback = ifdev; /* Finally, announce the new interface. */ rtsock_msg_ifannounce(ifdev, TRUE /*arrival*/); } /* * Remove an interface device. Return OK on success, or a negative error code * on failure. Only loopback interfaces may be refused for removal. */ int ifdev_remove(struct ifdev * ifdev) { struct bpfdev_link *bpfl; /* * If this is the loopback interface used to loop back packets for * other interfaces (typically lo0), we cannot afford to get rid of it. */ if (ifdev == ifdev_loopback) return EPERM; /* * Take down the interface for the purpose of sending a routing * message. NetBSD sends a RTM_IFINFO even if the interface was down * already, and so we do not check whether IFF_UP was set at all here. */ ifdev_update_ifflags(ifdev, ifdev->ifdev_ifflags & ~IFF_UP); /* * Report all associated addresses as deleted. It is not necessary to * actually delete the addresses, nor is that even possible in all * cases. In particular, the active hardware address cannot be * deleted. Since the active hardware address is used in all address * change announcements, delete it at the very end. */ ifaddr_v4_clear(ifdev); ifaddr_v6_clear(ifdev); ifaddr_dl_clear(ifdev); /* * Delete all remaining routes associated with the interface. These * are reported as well. We do this after clearing the addresses so as * not to confuse the route deletion part of clearing addresses. */ route_clear(ifdev); /* Finally, announce the interface itself as gone. */ rtsock_msg_ifannounce(ifdev, FALSE /*arrival*/); /* * Free up all per-socket multicast membership structures associated to * the interface. There is no need to leave the multicast groups. */ mcast_clear(ifdev); /* * Also tell attached BPF devices that the interface is now gone. Do * not bother to reset the list. */ TAILQ_FOREACH(bpfl, &ifdev->ifdev_bpf, bpfl_next) bpfdev_detach(bpfl); /* Then perform the actual interface removal. */ netif_remove(&ifdev->ifdev_netif); TAILQ_REMOVE(&ifdev_list, ifdev, ifdev_next); assert(ifdev_table[ifdev_get_index(ifdev)] == ifdev); ifdev_table[ifdev_get_index(ifdev)] = NULL; return OK; } /* * Return the loopback interface. */ struct ifdev * ifdev_get_loopback(void) { assert(ifdev_loopback != NULL); return ifdev_loopback; } /* * Report an update of the link state of the given interface, to 'unknown', * 'up', or 'down', using NetBSD's LINK_STATE_ values. The link state is * changed in the associated lwIP netif, and is reported on monitoring routing * sockets. This function is for use by interface modules, to update the * internal state to their current external state. */ void ifdev_update_link(struct ifdev * ifdev, int iflink) { struct netif *netif; int was_up, is_up; ifdev->ifdev_data.ifi_link_state = iflink; /* * For netif, 'up' and 'unknown' are the same link state: we simply try * to send and receive packets in both cases. Thus, transitions from * and to the 'down' link state are the ones that matter. */ netif = ifdev_get_netif(ifdev); was_up = netif_is_link_up(netif); is_up = (iflink != LINK_STATE_DOWN); if (was_up != is_up) { if (is_up) { netif_set_link_up(netif); /* See if we should also reset address states now. */ if (ifdev_is_up(ifdev)) ifaddr_v6_set_up(ifdev); } else netif_set_link_down(netif); rtsock_msg_ifinfo(ifdev); } } /* * Register a virtual interface type, using a name prefix and a function that * is called when creation of a virtual interface of that type is requested. */ void ifdev_register(const char * name, int (* create)(const char *)) { if (ifdev_vtypes == __arraycount(ifdev_vtype)) panic("too few slots for all virtual interface types"); ifdev_vtype[ifdev_vtypes].ifvt_name = name; ifdev_vtype[ifdev_vtypes].ifvt_namelen = strlen(name); ifdev_vtype[ifdev_vtypes].ifvt_create = create; ifdev_vtypes++; } /* * Verify that the given name is a valid interface name that can be used for * creating a new interface. In particular, check that the given name is a * valid interface name, consisting of an alphabetic string (the interface type * or driver name) followed by a number string (the unit or instance number). * Furthermore, make sure that the name does not already exist. Finally, see * if the name prefix is reserved for a virtual interface type. If the given * 'vtype_slot' pointer is not NULL, the prefix must be, and the virtual type * slot number is returned in 'vtype_slot' on success. If 'vtype_slot' is * NULL, the name must not have a virtual interface prefix, and an error is * returned if it is. Since vtype slot numbers are meaningless outside of this * module, external callers must always pass in NULL. This function returns OK * on succes or a negative error code on error. */ int ifdev_check_name(const char * name, unsigned int * vtype_slot) { const char *p; size_t namelen; unsigned int slot; /* * First see if the name is valid at all. TODO: decide if we want to * allow uppercase letters, dashes, and/or underscores. */ for (p = name; *p >= 'a' && *p <= 'z'; p++); if (p == name || *p == '\0') return EINVAL; namelen = (size_t)(p - name); for (; *p >= '0' && *p <= '9'; p++); if (*p != '\0') return EINVAL; /* Then make sure that it does not already exist. */ if (ifdev_find_by_name(name) != NULL) return EEXIST; /* See if there is a matching virtual interface type for the name. */ for (slot = 0; slot < ifdev_vtypes; slot++) { if (ifdev_vtype[slot].ifvt_namelen == namelen && !strncmp(ifdev_vtype[slot].ifvt_name, name, namelen)) break; } /* The interpretation of the result depends on 'vtype_slot'. */ if (vtype_slot != NULL) { if (slot == ifdev_vtypes) return EINVAL; *vtype_slot = slot; } else if (slot != ifdev_vtypes) return EINVAL; return OK; } /* * Create a new virtual interface. The virtual interface type is based on the * given name (without unit number). Return OK if the virtual interface has * been successfully created, or a negative error code otherwise. This * function is used both for the SIOCIFCREATE ioctl and internally. */ int ifdev_create(const char * name) { unsigned int slot; int r; /* Verify that the given name is an acceptable interface name. */ if ((r = ifdev_check_name(name, &slot)) != OK) return EINVAL; /* Let the virtual interface implementation handle the rest. */ return ifdev_vtype[slot].ifvt_create(name); } /* * Destroy an interface, if possible. */ int ifdev_destroy(struct ifdev * ifdev) { if (ifdev->ifdev_ops->iop_destroy == NULL) return EINVAL; return ifdev->ifdev_ops->iop_destroy(ifdev); } /* * Enumerate the names of currently supported virtual interface types. Return * a pointer to the null-terminated name prefix of the Nth virtual interface * type if the (zero-based) N value is within range, or NULL otherwise. */ const char * ifdev_enum_vtypes(unsigned int num) { if (num < ifdev_vtypes) return ifdev_vtype[num].ifvt_name; else return NULL; }