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$"); 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 IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp); 110 LLE_WLOCK_ASSERT(lle); 111 112 LIST_REMOVE(lle, lle_next); 113 lle->la_flags &= ~(LLE_VALID | LLE_LINKED); 114 115 pkts_dropped = 0; 116 while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) { 117 next = lle->la_hold->m_nextpkt; 118 m_freem(lle->la_hold); 119 lle->la_hold = next; 120 lle->la_numheld--; 121 pkts_dropped++; 122 } 123 124 KASSERT(lle->la_numheld == 0, 125 ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, 126 lle->la_numheld, pkts_dropped)); 127 128 LLE_FREE_LOCKED(lle); 129 130 return (pkts_dropped); 131} 132 133/* 134 * (al)locate an llentry for address dst (equivalent to rtalloc for new-arp). 135 * 136 * If found the llentry * is returned referenced and unlocked. 137 */ 138struct llentry * 139llentry_alloc(struct ifnet *ifp, struct lltable *lt, 140 struct sockaddr_storage *dst) 141{ 142 struct llentry *la; 143 144 IF_AFDATA_RLOCK(ifp); 145 la = lla_lookup(lt, LLE_EXCLUSIVE, (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, (LLE_CREATE | LLE_EXCLUSIVE), 151 (struct sockaddr *)dst); 152 IF_AFDATA_WUNLOCK(ifp); 153 } 154 155 if (la != NULL) { 156 LLE_ADDREF(la); 157 LLE_WUNLOCK(la); 158 } 159 160 return (la); 161} 162 163/* 164 * Free all entries from given table and free itself. 165 */ 166void 167lltable_free(struct lltable *llt) 168{ 169 struct llentry *lle, *next; 170 int i; 171 172 KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); 173 174 LLTABLE_WLOCK(); 175 SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); 176 LLTABLE_WUNLOCK(); 177 178 IF_AFDATA_WLOCK(llt->llt_ifp); 179 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) { 180 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { 181 LLE_WLOCK(lle); 182 if (callout_stop(&lle->la_timer)) 183 LLE_REMREF(lle); 184 llentry_free(lle); 185 } 186 } 187 IF_AFDATA_WUNLOCK(llt->llt_ifp); 188 189 free(llt, M_LLTABLE); 190} 191 192#if 0 193void 194lltable_drain(int af) 195{ 196 struct lltable *llt; 197 struct llentry *lle; 198 register int i; 199 200 LLTABLE_RLOCK(); 201 SLIST_FOREACH(llt, &V_lltables, llt_link) { 202 if (llt->llt_af != af) 203 continue; 204 205 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 206 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 207 LLE_WLOCK(lle); 208 if (lle->la_hold) { 209 m_freem(lle->la_hold); 210 lle->la_hold = NULL; 211 } 212 LLE_WUNLOCK(lle); 213 } 214 } 215 } 216 LLTABLE_RUNLOCK(); 217} 218#endif 219 220void 221lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask, 222 u_int flags) 223{ 224 struct lltable *llt; 225 226 LLTABLE_RLOCK(); 227 SLIST_FOREACH(llt, &V_lltables, llt_link) { 228 if (llt->llt_af != af) 229 continue; 230 231 llt->llt_prefix_free(llt, prefix, mask, flags); 232 } 233 LLTABLE_RUNLOCK(); 234} 235 236 237 238/* 239 * Create a new lltable. 240 */ 241struct lltable * 242lltable_init(struct ifnet *ifp, int af) 243{ 244 struct lltable *llt; 245 register int i; 246 247 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK); 248 249 llt->llt_af = af; 250 llt->llt_ifp = ifp; 251 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) 252 LIST_INIT(&llt->lle_head[i]); 253 254 LLTABLE_WLOCK(); 255 SLIST_INSERT_HEAD(&V_lltables, llt, llt_link); 256 LLTABLE_WUNLOCK(); 257 258 return (llt); 259} 260 261/* 262 * Called in route_output when adding/deleting a route to an interface. 263 */ 264int 265lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) 266{ 267 struct sockaddr_dl *dl = 268 (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; 269 struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; 270 struct ifnet *ifp; 271 struct lltable *llt; 272 struct llentry *lle; 273 u_int laflags = 0, flags = 0; 274 int error = 0; 275 276 KASSERT(dl != NULL && dl->sdl_family == AF_LINK, 277 ("%s: invalid dl\n", __func__)); 278 279 ifp = ifnet_byindex(dl->sdl_index); 280 if (ifp == NULL) { 281 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", 282 __func__, dl->sdl_index); 283 return EINVAL; 284 } 285 286 switch (rtm->rtm_type) { 287 case RTM_ADD: 288 if (rtm->rtm_flags & RTF_ANNOUNCE) 289 flags |= LLE_PUB; 290 flags |= LLE_CREATE; 291 break; 292 293 case RTM_DELETE: 294 flags |= LLE_DELETE; 295 break; 296 297 case RTM_CHANGE: 298 break; 299 300 default: 301 return EINVAL; /* XXX not implemented yet */ 302 } 303 304 /* XXX linked list may be too expensive */ 305 LLTABLE_RLOCK(); 306 SLIST_FOREACH(llt, &V_lltables, llt_link) { 307 if (llt->llt_af == dst->sa_family && 308 llt->llt_ifp == ifp) 309 break; 310 } 311 LLTABLE_RUNLOCK(); 312 KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); 313 314 if (flags & LLE_CREATE) 315 flags |= LLE_EXCLUSIVE; 316 317 IF_AFDATA_LOCK(ifp); 318 lle = lla_lookup(llt, flags, dst); 319 IF_AFDATA_UNLOCK(ifp); 320 if (LLE_IS_VALID(lle)) { 321 if (flags & LLE_CREATE) { 322 /* 323 * If we delay the delete, then a subsequent 324 * "arp add" should look up this entry, reset the 325 * LLE_DELETED flag, and reset the expiration timer 326 */ 327 bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); 328 lle->la_flags |= (flags & LLE_PUB); 329 lle->la_flags |= LLE_VALID; 330 lle->la_flags &= ~LLE_DELETED; 331#ifdef INET6 332 /* 333 * ND6 334 */ 335 if (dst->sa_family == AF_INET6) 336 lle->ln_state = ND6_LLINFO_REACHABLE; 337#endif 338 /* 339 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST) 340 */ 341 342 if (rtm->rtm_rmx.rmx_expire == 0) { 343 lle->la_flags |= LLE_STATIC; 344 lle->la_expire = 0; 345 } else 346 lle->la_expire = rtm->rtm_rmx.rmx_expire; 347 laflags = lle->la_flags; 348 LLE_WUNLOCK(lle); 349#ifdef INET 350 /* gratuitous ARP */ 351 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) 352 arprequest(ifp, 353 &((struct sockaddr_in *)dst)->sin_addr, 354 &((struct sockaddr_in *)dst)->sin_addr, 355 (u_char *)LLADDR(dl)); 356#endif 357 } else { 358 if (flags & LLE_EXCLUSIVE) 359 LLE_WUNLOCK(lle); 360 else 361 LLE_RUNLOCK(lle); 362 } 363 } else if ((lle == NULL) && (flags & LLE_DELETE)) 364 error = EINVAL; 365 366 367 return (error); 368} 369 370static void 371vnet_lltable_init() 372{ 373 374 SLIST_INIT(&V_lltables); 375} 376VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST, 377 vnet_lltable_init, NULL); 378 379#ifdef DDB 380struct llentry_sa { 381 struct llentry base; 382 struct sockaddr l3_addr; 383}; 384 385static void 386llatbl_lle_show(struct llentry_sa *la) 387{ 388 struct llentry *lle; 389 uint8_t octet[6]; 390 391 lle = &la->base; 392 db_printf("lle=%p\n", lle); 393 db_printf(" lle_next=%p\n", lle->lle_next.le_next); 394 db_printf(" lle_lock=%p\n", &lle->lle_lock); 395 db_printf(" lle_tbl=%p\n", lle->lle_tbl); 396 db_printf(" lle_head=%p\n", lle->lle_head); 397 db_printf(" la_hold=%p\n", lle->la_hold); 398 db_printf(" la_numheld=%d\n", lle->la_numheld); 399 db_printf(" la_expire=%ju\n", (uintmax_t)lle->la_expire); 400 db_printf(" la_flags=0x%04x\n", lle->la_flags); 401 db_printf(" la_asked=%u\n", lle->la_asked); 402 db_printf(" la_preempt=%u\n", lle->la_preempt); 403 db_printf(" ln_byhint=%u\n", lle->ln_byhint); 404 db_printf(" ln_state=%d\n", lle->ln_state); 405 db_printf(" ln_router=%u\n", lle->ln_router); 406 db_printf(" ln_ntick=%ju\n", (uintmax_t)lle->ln_ntick); 407 db_printf(" lle_refcnt=%d\n", lle->lle_refcnt); 408 bcopy(&lle->ll_addr.mac16, octet, sizeof(octet)); 409 db_printf(" ll_addr=%02x:%02x:%02x:%02x:%02x:%02x\n", 410 octet[0], octet[1], octet[2], octet[3], octet[4], octet[5]); 411 db_printf(" la_timer=%p\n", &lle->la_timer); 412 413 switch (la->l3_addr.sa_family) { 414#ifdef INET 415 case AF_INET: 416 { 417 struct sockaddr_in *sin; 418 char l3s[INET_ADDRSTRLEN]; 419 420 sin = (struct sockaddr_in *)&la->l3_addr; 421 inet_ntoa_r(sin->sin_addr, l3s); 422 db_printf(" l3_addr=%s\n", l3s); 423 break; 424 } 425#endif 426#ifdef INET6 427 case AF_INET6: 428 { 429 struct sockaddr_in6 *sin6; 430 char l3s[INET6_ADDRSTRLEN]; 431 432 sin6 = (struct sockaddr_in6 *)&la->l3_addr; 433 ip6_sprintf(l3s, &sin6->sin6_addr); 434 db_printf(" l3_addr=%s\n", l3s); 435 break; 436 } 437#endif 438 default: 439 db_printf(" l3_addr=N/A (af=%d)\n", la->l3_addr.sa_family); 440 break; 441 } 442} 443 444DB_SHOW_COMMAND(llentry, db_show_llentry) 445{ 446 447 if (!have_addr) { 448 db_printf("usage: show llentry <struct llentry *>\n"); 449 return; 450 } 451 452 llatbl_lle_show((struct llentry_sa *)addr); 453} 454 455static void 456llatbl_llt_show(struct lltable *llt) 457{ 458 int i; 459 struct llentry *lle; 460 461 db_printf("llt=%p llt_af=%d llt_ifp=%p\n", 462 llt, llt->llt_af, llt->llt_ifp); 463 464 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) { 465 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 466 467 llatbl_lle_show((struct llentry_sa *)lle); 468 if (db_pager_quit) 469 return; 470 } 471 } 472} 473 474DB_SHOW_COMMAND(lltable, db_show_lltable) 475{ 476 477 if (!have_addr) { 478 db_printf("usage: show lltable <struct lltable *>\n"); 479 return; 480 } 481 482 llatbl_llt_show((struct lltable *)addr); 483} 484 485DB_SHOW_ALL_COMMAND(lltables, db_show_all_lltables) 486{ 487 VNET_ITERATOR_DECL(vnet_iter); 488 struct lltable *llt; 489 490 VNET_FOREACH(vnet_iter) { 491 CURVNET_SET_QUIET(vnet_iter); 492#ifdef VIMAGE 493 db_printf("vnet=%p\n", curvnet); 494#endif 495 SLIST_FOREACH(llt, &V_lltables, llt_link) { 496 db_printf("llt=%p llt_af=%d llt_ifp=%p(%s)\n", 497 llt, llt->llt_af, llt->llt_ifp, 498 (llt->llt_ifp != NULL) ? 499 llt->llt_ifp->if_xname : "?"); 500 if (have_addr && addr != 0) /* verbose */ 501 llatbl_llt_show(llt); 502 if (db_pager_quit) { 503 CURVNET_RESTORE(); 504 return; 505 } 506 } 507 CURVNET_RESTORE(); 508 } 509} 510#endif 511