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 #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 58 MALLOC_DEFINE(M_LLTABLE, "lltable", "link level address tables"); 59 60 static SLIST_HEAD(, lltable) lltables = SLIST_HEAD_INITIALIZER(lltables); 61 62 extern 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 */ 68 int 69 lltable_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 } 82 done: 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 */ 93 void 94 llentry_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 106 /* 107 * Free all entries from given table and free itself. 108 * Since lltables collects from all of the intefaces, 109 * the caller of this function must acquire IFNET_WLOCK(). 110 */ 111 void 112 lltable_free(struct lltable *llt) 113 { 114 struct llentry *lle, *next; 115 int i; 116 117 KASSERT(llt != NULL, ("%s: llt is NULL", __func__)); 118 119 IFNET_WLOCK(); 120 SLIST_REMOVE(&lltables, llt, lltable, llt_link); 121 IFNET_WUNLOCK(); 122 123 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 124 LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { 125 126 callout_drain(&lle->la_timer); 127 LLE_WLOCK(lle); 128 llentry_free(lle); 129 } 130 } 131 132 free(llt, M_LLTABLE); 133 } 134 135 void 136 lltable_drain(int af) 137 { 138 struct lltable *llt; 139 struct llentry *lle; 140 register int i; 141 142 IFNET_RLOCK(); 143 SLIST_FOREACH(llt, &lltables, llt_link) { 144 if (llt->llt_af != af) 145 continue; 146 147 for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { 148 LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { 149 if (lle->la_hold) { 150 m_freem(lle->la_hold); 151 lle->la_hold = NULL; 152 } 153 } 154 } 155 } 156 IFNET_RUNLOCK(); 157 } 158 159 /* 160 * Create a new lltable. 161 */ 162 struct lltable * 163 lltable_init(struct ifnet *ifp, int af) 164 { 165 struct lltable *llt; 166 register int i; 167 168 llt = malloc(sizeof(struct lltable), M_LLTABLE, M_WAITOK); 169 if (llt == NULL) 170 return (NULL); 171 172 llt->llt_af = af; 173 llt->llt_ifp = ifp; 174 for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) 175 LIST_INIT(&llt->lle_head[i]); 176 177 IFNET_WLOCK(); 178 SLIST_INSERT_HEAD(&lltables, llt, llt_link); 179 IFNET_WUNLOCK(); 180 181 return (llt); 182 } 183 184 /* 185 * Called in route_output when adding/deleting a route to an interface. 186 */ 187 int 188 lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) 189 { 190 struct sockaddr_dl *dl = 191 (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; 192 struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST]; 193 struct ifnet *ifp; 194 struct lltable *llt; 195 struct llentry *lle; 196 u_int laflags = 0, flags = 0; 197 int error = 0; 198 199 if (dl == NULL || dl->sdl_family != AF_LINK) { 200 log(LOG_INFO, "%s: invalid dl\n", __func__); 201 return EINVAL; 202 } 203 ifp = ifnet_byindex(dl->sdl_index); 204 if (ifp == NULL) { 205 log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", 206 __func__, dl->sdl_index); 207 return EINVAL; 208 } 209 210 switch (rtm->rtm_type) { 211 case RTM_ADD: 212 if (rtm->rtm_flags & RTF_ANNOUNCE) { 213 flags |= LLE_PUB; 214 #ifdef INET 215 if (dst->sa_family == AF_INET && 216 ((struct sockaddr_inarp *)dst)->sin_other != 0) { 217 struct rtentry *rt = rtalloc1(dst, 0, 0); 218 if (rt == NULL || !(rt->rt_flags & RTF_HOST)) { 219 log(LOG_INFO, "%s: RTM_ADD publish " 220 "(proxy only) is invalid\n", 221 __func__); 222 if (rt) 223 RTFREE_LOCKED(rt); 224 return EINVAL; 225 } 226 RTFREE_LOCKED(rt); 227 228 flags |= LLE_PROXY; 229 } 230 #endif 231 } 232 flags |= LLE_CREATE; 233 break; 234 235 case RTM_DELETE: 236 flags |= LLE_DELETE; 237 break; 238 239 case RTM_CHANGE: 240 break; 241 242 default: 243 return EINVAL; /* XXX not implemented yet */ 244 } 245 246 /* XXX linked list may be too expensive */ 247 IFNET_RLOCK(); 248 SLIST_FOREACH(llt, &lltables, llt_link) { 249 if (llt->llt_af == dst->sa_family && 250 llt->llt_ifp == ifp) 251 break; 252 } 253 IFNET_RUNLOCK(); 254 KASSERT(llt != NULL, ("Yep, ugly hacks are bad\n")); 255 256 if (flags && LLE_CREATE) 257 flags |= LLE_EXCLUSIVE; 258 259 IF_AFDATA_LOCK(ifp); 260 lle = lla_lookup(llt, flags, dst); 261 IF_AFDATA_UNLOCK(ifp); 262 if (LLE_IS_VALID(lle)) { 263 if (flags & LLE_CREATE) { 264 /* 265 * If we delay the delete, then a subsequent 266 * "arp add" should look up this entry, reset the 267 * LLE_DELETED flag, and reset the expiration timer 268 */ 269 bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen); 270 lle->la_flags |= LLE_VALID; 271 lle->la_flags &= ~LLE_DELETED; 272 #ifdef INET6 273 /* 274 * ND6 275 */ 276 if (dst->sa_family == AF_INET6) 277 lle->ln_state = ND6_LLINFO_REACHABLE; 278 #endif 279 /* 280 * NB: arp and ndp always set (RTF_STATIC | RTF_HOST) 281 */ 282 283 if (rtm->rtm_rmx.rmx_expire == 0) { 284 lle->la_flags |= LLE_STATIC; 285 lle->la_expire = 0; 286 } else 287 lle->la_expire = rtm->rtm_rmx.rmx_expire; 288 laflags = lle->la_flags; 289 LLE_WUNLOCK(lle); 290 #ifdef INET 291 /* gratuitous ARP */ 292 if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) { 293 arprequest(ifp, 294 &((struct sockaddr_in *)dst)->sin_addr, 295 &((struct sockaddr_in *)dst)->sin_addr, 296 ((laflags & LLE_PROXY) ? 297 (u_char *)IF_LLADDR(ifp) : 298 (u_char *)LLADDR(dl))); 299 } 300 #endif 301 } else { 302 if (flags & LLE_EXCLUSIVE) 303 LLE_WUNLOCK(lle); 304 else 305 LLE_RUNLOCK(lle); 306 } 307 } else if ((lle == NULL) && (flags & LLE_DELETE)) 308 error = EINVAL; 309 310 311 return (error); 312 } 313