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_inet.h" 31 #include "opt_inet6.h" 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/malloc.h> 36 #include <sys/mbuf.h> 37 #include <sys/syslog.h> 38 #include <sys/sysctl.h> 39 #include <sys/socket.h> 40 #include <sys/kernel.h> 41 #include <sys/lock.h> 42 #include <sys/mutex.h> 43 #include <sys/rwlock.h> 44 45 #include <vm/uma.h> 46 47 #include <netinet/in.h> 48 #include <net/if_llatbl.h> 49 #include <net/if.h> 50 #include <net/if_dl.h> 51 #include <net/if_var.h> 52 #include <net/route.h> 53 #include <net/vnet.h> 54 #include <netinet/if_ether.h> 55 #include <netinet6/in6_var.h> 56 #include <netinet6/nd6.h> 57 58 MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables"); 59 60 static VNET_DEFINE(SLIST_HEAD(, lltable), lltables); 61 #define V_lltables VNET(lltables) 62 63 extern void arprequest(struct ifnet *, struct in_addr *, struct in_addr *, 64 u_char *); 65 66 static void vnet_lltable_init(void); 67 68 struct rwlock lltable_rwlock; 69 RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock"); 70 71 /* 72 * Dump arp state for a specific address family. 73 */ 74 int 75 lltable_sysctl_dumparp(int af, struct sysctl_req *wr) 76 { 77 struct lltable *llt; 78 int error = 0; 79 80 LLTABLE_RLOCK(); 81 SLIST_FOREACH(llt, &V_lltables, llt_link) { 82 if (llt->llt_af == af) { 83 error = llt->llt_dump(llt, wr); 84 if (error != 0) 85 goto done; 86 } 87 } 88 done: 89 LLTABLE_RUNLOCK(); 90 return (error); 91 } 92 93 /* 94 * Deletes an address from the address table. 95 * This function is called by the timer functions 96 * such as arptimer() and nd6_llinfo_timer(), and 97 * the caller does the locking. 98 */ 99 void 100 llentry_free(struct llentry *lle) 101 { 102 103 LLE_WLOCK_ASSERT(lle); 104 LIST_REMOVE(lle, lle_next); 105 106 if (lle->la_hold != NULL) 107 m_freem(lle->la_hold); 108 109 LLE_FREE_LOCKED(lle); 110 } 111 112 /* 113 * Update an llentry for address dst (equivalent to rtalloc for new-arp) 114 * Caller must pass in a valid struct llentry * 115 * 116 * if found the llentry * is returned referenced and unlocked 117 */ 118 int 119 llentry_update(struct llentry **llep, struct lltable *lt, 120 struct sockaddr *dst, struct ifnet *ifp) 121 { 122 struct llentry *la; 123 124 IF_AFDATA_RLOCK(ifp); 125 la = lla_lookup(lt, LLE_EXCLUSIVE, 126 (struct sockaddr *)dst); 127 IF_AFDATA_RUNLOCK(ifp); 128 if ((la == NULL) && 129 (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { 130 IF_AFDATA_WLOCK(ifp); 131 la = lla_lookup(lt, 132 (LLE_CREATE | LLE_EXCLUSIVE), 133 (struct sockaddr *)dst); 134 IF_AFDATA_WUNLOCK(ifp); 135 } 136 if (la != NULL && (*llep != la)) { 137 if (*llep != NULL) 138 LLE_FREE(*llep); 139 LLE_ADDREF(la); 140 LLE_WUNLOCK(la); 141 *llep = la; 142 } else if (la != NULL) 143 LLE_WUNLOCK(la); 144 145 if (la == NULL) 146 return (ENOENT); 147 148 return (0); 149 } 150 151 /* 152 * Free all entries from given table and free itself. 153 */ 154 void 155 lltable_free(struct lltable *llt) 156 { 157 struct llentry *lle, *next; 158 int i; 159 160 KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); 161 162 LLTABLE_WLOCK(); 163 SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); 164 LLTABLE_WUNLOCK(); 165 166 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 167 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { 168 169 callout_drain(&lle->la_timer); 170 LLE_WLOCK(lle); 171 llentry_free(lle); 172 } 173 } 174 175 free(llt, M_LLTABLE); 176 } 177 178 void 179 lltable_drain(int af) 180 { 181 struct lltable *llt; 182 struct llentry *lle; 183 register int i; 184 185 LLTABLE_RLOCK(); 186 SLIST_FOREACH(llt, &V_lltables, llt_link) { 187 if (llt->llt_af != af) 188 continue; 189 190 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 191 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 192 if (lle->la_hold) { 193 m_freem(lle->la_hold); 194 lle->la_hold = NULL; 195 } 196 } 197 } 198 } 199 LLTABLE_RUNLOCK(); 200 } 201 202 void 203 lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask) 204 { 205 struct lltable *llt; 206 207 LLTABLE_RLOCK(); 208 SLIST_FOREACH(llt, &V_lltables, llt_link) { 209 if (llt->llt_af != af) 210 continue; 211 212 llt->llt_prefix_free(llt, prefix, mask); 213 } 214 LLTABLE_RUNLOCK(); 215 } 216 217 218 219 /* 220 * Create a new lltable. 221 */ 222 struct lltable * 223 lltable_init(struct ifnet *ifp, int af) 224 { 225 struct lltable *llt; 226 register int i; 227 228 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK); 229 if (llt == NULL) 230 return (NULL); 231 232 llt->llt_af = af; 233 llt->llt_ifp = ifp; 234 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) 235 LIST_INIT(&llt->lle_head[i]); 236 237 LLTABLE_WLOCK(); 238 SLIST_INSERT_HEAD(&V_lltables, llt, llt_link); 239 LLTABLE_WUNLOCK(); 240 241 return (llt); 242 } 243 244 /* 245 * Called in route_output when adding/deleting a route to an interface. 246 */ 247 int 248 lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) 249 { 250 struct sockaddr_dl *dl = 251 (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; 252 struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; 253 struct ifnet *ifp; 254 struct lltable *llt; 255 struct llentry *lle; 256 u_int laflags = 0, flags = 0; 257 int error = 0; 258 259 if (dl == NULL || dl->sdl_family != AF_LINK) { 260 log(LOG_INFO, "%s: invalid dl\n", __func__); 261 return EINVAL; 262 } 263 ifp = ifnet_byindex(dl->sdl_index); 264 if (ifp == NULL) { 265 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", 266 __func__, dl->sdl_index); 267 return EINVAL; 268 } 269 270 switch (rtm->rtm_type) { 271 case RTM_ADD: 272 if (rtm->rtm_flags & RTF_ANNOUNCE) { 273 flags |= LLE_PUB; 274 #ifdef INET 275 if (dst->sa_family == AF_INET && 276 ((struct sockaddr_inarp *)dst)->sin_other != 0) { 277 struct rtentry *rt; 278 ((struct sockaddr_inarp *)dst)->sin_other = 0; 279 rt = rtalloc1(dst, 0, 0); 280 if (rt == NULL || !(rt->rt_flags & RTF_HOST)) { 281 log(LOG_INFO, "%s: RTM_ADD publish " 282 "(proxy only) is invalid\n", 283 __func__); 284 if (rt) 285 RTFREE_LOCKED(rt); 286 return EINVAL; 287 } 288 RTFREE_LOCKED(rt); 289 290 flags |= LLE_PROXY; 291 } 292 #endif 293 } 294 flags |= LLE_CREATE; 295 break; 296 297 case RTM_DELETE: 298 flags |= LLE_DELETE; 299 break; 300 301 case RTM_CHANGE: 302 break; 303 304 default: 305 return EINVAL; /* XXX not implemented yet */ 306 } 307 308 /* XXX linked list may be too expensive */ 309 LLTABLE_RLOCK(); 310 SLIST_FOREACH(llt, &V_lltables, llt_link) { 311 if (llt->llt_af == dst->sa_family && 312 llt->llt_ifp == ifp) 313 break; 314 } 315 LLTABLE_RUNLOCK(); 316 KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); 317 318 if (flags && LLE_CREATE) 319 flags |= LLE_EXCLUSIVE; 320 321 IF_AFDATA_LOCK(ifp); 322 lle = lla_lookup(llt, flags, dst); 323 IF_AFDATA_UNLOCK(ifp); 324 if (LLE_IS_VALID(lle)) { 325 if (flags & LLE_CREATE) { 326 /* 327 * If we delay the delete, then a subsequent 328 * "arp add" should look up this entry, reset the 329 * LLE_DELETED flag, and reset the expiration timer 330 */ 331 bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); 332 lle->la_flags |= LLE_VALID; 333 lle->la_flags &= ~LLE_DELETED; 334 #ifdef INET6 335 /* 336 * ND6 337 */ 338 if (dst->sa_family == AF_INET6) 339 lle->ln_state = ND6_LLINFO_REACHABLE; 340 #endif 341 /* 342 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST) 343 */ 344 345 if (rtm->rtm_rmx.rmx_expire == 0) { 346 lle->la_flags |= LLE_STATIC; 347 lle->la_expire = 0; 348 } else 349 lle->la_expire = rtm->rtm_rmx.rmx_expire; 350 laflags = lle->la_flags; 351 LLE_WUNLOCK(lle); 352 #ifdef INET 353 /* gratuitous ARP */ 354 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) { 355 arprequest(ifp, 356 &((struct sockaddr_in *)dst)->sin_addr, 357 &((struct sockaddr_in *)dst)->sin_addr, 358 ((laflags & LLE_PROXY) ? 359 (u_char *)IF_LLADDR(ifp) : 360 (u_char *)LLADDR(dl))); 361 } 362 #endif 363 } else { 364 if (flags & LLE_EXCLUSIVE) 365 LLE_WUNLOCK(lle); 366 else 367 LLE_RUNLOCK(lle); 368 } 369 } else if ((lle == NULL) && (flags & LLE_DELETE)) 370 error = EINVAL; 371 372 373 return (error); 374 } 375 376 static void 377 vnet_lltable_init() 378 { 379 380 SLIST_INIT(&V_lltables); 381 } 382 VNET_SYSINIT(vnet_lltable_init, SI_SUB_PSEUDO, SI_ORDER_FIRST, 383 vnet_lltable_init, NULL); 384 385