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