Remove building with NOCRYPTO option
[minix.git] / minix / net / lwip / lldata.c
blob3867c3985ba2ed9fa2f1a22ae15b10d58a13534d
1 /* LWIP service - lldata.c - link-layer (ARP, NDP) data related routines */
2 /*
3 * This module is largely isolated from the regular routing code. There are
4 * two reasons for that. First, mixing link-layer routes with regular routes
5 * would not work well due to the fact that lwIP keeps these data structures
6 * entirely separate. Second, as of version 8, NetBSD keeps the IP-layer and
7 * link-layer routing separate as well.
9 * Unfortunately, lwIP does not provide much in the way of implementing the
10 * functionality that would be expected for this module. As such, the current
11 * implementation is very restricted and simple.
13 * For ARP table entries, lwIP only allows for adding and deleting static
14 * entries. Non-static entries cannot be deleted. Incomplete (pending)
15 * entries cannot even be enumerated, nor can (e.g.) expiry information be
16 * obtained. The lwIP ARP datastructures are completely hidden, so there is no
17 * way to overcome these limitations without changing lwIP itself. As a
18 * result, not all functionality of the arp(8) userland utility is supported.
20 * For NDP table entries, lwIP offers no API at all. However, since the data
21 * structures are exposed directly, we can use those to implement full support
22 * for exposing information in a read-only way. However, manipulating data
23 * structures directly from here is too risky, nor does lwIP currently support
24 * the concept of static NDP table entries. Therefore, adding, changing, and
25 * deleting NDP entries is currently not supported, and will also first require
26 * changes to lwIP itself.
28 * The ndp(8) userland utility is also able to show and manipulate various
29 * other neighbor discovery related tables and settings. We support only a
30 * small subset of them. The main reason for this is that the other tables,
31 * in particular the prefix and default router lists, are not relevant: on
32 * MINIX 3, these are always managed fully in userland (usually dhcpcd(8)), and
33 * we even hardcode lwIP not to parse Router Advertisement messages at all, so
34 * even though those tables are still part of lwIP, they are always empty.
35 * Other ndp(8) functionality are unsupported for similar reasons.
38 #include "lwip.h"
39 #include "lldata.h"
40 #include "route.h"
41 #include "rtsock.h"
43 #include "lwip/etharp.h"
44 #include "lwip/nd6.h"
45 #include "lwip/priv/nd6_priv.h" /* for neighbor_cache */
48 * Process a routing command specifically for an ARP table entry. Return OK if
49 * the routing command has been processed successfully and a routing socket
50 * reply message has already been generated. Return a negative error code on
51 * failure, in which case the caller will generate a reply message instead.
53 static int
54 lldata_arp_process(unsigned int type, const ip_addr_t * dst_addr,
55 const struct eth_addr * gw_addr, struct ifdev * ifdev,
56 unsigned int flags, const struct rtsock_request * rtr)
58 const ip4_addr_t *ip4addr;
59 struct eth_addr ethaddr, *ethptr;
60 struct netif *netif;
61 lldata_arp_num_t num;
62 err_t err;
64 netif = (ifdev != NULL) ? ifdev_get_netif(ifdev) : NULL;
66 num = etharp_find_addr(netif, ip_2_ip4(dst_addr), &ethptr, &ip4addr);
68 if (type != RTM_ADD && num < 0)
69 return ESRCH;
70 else if (type == RTM_ADD && num >= 0)
71 return EEXIST;
73 switch (type) {
74 case RTM_CHANGE:
76 * This request is not used by arp(8), so keep things simple.
77 * For RTM_ADD we support only static entries; we support only
78 * those too here, and thus we can use delete-and-readd. If
79 * the ethernet address is not being changed, try readding the
80 * entry with the previous ethernet address.
82 if (gw_addr == NULL)
83 gw_addr = ethptr;
85 if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK)
86 return EPERM;
88 /* FALLTHROUGH */
89 case RTM_ADD:
90 assert(gw_addr != NULL);
92 memcpy(&ethaddr, gw_addr, sizeof(ethaddr));
95 * Adding static, permanent, unpublished, non-proxy entries is
96 * all that lwIP supports right now. We also do not get to
97 * specify the interface, and the way lwIP picks the interface
98 * may in fact result in a different one.
100 if ((err = etharp_add_static_entry(ip_2_ip4(dst_addr),
101 &ethaddr)) != ERR_OK)
102 return util_convert_err(err);
104 if ((num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr),
105 &ethptr, &ip4addr)) < 0)
106 panic("unable to find just-added static ARP entry");
108 /* FALLTHROUGH */
109 case RTM_LOCK:
110 case RTM_GET:
111 rtsock_msg_arp(num, type, rtr);
113 return OK;
115 case RTM_DELETE:
116 memcpy(&ethaddr, ethptr, sizeof(ethaddr));
118 if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK)
119 return EPERM;
122 * FIXME: the following block is a hack, because we cannot
123 * predict whether the above removal will succeed, while at the
124 * same time we need the entry to be present in order to report
125 * the deleted address to the routing socket. We temporarily
126 * readd and then remove the entry just for the purpose of
127 * generating the routing socket reply. There are other ways
128 * to resolve this, but only a better lwIP etharp API would
129 * allow us to resolve this problem cleanly.
131 (void)etharp_add_static_entry(ip_2_ip4(dst_addr), &ethaddr);
133 num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr),
134 &ethptr, &ip4addr);
135 assert(num >= 0);
137 rtsock_msg_arp(num, type, rtr);
139 (void)etharp_remove_static_entry(ip_2_ip4(dst_addr));
141 return OK;
143 default:
144 return EINVAL;
149 * Enumerate ARP table entries. Return TRUE if there is at least one more ARP
150 * table entry, of which the number is stored in 'num'. The caller should set
151 * 'num' to 0 initially, and increase it by one between a successful call and
152 * the next call. Return FALSE if there are no more ARP table entries.
155 lldata_arp_enum(lldata_arp_num_t * num)
157 ip4_addr_t *ip4addr;
158 struct netif *netif;
159 struct eth_addr *ethaddr;
161 for (; *num < ARP_TABLE_SIZE; ++*num) {
162 if (etharp_get_entry(*num, &ip4addr, &netif, &ethaddr))
163 return TRUE;
166 return FALSE;
170 * Obtain information about the ARP table entry identified by 'num'. The IPv4
171 * address of the entry is stored in 'addr'. Its ethernet address is stored in
172 * 'gateway'. The associated interface is stored in 'ifdevp', and the entry's
173 * routing flags (RTF_) are stored in 'flagsp'.
175 void
176 lldata_arp_get(lldata_arp_num_t num, struct sockaddr_in * addr,
177 struct sockaddr_dlx * gateway, struct ifdev ** ifdevp,
178 unsigned int * flagsp)
180 ip_addr_t ipaddr;
181 ip4_addr_t *ip4addr;
182 struct netif *netif;
183 struct ifdev *ifdev;
184 struct eth_addr *ethaddr;
185 socklen_t addr_len;
187 if (!etharp_get_entry(num, &ip4addr, &netif, &ethaddr))
188 panic("request for invalid ARP entry");
190 ip_addr_copy_from_ip4(ipaddr, *ip4addr);
192 assert(netif != NULL);
193 ifdev = netif_get_ifdev(netif);
195 addr_len = sizeof(*addr);
197 addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr,
198 TRUE /*kame*/, 0 /*port*/);
200 addr_len = sizeof(*gateway);
202 addr_put_link((struct sockaddr *)gateway, &addr_len,
203 ifdev_get_index(ifdev), ifdev_get_iftype(ifdev), NULL /*name*/,
204 ethaddr->addr, sizeof(ethaddr->addr));
206 *ifdevp = ifdev;
209 * TODO: this is not necessarily accurate, but lwIP does not provide us
210 * with information as to whether this is a static entry or not..
212 *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC | RTF_CLONED;
216 * Obtain information about the ND6 neighbor cache entry 'i', which must be a
217 * number between 0 (inclusive) and LWIP_ND6_NUM_NEIGHBORS (exclusive). If an
218 * entry with this number exists, return a pointer to its IPv6 address, and
219 * additional information in each of the given pointers if not NULL. The
220 * associated interface is stored in 'netif'. If the entry has an associated
221 * link-layer address, a pointer to it is stored in 'lladdr'. The entry's
222 * state (ND6_{INCOMPLETE,REACHABLE,STALE,DELAY,PROBE}) is stored in 'state'.
223 * The 'isrouter' parameter is filled with a boolean value indicating whether
224 * the entry is for a router. For ND6_INCOMPLETE and ND6_PROBE, the number of
225 * probes sent so far is stored in 'probes_sent'; for other states, the value
226 * is set to zero. For ND6_REACHABLE and ND6_DELAY, the time until expiration
227 * in ND6_TMR_INTERVAL-millisecond units is stored in 'expire_time'; for other
228 * states, the value is set to zero. If an entry with number 'i' does not
229 * exist, NULL is returned.
231 * TODO: upstream this function to lwIP.
233 static const ip6_addr_t *
234 nd6_get_neighbor_cache_entry(int8_t i, struct netif ** netif,
235 const uint8_t ** lladdr, uint8_t * state, uint8_t * isrouter,
236 uint32_t * probes_sent, uint32_t * expire_time)
239 if (i < 0 || i >= LWIP_ND6_NUM_NEIGHBORS ||
240 neighbor_cache[i].state == ND6_NO_ENTRY)
241 return NULL;
243 if (netif != NULL)
244 *netif = neighbor_cache[i].netif;
246 if (lladdr != NULL) {
247 if (neighbor_cache[i].state != ND6_INCOMPLETE)
248 *lladdr = neighbor_cache[i].lladdr;
249 else
250 *lladdr = NULL;
253 if (state != NULL)
254 *state = neighbor_cache[i].state;
256 if (isrouter != NULL)
257 *isrouter = neighbor_cache[i].isrouter;
259 if (probes_sent != NULL) {
260 if (neighbor_cache[i].state == ND6_INCOMPLETE ||
261 neighbor_cache[i].state == ND6_PROBE)
262 *probes_sent = neighbor_cache[i].counter.probes_sent;
263 else
264 *probes_sent = 0;
267 if (expire_time != NULL) {
268 switch (neighbor_cache[i].state) {
269 case ND6_REACHABLE:
270 *expire_time =
271 neighbor_cache[i].counter.reachable_time /
272 ND6_TMR_INTERVAL;
273 break;
274 case ND6_DELAY:
275 *expire_time = neighbor_cache[i].counter.delay_time;
276 break;
277 case ND6_INCOMPLETE:
278 case ND6_PROBE:
279 /* Probes are sent once per timer tick. */
280 *expire_time = (LWIP_ND6_MAX_MULTICAST_SOLICIT + 1 -
281 neighbor_cache[i].counter.probes_sent) *
282 (ND6_TMR_INTERVAL / 1000);
283 break;
284 default:
285 /* Stale entries do not expire; they get replaced. */
286 *expire_time = 0;
287 break;
291 return &neighbor_cache[i].next_hop_address;
295 * Find a neighbor cache entry by IPv6 address. Return its index number if
296 * found, or -1 if not. This is a reimplementation of the exact same function
297 * internal to lwIP.
299 * TODO: make this function public in lwIP.
301 static int8_t
302 nd6_find_neighbor_cache_entry(const ip6_addr_t * addr)
304 int8_t i;
306 for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
307 if (ip6_addr_cmp(addr, &neighbor_cache[i].next_hop_address))
308 return i;
311 return -1;
315 * Find an NDP table entry based on the given interface and IPv6 address. On
316 * success, return OK, with the entry's index number stored in 'nump'. On
317 * failure, return an appropriate error code.
320 lldata_ndp_find(struct ifdev * ifdev, const struct sockaddr_in6 * addr,
321 lldata_ndp_num_t * nump)
323 ip_addr_t ipaddr;
324 int8_t i;
325 int r;
327 if ((r = addr_get_inet((const struct sockaddr *)addr, sizeof(*addr),
328 IPADDR_TYPE_V6, &ipaddr, TRUE /*kame*/, NULL /*port*/)) != OK)
329 return r;
332 * For given link-local addresses, no zone may be provided in the
333 * address at all. In such cases, add the zone ourselves, using the
334 * given interface.
336 if (ip6_addr_lacks_zone(ip_2_ip6(&ipaddr), IP6_UNKNOWN))
337 ip6_addr_assign_zone(ip_2_ip6(&ipaddr), IP6_UNKNOWN,
338 ifdev_get_netif(ifdev));
340 i = nd6_find_neighbor_cache_entry(ip_2_ip6(&ipaddr));
341 if (i < 0)
342 return ESRCH;
345 * We should compare the neighbor cache entry's associated netif to
346 * the given ifdev, but since the lwIP neighbor cache is currently not
347 * keyed by netif anyway (i.e. the internal lookups are purely by IPv6
348 * address as well), doing so makes little sense in practice.
351 *nump = (lldata_ndp_num_t)i;
352 return OK;
356 * Process a routing command specifically for an NDP table entry. Return OK if
357 * the routing command has been processed successfully and a routing socket
358 * reply message has already been generated. Return a negative error code on
359 * failure, in which case the caller will generate a reply message instead.
361 static int
362 lldata_ndp_process(unsigned int type, const ip_addr_t * dst_addr,
363 const struct eth_addr * gw_addr,
364 struct ifdev * ifdev, unsigned int flags,
365 const struct rtsock_request * rtr)
367 lldata_ndp_num_t num;
369 num = (lldata_ndp_num_t)
370 nd6_find_neighbor_cache_entry(ip_2_ip6(dst_addr));
372 if (type != RTM_ADD && num < 0)
373 return ESRCH;
374 else if (type == RTM_ADD && num >= 0)
375 return EEXIST;
377 switch (type) {
378 case RTM_LOCK:
379 case RTM_GET:
380 rtsock_msg_arp(num, type, rtr);
382 return OK;
384 case RTM_ADD:
385 case RTM_CHANGE:
386 case RTM_DELETE:
387 /* TODO: add lwIP support to implement these commands. */
388 return ENOSYS;
390 default:
391 return EINVAL;
396 * Enumerate NDP table entries. Return TRUE if there is at least one more NDP
397 * table entry, of which the number is stored in 'num'. The caller should set
398 * 'num' to 0 initially, and increase it by one between a successful call and
399 * the next call. Return FALSE if there are no more NDP table entries.
402 lldata_ndp_enum(lldata_ndp_num_t * num)
405 for (; *num < LWIP_ND6_NUM_NEIGHBORS; ++*num) {
406 if (nd6_get_neighbor_cache_entry(*num, NULL /*netif*/,
407 NULL /*lladdr*/, NULL /*state*/, NULL /*isrouter*/,
408 NULL /*probes_sent*/, NULL /*expire_time*/) != NULL)
409 return TRUE;
412 return FALSE;
416 * Obtain information about the NDP table entry identified by 'num'. The IPv6
417 * address of the entry is stored in 'addr'. Its ethernet address is stored in
418 * 'gateway'. The associated interface is stored in 'ifdevp', and the entry's
419 * routing flags (RTF_) are stored in 'flagsp'.
421 void
422 lldata_ndp_get(lldata_ndp_num_t num, struct sockaddr_in6 * addr,
423 struct sockaddr_dlx * gateway, struct ifdev ** ifdevp,
424 unsigned int * flagsp)
426 const ip6_addr_t *ip6addr;
427 ip_addr_t ipaddr;
428 struct netif *netif = NULL /*gcc*/;
429 struct ifdev *ifdev;
430 const uint8_t *lladdr = NULL /*gcc*/;
431 socklen_t addr_len;
433 ip6addr = nd6_get_neighbor_cache_entry(num, &netif, &lladdr,
434 NULL /*state*/, NULL /*isrouter*/, NULL /*probes_sent*/,
435 NULL /*expire_time*/);
436 assert(ip6addr != NULL);
438 ip_addr_copy_from_ip6(ipaddr, *ip6addr);
440 ifdev = netif_get_ifdev(netif);
441 assert(ifdev != NULL);
443 addr_len = sizeof(*addr);
445 addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr,
446 TRUE /*kame*/, 0 /*port*/);
448 addr_len = sizeof(*gateway);
450 addr_put_link((struct sockaddr *)gateway, &addr_len,
451 ifdev_get_index(ifdev), ifdev_get_iftype(ifdev), NULL /*name*/,
452 lladdr, ifdev_get_hwlen(ifdev));
454 *ifdevp = ifdev;
455 *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_CLONED;
459 * Obtain information about the NDP table entry with the number 'num', which
460 * must be obtained through a previous call to lldata_ndp_find(). On return,
461 * 'asked' is filled with the number of probes sent so far (0 if inapplicable),
462 * 'isrouter' is set to 1 or 0 depending on whether the entry is for a router,
463 * 'state' is set to the entry's state (ND6_LLINFO_), and 'expire' is set to
464 * either the UNIX timestamp of expiry for the entry; 0 for permanent entries.
465 * None of the given pointers must be NULL. This function always succeeds.
467 void
468 lldata_ndp_get_info(lldata_ndp_num_t num, long * asked, int * isrouter,
469 int * state, int * expire)
471 uint32_t nd6_probes_sent = 0 /*gcc*/, nd6_expire_time = 0 /*gcc*/;
472 uint8_t nd6_state = 0 /*gcc*/, nd6_isrouter = 0 /*gcc*/;
474 (void)nd6_get_neighbor_cache_entry(num, NULL /*netif*/,
475 NULL /*lladdr*/, &nd6_state, &nd6_isrouter, &nd6_probes_sent,
476 &nd6_expire_time);
478 *asked = (long)nd6_probes_sent;
480 *isrouter = !!nd6_isrouter;
482 switch (nd6_state) {
483 case ND6_INCOMPLETE: *state = ND6_LLINFO_INCOMPLETE; break;
484 case ND6_REACHABLE: *state = ND6_LLINFO_REACHABLE; break;
485 case ND6_STALE: *state = ND6_LLINFO_STALE; break;
486 case ND6_DELAY: *state = ND6_LLINFO_DELAY; break;
487 case ND6_PROBE: *state = ND6_LLINFO_PROBE; break;
488 default: panic("unknown ND6 state %u", nd6_state);
491 if (nd6_expire_time != 0)
492 *expire = clock_time(NULL) +
493 (int)nd6_expire_time * (ND6_TMR_INTERVAL / 1000);
494 else
495 *expire = 0;
499 * Process a routing command specifically for a link-layer route, as one of the
500 * specific continuations of processing started by route_process(). The RTM_
501 * routing command is given as 'type'. The route destination is given as
502 * 'dst_addr'; its address type determines whether the operation is for ARP or
503 * NDP. The sockaddr structure for 'gateway' is passed on as is and may have
504 * to be parsed here if not NULL. 'ifdev' is the interface to be associated
505 * with the route; it is non-NULL only if an interface name (IFP) or address
506 * (IFA) was given. The RTF_ flags field has been checked against the globally
507 * supported flags, but may have to be checked for flags that do not apply to
508 * ARP/NDP routes. Return OK or a negative error code, following the same
509 * semantics as route_process().
512 lldata_process(unsigned int type, const ip_addr_t * dst_addr,
513 const struct sockaddr * gateway, struct ifdev * ifdev,
514 unsigned int flags, const struct rtsock_request * rtr)
516 const struct route_entry *route;
517 struct eth_addr ethaddr, *gw_addr;
518 int r;
520 assert(flags & RTF_LLDATA);
523 * It seems that RTF_UP does not apply to link-layer routing entries.
524 * We basically accept any flags that we can return, but we do not
525 * actually check most of them anywhere.
527 if ((flags & ~(RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC |
528 RTF_CLONED | RTF_ANNOUNCE)) != 0)
529 return EINVAL;
531 gw_addr = NULL;
533 if (type == RTM_ADD || type == RTM_CHANGE) {
535 * Link-layer entries are always host entries. Not all
536 * requests pass in this flag though, so check only when the
537 * flags are supposed to be set.
539 if ((type == RTM_ADD || type == RTM_CHANGE) &&
540 !(flags & RTF_HOST))
541 return EINVAL;
543 /* lwIP does not support publishing custom entries. */
544 if (flags & RTF_ANNOUNCE)
545 return ENOSYS;
547 /* RTF_GATEWAY is always cleared for link-layer entries. */
548 if (gateway != NULL) {
549 if ((r = addr_get_link(gateway, gateway->sa_len,
550 NULL /*name*/, 0 /*name_max*/, ethaddr.addr,
551 sizeof(ethaddr.addr))) != OK)
552 return r;
554 gw_addr = &ethaddr;
557 if (type == RTM_ADD) {
558 if (gateway == NULL)
559 return EINVAL;
562 * If no interface has been specified, see if the
563 * destination address is on a locally connected
564 * network. If so, use that network's interface.
565 * Otherwise reject the request altogether: we must
566 * have an interface to which to associate the entry.
568 if (ifdev == NULL) {
569 if ((route = route_lookup(dst_addr)) != NULL &&
570 !(route_get_flags(route) & RTF_GATEWAY))
571 ifdev = route_get_ifdev(route);
572 else
573 return ENETUNREACH;
578 if (IP_IS_V4(dst_addr))
579 return lldata_arp_process(type, dst_addr, gw_addr, ifdev,
580 flags, rtr);
581 else
582 return lldata_ndp_process(type, dst_addr, gw_addr, ifdev,
583 flags, rtr);