if_llatbl.c revision 238967
1/* 2 * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved. 3 * Copyright (c) 2004-2008 Qing Li. All rights reserved. 4 * Copyright (c) 2008 Kip Macy. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/net/if_llatbl.c 238967 2012-08-01 09:00:26Z glebius $"); 29 30#include "opt_ddb.h" 31#include "opt_inet.h" 32#include "opt_inet6.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/malloc.h> 37#include <sys/mbuf.h> 38#include <sys/syslog.h> 39#include <sys/sysctl.h> 40#include <sys/socket.h> 41#include <sys/kernel.h> 42#include <sys/lock.h> 43#include <sys/mutex.h> 44#include <sys/rwlock.h> 45 46#ifdef DDB 47#include <ddb/ddb.h> 48#endif 49 50#include <vm/uma.h> 51 52#include <netinet/in.h> 53#include <net/if_llatbl.h> 54#include <net/if.h> 55#include <net/if_dl.h> 56#include <net/if_var.h> 57#include <net/route.h> 58#include <net/vnet.h> 59#include <netinet/if_ether.h> 60#include <netinet6/in6_var.h> 61#include <netinet6/nd6.h> 62 63MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables"); 64 65static VNET_DEFINE(SLIST_HEAD(, lltable), lltables); 66#define V_lltables VNET(lltables) 67 68static void vnet_lltable_init(void); 69 70struct rwlock lltable_rwlock; 71RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock"); 72 73/* 74 * Dump arp state for a specific address family. 75 */ 76int 77lltable_sysctl_dumparp(int af, struct sysctl_req *wr) 78{ 79 struct lltable *llt; 80 int error = 0; 81 82 LLTABLE_RLOCK(); 83 SLIST_FOREACH(llt, &V_lltables, llt_link) { 84 if (llt->llt_af == af) { 85 error = llt->llt_dump(llt, wr); 86 if (error != 0) 87 goto done; 88 } 89 } 90done: 91 LLTABLE_RUNLOCK(); 92 return (error); 93} 94 95/* 96 * Deletes an address from the address table. 97 * This function is called by the timer functions 98 * such as arptimer() and nd6_llinfo_timer(), and 99 * the caller does the locking. 100 * 101 * Returns the number of held packets, if any, that were dropped. 102 */ 103size_t 104llentry_free(struct llentry *lle) 105{ 106 size_t pkts_dropped; 107 struct mbuf *next; 108 109 pkts_dropped = 0; 110 LLE_WLOCK_ASSERT(lle); 111 LIST_REMOVE(lle, lle_next); 112 113 while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) { 114 next = lle->la_hold->m_nextpkt; 115 m_freem(lle->la_hold); 116 lle->la_hold = next; 117 lle->la_numheld--; 118 pkts_dropped++; 119 } 120 121 KASSERT(lle->la_numheld == 0, 122 ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, 123 lle->la_numheld, pkts_dropped)); 124 125 lle->la_flags &= ~LLE_VALID; 126 LLE_FREE_LOCKED(lle); 127 128 return (pkts_dropped); 129} 130 131/* 132 * Update an llentry for address dst (equivalent to rtalloc for new-arp) 133 * Caller must pass in a valid struct llentry * (or NULL) 134 * 135 * if found the llentry * is returned referenced and unlocked 136 */ 137int 138llentry_update(struct llentry **llep, struct lltable *lt, 139 struct sockaddr_storage *dst, struct ifnet *ifp) 140{ 141 struct llentry *la; 142 143 IF_AFDATA_RLOCK(ifp); 144 la = lla_lookup(lt, LLE_EXCLUSIVE, 145 (struct sockaddr *)dst); 146 IF_AFDATA_RUNLOCK(ifp); 147 if ((la == NULL) && 148 (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { 149 IF_AFDATA_WLOCK(ifp); 150 la = lla_lookup(lt, 151 (LLE_CREATE | LLE_EXCLUSIVE), 152 (struct sockaddr *)dst); 153 IF_AFDATA_WUNLOCK(ifp); 154 } 155 if (la != NULL && (*llep != la)) { 156 if (*llep != NULL) 157 LLE_FREE(*llep); 158 LLE_ADDREF(la); 159 LLE_WUNLOCK(la); 160 *llep = la; 161 } else if (la != NULL) 162 LLE_WUNLOCK(la); 163 164 if (la == NULL) 165 return (ENOENT); 166 167 return (0); 168} 169 170/* 171 * Free all entries from given table and free itself. 172 */ 173void 174lltable_free(struct lltable *llt) 175{ 176 struct llentry *lle, *next; 177 int i; 178 179 KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); 180 181 LLTABLE_WLOCK(); 182 SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); 183 LLTABLE_WUNLOCK(); 184 185 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) { 186 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { 187 int canceled; 188 189 canceled = callout_drain(&lle->la_timer); 190 LLE_WLOCK(lle); 191 if (canceled) 192 LLE_REMREF(lle); 193 llentry_free(lle); 194 } 195 } 196 197 free(llt, M_LLTABLE); 198} 199 200#if 0 201void 202lltable_drain(int af) 203{ 204 struct lltable *llt; 205 struct llentry *lle; 206 register int i; 207 208 LLTABLE_RLOCK(); 209 SLIST_FOREACH(llt, &V_lltables, llt_link) { 210 if (llt->llt_af != af) 211 continue; 212 213 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 214 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 215 LLE_WLOCK(lle); 216 if (lle->la_hold) { 217 m_freem(lle->la_hold); 218 lle->la_hold = NULL; 219 } 220 LLE_WUNLOCK(lle); 221 } 222 } 223 } 224 LLTABLE_RUNLOCK(); 225} 226#endif 227 228void 229lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask, 230 u_int flags) 231{ 232 struct lltable *llt; 233 234 LLTABLE_RLOCK(); 235 SLIST_FOREACH(llt, &V_lltables, llt_link) { 236 if (llt->llt_af != af) 237 continue; 238 239 llt->llt_prefix_free(llt, prefix, mask, flags); 240 } 241 LLTABLE_RUNLOCK(); 242} 243 244 245 246/* 247 * Create a new lltable. 248 */ 249struct lltable * 250lltable_init(struct ifnet *ifp, int af) 251{ 252 struct lltable *llt; 253 register int i; 254 255 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK); 256 257 llt->llt_af = af; 258 llt->llt_ifp = ifp; 259 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) 260 LIST_INIT(&llt->lle_head[i]); 261 262 LLTABLE_WLOCK(); 263 SLIST_INSERT_HEAD(&V_lltables, llt, llt_link); 264 LLTABLE_WUNLOCK(); 265 266 return (llt); 267} 268 269/* 270 * Called in route_output when adding/deleting a route to an interface. 271 */ 272int 273lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) 274{ 275 struct sockaddr_dl *dl = 276 (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; 277 struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; 278 struct ifnet *ifp; 279 struct lltable *llt; 280 struct llentry *lle; 281 u_int laflags = 0, flags = 0; 282 int error = 0; 283 284 if (dl == NULL || dl->sdl_family != AF_LINK) { 285 log(LOG_INFO, "%s: invalid dl\n", __func__); 286 return EINVAL; 287 } 288 ifp = ifnet_byindex(dl->sdl_index); 289 if (ifp == NULL) { 290 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", 291 __func__, dl->sdl_index); 292 return EINVAL; 293 } 294 295 switch (rtm->rtm_type) { 296 case RTM_ADD: 297 if (rtm->rtm_flags & RTF_ANNOUNCE) { 298 flags |= LLE_PUB; 299#ifdef INET 300 if (dst->sa_family == AF_INET && 301 ((struct sockaddr_inarp *)dst)->sin_other != 0) { 302 struct rtentry *rt; 303 ((struct sockaddr_inarp *)dst)->sin_other = 0; 304 rt = rtalloc1(dst, 0, 0); 305 if (rt == NULL || !(rt->rt_flags & RTF_HOST)) { 306 log(LOG_INFO, "%s: RTM_ADD publish " 307 "(proxy only) is invalid\n", 308 __func__); 309 if (rt) 310 RTFREE_LOCKED(rt); 311 return EINVAL; 312 } 313 RTFREE_LOCKED(rt); 314 315 flags |= LLE_PROXY; 316 } 317#endif 318 } 319 flags |= LLE_CREATE; 320 break; 321 322 case RTM_DELETE: 323 flags |= LLE_DELETE; 324 break; 325 326 case RTM_CHANGE: 327 break; 328 329 default: 330 return EINVAL; /* XXX not implemented yet */ 331 } 332 333 /* XXX linked list may be too expensive */ 334 LLTABLE_RLOCK(); 335 SLIST_FOREACH(llt, &V_lltables, llt_link) { 336 if (llt->llt_af == dst->sa_family && 337 llt->llt_ifp == ifp) 338 break; 339 } 340 LLTABLE_RUNLOCK(); 341 KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); 342 343 if (flags & LLE_CREATE) 344 flags |= LLE_EXCLUSIVE; 345 346 IF_AFDATA_LOCK(ifp); 347 lle = lla_lookup(llt, flags, dst); 348 IF_AFDATA_UNLOCK(ifp); 349 if (LLE_IS_VALID(lle)) { 350 if (flags & LLE_CREATE) { 351 /* 352 * If we delay the delete, then a subsequent 353 * "arp add" should look up this entry, reset the 354 * LLE_DELETED flag, and reset the expiration timer 355 */ 356 bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); 357 lle->la_flags |= (flags & (LLE_PUB | LLE_PROXY)); 358 lle->la_flags |= LLE_VALID; 359 lle->la_flags &= ~LLE_DELETED; 360#ifdef INET6 361 /* 362 * ND6 363 */ 364 if (dst->sa_family == AF_INET6) 365 lle->ln_state = ND6_LLINFO_REACHABLE; 366#endif 367 /* 368 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST) 369 */ 370 371 if (rtm->rtm_rmx.rmx_expire == 0) { 372 lle->la_flags |= LLE_STATIC; 373 lle->la_expire = 0; 374 } else 375 lle->la_expire = rtm->rtm_rmx.rmx_expire; 376 laflags = lle->la_flags; 377 LLE_WUNLOCK(lle); 378#ifdef INET 379 /* gratuitous ARP */ 380 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) { 381 arprequest(ifp, 382 &((struct sockaddr_in *)dst)->sin_addr, 383 &((struct sockaddr_in *)dst)->sin_addr, 384 ((laflags & LLE_PROXY) ? 385 (u_char *)IF_LLADDR(ifp) : 386 (u_char *)LLADDR(dl))); 387 } 388#endif 389 } else { 390 if (flags & LLE_EXCLUSIVE) 391 LLE_WUNLOCK(lle); 392 else 393 LLE_RUNLOCK(lle); 394 } 395 } else if ((lle == NULL) && (flags & LLE_DELETE)) 396 error = EINVAL; 397 398 399 return (error); 400} 401 402static void 403vnet_lltable_init() 404{ 405 406 SLIST_INIT(&V_lltables); 407} 408VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST, 409 vnet_lltable_init, NULL); 410 411#ifdef DDB 412struct llentry_sa { 413 struct llentry base; 414 struct sockaddr l3_addr; 415}; 416 417static void 418llatbl_lle_show(struct llentry_sa *la) 419{ 420 struct llentry *lle; 421 uint8_t octet[6]; 422 423 lle = &la->base; 424 db_printf("lle=%p\n", lle); 425 db_printf(" lle_next=%p\n", lle->lle_next.le_next); 426 db_printf(" lle_lock=%p\n", &lle->lle_lock); 427 db_printf(" lle_tbl=%p\n", lle->lle_tbl); 428 db_printf(" lle_head=%p\n", lle->lle_head); 429 db_printf(" la_hold=%p\n", lle->la_hold); 430 db_printf(" la_numheld=%d\n", lle->la_numheld); 431 db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire); 432 db_printf(" la_flags=0x%04x\n", lle->la_flags); 433 db_printf(" la_asked=%u\n", lle->la_asked); 434 db_printf(" la_preempt=%u\n", lle->la_preempt); 435 db_printf(" ln_byhint=%u\n", lle->ln_byhint); 436 db_printf(" ln_state=%d\n", lle->ln_state); 437 db_printf(" ln_router=%u\n", lle->ln_router); 438 db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick); 439 db_printf(" lle_refcnt=%d\n", lle->lle_refcnt); 440 bcopy(&lle->ll_addr.mac16, octet, sizeof(octet)); 441 db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n", 442 octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]); 443 db_printf(" la_timer=%p\n", &lle->la_timer); 444 445 switch (la->l3_addr.sa_family) { 446#ifdef INET 447 case AF_INET: 448 { 449 struct sockaddr_in *sin; 450 char l3s[INET_ADDRSTRLEN]; 451 452 sin = (struct sockaddr_in *)&la->l3_addr; 453 inet_ntoa_r(sin->sin_addr, l3s); 454 db_printf(" l3_addr=%s\n", l3s); 455 break; 456 } 457#endif 458#ifdef INET6 459 case AF_INET6: 460 { 461 struct sockaddr_in6 *sin6; 462 char l3s[INET6_ADDRSTRLEN]; 463 464 sin6 = (struct sockaddr_in6 *)&la->l3_addr; 465 ip6_sprintf(l3s, &sin6->sin6_addr); 466 db_printf(" l3_addr=%s\n", l3s); 467 break; 468 } 469#endif 470 default: 471 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family); 472 break; 473 } 474} 475 476DB_SHOW_COMMAND(llentry, db_show_llentry) 477{ 478 479 if (!have_addr) { 480 db_printf("usage: show llentry <struct llentry *>\n"); 481 return; 482 } 483 484 llatbl_lle_show((struct llentry_sa *)addr); 485} 486 487static void 488llatbl_llt_show(struct lltable *llt) 489{ 490 int i; 491 struct llentry *lle; 492 493 db_printf("llt=%p llt_af=%d llt_ifp=%p\n", 494 llt, llt->llt_af, llt->llt_ifp); 495 496 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) { 497 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 498 499 llatbl_lle_show((struct llentry_sa *)lle); 500 if (db_pager_quit) 501 return; 502 } 503 } 504} 505 506DB_SHOW_COMMAND(lltable, db_show_lltable) 507{ 508 509 if (!have_addr) { 510 db_printf("usage: show lltable <struct lltable *>\n"); 511 return; 512 } 513 514 llatbl_llt_show((struct lltable *)addr); 515} 516 517DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables) 518{ 519 VNET_ITERATOR_DECL(vnet_iter); 520 struct lltable *llt; 521 522 VNET_FOREACH(vnet_iter) { 523 CURVNET_SET_QUIET(vnet_iter); 524#ifdef VIMAGE 525 db_printf("vnet=%p\n", curvnet); 526#endif 527 SLIST_FOREACH(llt, &V_lltables, llt_link) { 528 db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n", 529 llt, llt->llt_af, llt->llt_ifp, 530 (llt->llt_ifp != NULL) ? 531 llt->llt_ifp->if_xname : "?"); 532 if (have_addr && addr != 0) /* verbose */ 533 llatbl_llt_show(llt); 534 if (db_pager_quit) { 535 CURVNET_RESTORE(); 536 return; 537 } 538 } 539 CURVNET_RESTORE(); 540 } 541} 542#endif 543