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. 8 * 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. 12 * 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. 19 * 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. 27 * 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. 36 */ 37 38 #include "lwip.h" 39 #include "lldata.h" 40 #include "route.h" 41 #include "rtsock.h" 42 43 #include "lwip/etharp.h" 44 #include "lwip/nd6.h" 45 #include "lwip/priv/nd6_priv.h" /* for neighbor_cache */ 46 47 /* 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. 52 */ 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) 57 { 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; 63 64 netif = (ifdev != NULL) ? ifdev_get_netif(ifdev) : NULL; 65 66 num = etharp_find_addr(netif, ip_2_ip4(dst_addr), ðptr, &ip4addr); 67 68 if (type != RTM_ADD && num < 0) 69 return ESRCH; 70 else if (type == RTM_ADD && num >= 0) 71 return EEXIST; 72 73 switch (type) { 74 case RTM_CHANGE: 75 /* 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. 81 */ 82 if (gw_addr == NULL) 83 gw_addr = ethptr; 84 85 if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK) 86 return EPERM; 87 88 /* FALLTHROUGH */ 89 case RTM_ADD: 90 assert(gw_addr != NULL); 91 92 memcpy(ðaddr, gw_addr, sizeof(ethaddr)); 93 94 /* 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. 99 */ 100 if ((err = etharp_add_static_entry(ip_2_ip4(dst_addr), 101 ðaddr)) != ERR_OK) 102 return util_convert_err(err); 103 104 if ((num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr), 105 ðptr, &ip4addr)) < 0) 106 panic("unable to find just-added static ARP entry"); 107 108 /* FALLTHROUGH */ 109 case RTM_LOCK: 110 case RTM_GET: 111 rtsock_msg_arp(num, type, rtr); 112 113 return OK; 114 115 case RTM_DELETE: 116 memcpy(ðaddr, ethptr, sizeof(ethaddr)); 117 118 if (etharp_remove_static_entry(ip_2_ip4(dst_addr)) != ERR_OK) 119 return EPERM; 120 121 /* 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. 130 */ 131 (void)etharp_add_static_entry(ip_2_ip4(dst_addr), ðaddr); 132 133 num = etharp_find_addr(NULL /*netif*/, ip_2_ip4(dst_addr), 134 ðptr, &ip4addr); 135 assert(num >= 0); 136 137 rtsock_msg_arp(num, type, rtr); 138 139 (void)etharp_remove_static_entry(ip_2_ip4(dst_addr)); 140 141 return OK; 142 143 default: 144 return EINVAL; 145 } 146 } 147 148 /* 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. 153 */ 154 int 155 lldata_arp_enum(lldata_arp_num_t * num) 156 { 157 ip4_addr_t *ip4addr; 158 struct netif *netif; 159 struct eth_addr *ethaddr; 160 161 for (; *num < ARP_TABLE_SIZE; ++*num) { 162 if (etharp_get_entry(*num, &ip4addr, &netif, ðaddr)) 163 return TRUE; 164 } 165 166 return FALSE; 167 } 168 169 /* 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'. 174 */ 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) 179 { 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; 186 187 if (!etharp_get_entry(num, &ip4addr, &netif, ðaddr)) 188 panic("request for invalid ARP entry"); 189 190 ip_addr_copy_from_ip4(ipaddr, *ip4addr); 191 192 assert(netif != NULL); 193 ifdev = netif_get_ifdev(netif); 194 195 addr_len = sizeof(*addr); 196 197 addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr, 198 TRUE /*kame*/, 0 /*port*/); 199 200 addr_len = sizeof(*gateway); 201 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)); 205 206 *ifdevp = ifdev; 207 208 /* 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.. 211 */ 212 *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC | RTF_CLONED; 213 } 214 215 /* 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. 230 * 231 * TODO: upstream this function to lwIP. 232 */ 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) 237 { 238 239 if (i < 0 || i >= LWIP_ND6_NUM_NEIGHBORS || 240 neighbor_cache[i].state == ND6_NO_ENTRY) 241 return NULL; 242 243 if (netif != NULL) 244 *netif = neighbor_cache[i].netif; 245 246 if (lladdr != NULL) { 247 if (neighbor_cache[i].state != ND6_INCOMPLETE) 248 *lladdr = neighbor_cache[i].lladdr; 249 else 250 *lladdr = NULL; 251 } 252 253 if (state != NULL) 254 *state = neighbor_cache[i].state; 255 256 if (isrouter != NULL) 257 *isrouter = neighbor_cache[i].isrouter; 258 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; 265 } 266 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; 288 } 289 } 290 291 return &neighbor_cache[i].next_hop_address; 292 } 293 294 /* 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. 298 * 299 * TODO: make this function public in lwIP. 300 */ 301 static int8_t 302 nd6_find_neighbor_cache_entry(const ip6_addr_t * addr) 303 { 304 int8_t i; 305 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; 309 } 310 311 return -1; 312 } 313 314 /* 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. 318 */ 319 int 320 lldata_ndp_find(struct ifdev * ifdev, const struct sockaddr_in6 * addr, 321 lldata_ndp_num_t * nump) 322 { 323 ip_addr_t ipaddr; 324 int8_t i; 325 int r; 326 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; 330 331 /* 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. 335 */ 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)); 339 340 i = nd6_find_neighbor_cache_entry(ip_2_ip6(&ipaddr)); 341 if (i < 0) 342 return ESRCH; 343 344 /* 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. 349 */ 350 351 *nump = (lldata_ndp_num_t)i; 352 return OK; 353 } 354 355 /* 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. 360 */ 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) 366 { 367 lldata_ndp_num_t num; 368 369 num = (lldata_ndp_num_t) 370 nd6_find_neighbor_cache_entry(ip_2_ip6(dst_addr)); 371 372 if (type != RTM_ADD && num < 0) 373 return ESRCH; 374 else if (type == RTM_ADD && num >= 0) 375 return EEXIST; 376 377 switch (type) { 378 case RTM_LOCK: 379 case RTM_GET: 380 rtsock_msg_arp(num, type, rtr); 381 382 return OK; 383 384 case RTM_ADD: 385 case RTM_CHANGE: 386 case RTM_DELETE: 387 /* TODO: add lwIP support to implement these commands. */ 388 return ENOSYS; 389 390 default: 391 return EINVAL; 392 } 393 } 394 395 /* 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. 400 */ 401 int 402 lldata_ndp_enum(lldata_ndp_num_t * num) 403 { 404 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; 410 } 411 412 return FALSE; 413 } 414 415 /* 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'. 420 */ 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) 425 { 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; 432 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); 437 438 ip_addr_copy_from_ip6(ipaddr, *ip6addr); 439 440 ifdev = netif_get_ifdev(netif); 441 assert(ifdev != NULL); 442 443 addr_len = sizeof(*addr); 444 445 addr_put_inet((struct sockaddr *)addr, &addr_len, &ipaddr, 446 TRUE /*kame*/, 0 /*port*/); 447 448 addr_len = sizeof(*gateway); 449 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)); 453 454 *ifdevp = ifdev; 455 *flagsp = RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_CLONED; 456 } 457 458 /* 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. 466 */ 467 void 468 lldata_ndp_get_info(lldata_ndp_num_t num, long * asked, int * isrouter, 469 int * state, int * expire) 470 { 471 uint32_t nd6_probes_sent = 0 /*gcc*/, nd6_expire_time = 0 /*gcc*/; 472 uint8_t nd6_state = 0 /*gcc*/, nd6_isrouter = 0 /*gcc*/; 473 474 (void)nd6_get_neighbor_cache_entry(num, NULL /*netif*/, 475 NULL /*lladdr*/, &nd6_state, &nd6_isrouter, &nd6_probes_sent, 476 &nd6_expire_time); 477 478 *asked = (long)nd6_probes_sent; 479 480 *isrouter = !!nd6_isrouter; 481 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); 489 } 490 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; 496 } 497 498 /* 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(). 510 */ 511 int 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) 515 { 516 const struct route_entry *route; 517 struct eth_addr ethaddr, *gw_addr; 518 int r; 519 520 assert(flags & RTF_LLDATA); 521 522 /* 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. 526 */ 527 if ((flags & ~(RTF_HOST | RTF_LLINFO | RTF_LLDATA | RTF_STATIC | 528 RTF_CLONED | RTF_ANNOUNCE)) != 0) 529 return EINVAL; 530 531 gw_addr = NULL; 532 533 if (type == RTM_ADD || type == RTM_CHANGE) { 534 /* 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. 538 */ 539 if ((type == RTM_ADD || type == RTM_CHANGE) && 540 !(flags & RTF_HOST)) 541 return EINVAL; 542 543 /* lwIP does not support publishing custom entries. */ 544 if (flags & RTF_ANNOUNCE) 545 return ENOSYS; 546 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; 553 554 gw_addr = ðaddr; 555 } 556 557 if (type == RTM_ADD) { 558 if (gateway == NULL) 559 return EINVAL; 560 561 /* 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. 567 */ 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; 574 } 575 } 576 } 577 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); 584 } 585