1*66570633Sbluhm /* $OpenBSD: if_ether.c,v 1.268 2025/01/01 13:44:22 bluhm Exp $ */ 26a239809Sderaadt /* $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1982, 1986, 1988, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 1629295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt * 32df930be7Sderaadt * @(#)if_ether.c 8.1 (Berkeley) 6/10/93 33df930be7Sderaadt */ 34df930be7Sderaadt 35df930be7Sderaadt /* 36df930be7Sderaadt * Ethernet address resolution protocol. 37df930be7Sderaadt * TODO: 38df930be7Sderaadt * add "inuse/lock" bit (or ref. count) along with valid bit 39df930be7Sderaadt */ 40df930be7Sderaadt 41c05683a6Smcbride #include "carp.h" 42df930be7Sderaadt 43df930be7Sderaadt #include <sys/param.h> 44b72c4d42Sderaadt #include <sys/systm.h> 45df930be7Sderaadt #include <sys/mbuf.h> 46df930be7Sderaadt #include <sys/socket.h> 47638c25e3Stedu #include <sys/timeout.h> 48df930be7Sderaadt #include <sys/kernel.h> 49df930be7Sderaadt #include <sys/syslog.h> 50656dd4d4Smpi #include <sys/queue.h> 51656dd4d4Smpi #include <sys/pool.h> 52df930be7Sderaadt 53df930be7Sderaadt #include <net/if.h> 540deb6685Smpi #include <net/if_var.h> 55df930be7Sderaadt #include <net/if_dl.h> 56df930be7Sderaadt #include <net/route.h> 57b65f77f4Sitojun #include <net/if_types.h> 5898a920fdSdlg #include <net/netisr.h> 59df930be7Sderaadt 60df930be7Sderaadt #include <netinet/in.h> 61df930be7Sderaadt #include <netinet/in_var.h> 62df930be7Sderaadt #include <netinet/if_ether.h> 6304cb3f44Sbluhm #include <netinet/ip_var.h> 64c05683a6Smcbride #if NCARP > 0 65c05683a6Smcbride #include <netinet/ip_carp.h> 66c05683a6Smcbride #endif 67df930be7Sderaadt 685bdbb3c9Sbluhm /* 695bdbb3c9Sbluhm * Locks used to protect struct members in this file: 705bdbb3c9Sbluhm * a atomic operations 715bdbb3c9Sbluhm * I immutable after creation 725bdbb3c9Sbluhm * K kernel lock 735bdbb3c9Sbluhm * m arp mutex, needed when net lock is shared 745bdbb3c9Sbluhm * N net lock 755bdbb3c9Sbluhm */ 765bdbb3c9Sbluhm 77e4cf08b7Smpi struct llinfo_arp { 785bdbb3c9Sbluhm LIST_ENTRY(llinfo_arp) la_list; /* [mN] global arp_list */ 795bdbb3c9Sbluhm struct rtentry *la_rt; /* [I] backpointer to rtentry */ 804e550751Sbluhm struct mbuf_queue la_mq; /* packet hold queue */ 81fe07dcc8Sclaudio time_t la_refreshed; /* when was refresh sent */ 82fe07dcc8Sclaudio int la_asked; /* number of queries sent */ 83e4cf08b7Smpi }; 84e4cf08b7Smpi #define LA_HOLD_QUEUE 10 85e4cf08b7Smpi #define LA_HOLD_TOTAL 100 86e4cf08b7Smpi 87df930be7Sderaadt /* timer values */ 885bdbb3c9Sbluhm int arpt_prune = (5 * 60); /* [I] walk list every 5 minutes */ 895bdbb3c9Sbluhm int arpt_keep = (20 * 60); /* [a] once resolved, cache for 20 minutes */ 905bdbb3c9Sbluhm int arpt_down = 20; /* [a] once declared down, don't send for 20 secs */ 91df930be7Sderaadt 921a184782Sbluhm struct mbuf *arppullup(struct mbuf *m); 93c370e97fSmpi void arpinvalidate(struct rtentry *); 942ca83196Smpi void arptfree(struct rtentry *); 95c4071fd1Smillert void arptimer(void *); 96d2dce6aeSmpi struct rtentry *arplookup(struct in_addr *, int, int, unsigned int); 975a411d3cSmpi void in_arpinput(struct ifnet *, struct mbuf *); 985a411d3cSmpi void in_revarpinput(struct ifnet *, struct mbuf *); 99590a0433Smpi int arpcache(struct ifnet *, struct ether_arp *, struct rtentry *); 1003b27b39dSstsp void arpreply(struct ifnet *, struct mbuf *, struct in_addr *, uint8_t *, 1013b27b39dSstsp unsigned int); 102df930be7Sderaadt 103c709125fSmpi struct niqueue arpinq = NIQUEUE_INITIALIZER(50, NETISR_ARP); 104e758f4c8Sbluhm 105e758f4c8Sbluhm /* llinfo_arp live time, rt_llinfo and RTF_LLINFO are protected by arp_mtx */ 1065bdbb3c9Sbluhm struct mutex arp_mtx = MUTEX_INITIALIZER(IPL_SOFTNET); 107c709125fSmpi 10866a74f82Skn LIST_HEAD(, llinfo_arp) arp_list = 10966a74f82Skn LIST_HEAD_INITIALIZER(arp_list); /* [mN] list of llinfo_arp structures */ 1105bdbb3c9Sbluhm struct pool arp_pool; /* [I] pool for llinfo_arp structures */ 1115bdbb3c9Sbluhm int arp_maxtries = 5; /* [I] arp requests before set to rejected */ 112e2c34204Sbluhm unsigned int la_hold_total; /* [a] packets currently in the arp queue */ 113df930be7Sderaadt 114300480deSderaadt #ifdef NFSCLIENT 115df930be7Sderaadt /* revarp state */ 116300480deSderaadt struct in_addr revarp_myip, revarp_srvip; 117300480deSderaadt int revarp_finished; 118df5ed9e3Smpi unsigned int revarp_ifidx; 119300480deSderaadt #endif /* NFSCLIENT */ 120df930be7Sderaadt 121df930be7Sderaadt /* 122df930be7Sderaadt * Timeout routine. Age arp_tab entries periodically. 123df930be7Sderaadt */ 124f67b77b6Sart void 12520b3612bSdhill arptimer(void *arg) 126df930be7Sderaadt { 127632220b1Sbluhm struct timeout *to = arg; 1282a448840Sart struct llinfo_arp *la, *nla; 12941c068b6Sbluhm time_t uptime; 130df930be7Sderaadt 131aa28b9a6Smpi NET_LOCK(); 13241c068b6Sbluhm uptime = getuptime(); 133e15e1c8cSblambert timeout_add_sec(to, arpt_prune); 1345bdbb3c9Sbluhm /* Net lock is exclusive, no arp mutex needed for arp_list here. */ 1351d04c8c6Sbluhm LIST_FOREACH_SAFE(la, &arp_list, la_list, nla) { 13664aa4cc7Sitojun struct rtentry *rt = la->la_rt; 137df930be7Sderaadt 13841c068b6Sbluhm if (rt->rt_expire && rt->rt_expire < uptime) 1392ca83196Smpi arptfree(rt); /* timer has expired; clear */ 140df930be7Sderaadt } 141aa28b9a6Smpi NET_UNLOCK(); 142df930be7Sderaadt } 143df930be7Sderaadt 144df930be7Sderaadt void 145632220b1Sbluhm arpinit(void) 146df930be7Sderaadt { 1472a448840Sart static struct timeout arptimer_to; 1482a448840Sart 1491378bae2Sdlg pool_init(&arp_pool, sizeof(struct llinfo_arp), 0, 1501378bae2Sdlg IPL_SOFTNET, 0, "arp", NULL); 1512a448840Sart 152f7e8a207Sbluhm timeout_set_flags(&arptimer_to, arptimer, &arptimer_to, 153f7e8a207Sbluhm KCLOCK_NONE, TIMEOUT_PROC | TIMEOUT_MPSAFE); 154fe07dcc8Sclaudio timeout_add_sec(&arptimer_to, arpt_prune); 155df930be7Sderaadt } 156b65f77f4Sitojun 157632220b1Sbluhm void 158632220b1Sbluhm arp_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) 159632220b1Sbluhm { 160632220b1Sbluhm struct sockaddr *gate = rt->rt_gateway; 161e758f4c8Sbluhm struct llinfo_arp *la; 16241c068b6Sbluhm time_t uptime; 163632220b1Sbluhm 1645bdbb3c9Sbluhm NET_ASSERT_LOCKED(); 1655bdbb3c9Sbluhm 1664cb08838Sclaudio if (ISSET(rt->rt_flags, 1674cb08838Sclaudio RTF_GATEWAY|RTF_BROADCAST|RTF_MULTICAST|RTF_MPLS)) 168df930be7Sderaadt return; 169b65f77f4Sitojun 17041c068b6Sbluhm uptime = getuptime(); 171df930be7Sderaadt switch (req) { 172df930be7Sderaadt case RTM_ADD: 173fe07dcc8Sclaudio if (rt->rt_flags & RTF_CLONING) { 174fe07dcc8Sclaudio rt->rt_expire = 0; 175df930be7Sderaadt break; 176df930be7Sderaadt } 177e758f4c8Sbluhm if ((rt->rt_flags & RTF_LOCAL) && rt->rt_llinfo == NULL) 178fe07dcc8Sclaudio rt->rt_expire = 0; 179b386d12aSmpi /* 180b386d12aSmpi * Announce a new entry if requested or warn the user 181b386d12aSmpi * if another station has this IP address. 182b386d12aSmpi */ 183b386d12aSmpi if (rt->rt_flags & (RTF_ANNOUNCE|RTF_LOCAL)) 18486b61919Smpi arprequest(ifp, 185a961b778Smpi &satosin(rt_key(rt))->sin_addr.s_addr, 186a961b778Smpi &satosin(rt_key(rt))->sin_addr.s_addr, 187c7b7b779Sbluhm (u_char *)LLADDR(satosdl(gate))); 188df930be7Sderaadt /*FALLTHROUGH*/ 189df930be7Sderaadt case RTM_RESOLVE: 190df930be7Sderaadt if (gate->sa_family != AF_LINK || 191f8007c4eSmpi gate->sa_len < sizeof(struct sockaddr_dl)) { 19286b61919Smpi log(LOG_DEBUG, "%s: bad gateway value: %s\n", __func__, 19386b61919Smpi ifp->if_xname); 194df930be7Sderaadt break; 195df930be7Sderaadt } 196c7b7b779Sbluhm satosdl(gate)->sdl_type = ifp->if_type; 197c7b7b779Sbluhm satosdl(gate)->sdl_index = ifp->if_index; 198df930be7Sderaadt /* 199df930be7Sderaadt * Case 2: This route may come from cloning, or a manual route 200df930be7Sderaadt * add with a LL address. 201df930be7Sderaadt */ 202656dd4d4Smpi la = pool_get(&arp_pool, PR_NOWAIT | PR_ZERO); 2037371a15dStedu if (la == NULL) { 20493fbd125Sbluhm log(LOG_DEBUG, "%s: pool get failed\n", __func__); 205df930be7Sderaadt break; 206df930be7Sderaadt } 2075fe57d5dSdlg 208e758f4c8Sbluhm mtx_enter(&arp_mtx); 209e758f4c8Sbluhm if (rt->rt_llinfo != NULL) { 210e758f4c8Sbluhm /* we lost the race, another thread has entered it */ 211e758f4c8Sbluhm mtx_leave(&arp_mtx); 212e758f4c8Sbluhm pool_put(&arp_pool, la); 213e758f4c8Sbluhm break; 214e758f4c8Sbluhm } 2154e550751Sbluhm mq_init(&la->la_mq, LA_HOLD_QUEUE, IPL_SOFTNET); 216e758f4c8Sbluhm rt->rt_llinfo = (caddr_t)la; 217df930be7Sderaadt la->la_rt = rt; 218df930be7Sderaadt rt->rt_flags |= RTF_LLINFO; 219e758f4c8Sbluhm LIST_INSERT_HEAD(&arp_list, la, la_list); 220fe07dcc8Sclaudio if ((rt->rt_flags & RTF_LOCAL) == 0) 22141c068b6Sbluhm rt->rt_expire = uptime; 2225bdbb3c9Sbluhm mtx_leave(&arp_mtx); 223e758f4c8Sbluhm 224df930be7Sderaadt break; 225df930be7Sderaadt 226df930be7Sderaadt case RTM_DELETE: 2275bdbb3c9Sbluhm mtx_enter(&arp_mtx); 228e758f4c8Sbluhm la = (struct llinfo_arp *)rt->rt_llinfo; 229e758f4c8Sbluhm if (la == NULL) { 230e758f4c8Sbluhm /* we lost the race, another thread has removed it */ 2315bdbb3c9Sbluhm mtx_leave(&arp_mtx); 232e758f4c8Sbluhm break; 233e758f4c8Sbluhm } 234e758f4c8Sbluhm LIST_REMOVE(la, la_list); 235b3aa8519Smpi rt->rt_llinfo = NULL; 236df930be7Sderaadt rt->rt_flags &= ~RTF_LLINFO; 2374e550751Sbluhm atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq)); 238e758f4c8Sbluhm mtx_leave(&arp_mtx); 239e758f4c8Sbluhm 240656dd4d4Smpi pool_put(&arp_pool, la); 241c370e97fSmpi break; 242c370e97fSmpi 243c370e97fSmpi case RTM_INVALIDATE: 244c370e97fSmpi if (!ISSET(rt->rt_flags, RTF_LOCAL)) 245c370e97fSmpi arpinvalidate(rt); 246c370e97fSmpi break; 247df930be7Sderaadt } 248df930be7Sderaadt } 249df930be7Sderaadt 250df930be7Sderaadt /* 251df930be7Sderaadt * Broadcast an ARP request. Caller specifies: 252df930be7Sderaadt * - arp header source ip address 253df930be7Sderaadt * - arp header target ip address 254df930be7Sderaadt * - arp header source ethernet address 255df930be7Sderaadt */ 2568a85c499Smickey void 25720b3612bSdhill arprequest(struct ifnet *ifp, u_int32_t *sip, u_int32_t *tip, u_int8_t *enaddr) 258df930be7Sderaadt { 25964aa4cc7Sitojun struct mbuf *m; 26064aa4cc7Sitojun struct ether_header *eh; 26164aa4cc7Sitojun struct ether_arp *ea; 262df930be7Sderaadt struct sockaddr sa; 263df930be7Sderaadt 264df930be7Sderaadt if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) 265df930be7Sderaadt return; 266df930be7Sderaadt m->m_len = sizeof(*ea); 267df930be7Sderaadt m->m_pkthdr.len = sizeof(*ea); 2685ee8afe3Smpi m->m_pkthdr.ph_rtableid = ifp->if_rdomain; 269a5e12720Svgross m->m_pkthdr.pf.prio = ifp->if_llprio; 270da17b520Sclaudio m_align(m, sizeof(*ea)); 271df930be7Sderaadt ea = mtod(m, struct ether_arp *); 272df930be7Sderaadt eh = (struct ether_header *)sa.sa_data; 273f8575965Stedu memset(ea, 0, sizeof(*ea)); 27469985460Stedu memcpy(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)); 275df930be7Sderaadt eh->ether_type = htons(ETHERTYPE_ARP); /* if_output will not swap */ 276df930be7Sderaadt ea->arp_hrd = htons(ARPHRD_ETHER); 277df930be7Sderaadt ea->arp_pro = htons(ETHERTYPE_IP); 278df930be7Sderaadt ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */ 279df930be7Sderaadt ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */ 280df930be7Sderaadt ea->arp_op = htons(ARPOP_REQUEST); 28169985460Stedu memcpy(eh->ether_shost, enaddr, sizeof(eh->ether_shost)); 28269985460Stedu memcpy(ea->arp_sha, enaddr, sizeof(ea->arp_sha)); 28369985460Stedu memcpy(ea->arp_spa, sip, sizeof(ea->arp_spa)); 28469985460Stedu memcpy(ea->arp_tpa, tip, sizeof(ea->arp_tpa)); 285718ccf97Spascoe sa.sa_family = pseudo_AF_HDRCMPLT; 286df930be7Sderaadt sa.sa_len = sizeof(sa); 287ba4a553cScamield m->m_flags |= M_BCAST; 288d9dc9856Smpi ifp->if_output(ifp, m, &sa, NULL); 289df930be7Sderaadt } 290df930be7Sderaadt 291ad1e2b0cSmpi void 2923b27b39dSstsp arpreply(struct ifnet *ifp, struct mbuf *m, struct in_addr *sip, uint8_t *eaddr, 2933b27b39dSstsp unsigned int rdomain) 294ad1e2b0cSmpi { 295ad1e2b0cSmpi struct ether_header *eh; 296ad1e2b0cSmpi struct ether_arp *ea; 297ad1e2b0cSmpi struct sockaddr sa; 298ad1e2b0cSmpi 2993b27b39dSstsp m_resethdr(m); 3003b27b39dSstsp m->m_pkthdr.ph_rtableid = rdomain; 3013b27b39dSstsp 302ad1e2b0cSmpi ea = mtod(m, struct ether_arp *); 303ad1e2b0cSmpi ea->arp_op = htons(ARPOP_REPLY); 304ad1e2b0cSmpi ea->arp_pro = htons(ETHERTYPE_IP); /* let's be sure! */ 305ad1e2b0cSmpi 306ad1e2b0cSmpi /* We're replying to a request. */ 307ad1e2b0cSmpi memcpy(ea->arp_tha, ea->arp_sha, sizeof(ea->arp_sha)); 308ad1e2b0cSmpi memcpy(ea->arp_tpa, ea->arp_spa, sizeof(ea->arp_spa)); 309ad1e2b0cSmpi 310ad1e2b0cSmpi memcpy(ea->arp_sha, eaddr, sizeof(ea->arp_sha)); 311ad1e2b0cSmpi memcpy(ea->arp_spa, sip, sizeof(ea->arp_spa)); 312ad1e2b0cSmpi 313ad1e2b0cSmpi eh = (struct ether_header *)sa.sa_data; 314ad1e2b0cSmpi memcpy(eh->ether_dhost, ea->arp_tha, sizeof(eh->ether_dhost)); 315ad1e2b0cSmpi memcpy(eh->ether_shost, eaddr, sizeof(eh->ether_shost)); 316ad1e2b0cSmpi eh->ether_type = htons(ETHERTYPE_ARP); 317ad1e2b0cSmpi sa.sa_family = pseudo_AF_HDRCMPLT; 318ad1e2b0cSmpi sa.sa_len = sizeof(sa); 319ad1e2b0cSmpi ifp->if_output(ifp, m, &sa, NULL); 320ad1e2b0cSmpi } 321ad1e2b0cSmpi 322df930be7Sderaadt /* 323df930be7Sderaadt * Resolve an IP address into an ethernet address. If success, 324df930be7Sderaadt * desten is filled in. If there is no entry in arptab, 325df930be7Sderaadt * set one up and broadcast a request for the IP address. 326df930be7Sderaadt * Hold onto this mbuf and resend it once the address 3278c023157Smpi * is finally resolved. A return value of 0 indicates 328df930be7Sderaadt * that desten has been filled in and the packet should be sent 3298c023157Smpi * normally; A return value of EAGAIN indicates that the packet 3308c023157Smpi * has been taken over here, either now or for later transmission. 3318c023157Smpi * Any other return value indicates an error. 332df930be7Sderaadt */ 333df930be7Sderaadt int 334f47663adSmpi arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, 33520b3612bSdhill struct sockaddr *dst, u_char *desten) 336df930be7Sderaadt { 337f47663adSmpi struct arpcom *ac = (struct arpcom *)ifp; 338fe07dcc8Sclaudio struct llinfo_arp *la; 339df930be7Sderaadt struct sockaddr_dl *sdl; 3400003bebaSjsg struct rtentry *rt = NULL; 341bbcf0337Smpi char addr[INET_ADDRSTRLEN]; 34241c068b6Sbluhm time_t uptime; 343862bb2afSbluhm int refresh = 0, reject = 0; 344df930be7Sderaadt 345df930be7Sderaadt if (m->m_flags & M_BCAST) { /* broadcast */ 34669985460Stedu memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr)); 3478c023157Smpi return (0); 348df930be7Sderaadt } 349df930be7Sderaadt if (m->m_flags & M_MCAST) { /* multicast */ 350a961b778Smpi ETHER_MAP_IP_MULTICAST(&satosin(dst)->sin_addr, desten); 3518c023157Smpi return (0); 352df930be7Sderaadt } 3538c023157Smpi 35441c068b6Sbluhm uptime = getuptime(); 355c370e97fSmpi rt = rt_getll(rt0); 356c370e97fSmpi 357c370e97fSmpi if (ISSET(rt->rt_flags, RTF_REJECT) && 35841c068b6Sbluhm (rt->rt_expire == 0 || rt->rt_expire > uptime)) { 3598c023157Smpi m_freem(m); 360c370e97fSmpi return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); 3618c023157Smpi } 3628c023157Smpi 3635da3fb44Smpi if (!ISSET(rt->rt_flags, RTF_LLINFO)) { 3645da3fb44Smpi log(LOG_DEBUG, "%s: %s: route contains no arp information\n", 3655da3fb44Smpi __func__, inet_ntop(AF_INET, &satosin(rt_key(rt))->sin_addr, 3665da3fb44Smpi addr, sizeof(addr))); 36732b4d8f2Sbluhm goto bad; 368e3efe2d8Smpi } 369e3efe2d8Smpi 370c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 371dbae7f73Smpi if (sdl->sdl_alen > 0 && sdl->sdl_alen != ETHER_ADDR_LEN) { 372dbae7f73Smpi log(LOG_DEBUG, "%s: %s: incorrect arp information\n", __func__, 373dbae7f73Smpi inet_ntop(AF_INET, &satosin(dst)->sin_addr, 374dbae7f73Smpi addr, sizeof(addr))); 375bd2844f4Smpi goto bad; 376dbae7f73Smpi } 3775da3fb44Smpi 378fe07dcc8Sclaudio 379df930be7Sderaadt /* 380df930be7Sderaadt * Check the address family and length is valid, the address 381df930be7Sderaadt * is resolved; otherwise, try to resolve. 382df930be7Sderaadt */ 38341c068b6Sbluhm if ((rt->rt_expire == 0 || rt->rt_expire > uptime) && 384df930be7Sderaadt sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) { 38569985460Stedu memcpy(desten, LLADDR(sdl), sdl->sdl_alen); 386fe07dcc8Sclaudio 387fe07dcc8Sclaudio /* refresh ARP entry when timeout gets close */ 388fe07dcc8Sclaudio if (rt->rt_expire != 0 && 389e758f4c8Sbluhm rt->rt_expire - arpt_keep / 8 < uptime) { 390e758f4c8Sbluhm 391e758f4c8Sbluhm mtx_enter(&arp_mtx); 392e758f4c8Sbluhm la = (struct llinfo_arp *)rt->rt_llinfo; 393bb9afb7eSbluhm if (la != NULL) { 394e758f4c8Sbluhm if (la->la_refreshed + 30 < uptime) { 39541c068b6Sbluhm la->la_refreshed = uptime; 396e758f4c8Sbluhm refresh = 1; 397e758f4c8Sbluhm } 398e758f4c8Sbluhm } 399e758f4c8Sbluhm mtx_leave(&arp_mtx); 400e758f4c8Sbluhm } 401e758f4c8Sbluhm if (refresh) { 402fe07dcc8Sclaudio arprequest(ifp, 403fe07dcc8Sclaudio &satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr, 404fe07dcc8Sclaudio &satosin(dst)->sin_addr.s_addr, 405fe07dcc8Sclaudio ac->ac_enaddr); 406fe07dcc8Sclaudio } 4078c023157Smpi return (0); 408df930be7Sderaadt } 4095da3fb44Smpi 410beb8b0ddSmpi if (ifp->if_flags & (IFF_NOARP|IFF_STATICARP)) 411bd2844f4Smpi goto bad; 412b1b24c71Sderaadt 413862bb2afSbluhm mtx_enter(&arp_mtx); 414bb9afb7eSbluhm la = (struct llinfo_arp *)rt->rt_llinfo; 415bb9afb7eSbluhm if (la == NULL) { 416862bb2afSbluhm mtx_leave(&arp_mtx); 41732b4d8f2Sbluhm goto bad; 41832b4d8f2Sbluhm } 41932b4d8f2Sbluhm 420df930be7Sderaadt /* 421df930be7Sderaadt * There is an arptab entry, but no ethernet address 422e3e73f87Sclaudio * response yet. Insert mbuf in hold queue if below limit. 423e3e73f87Sclaudio * If above the limit free the queue without queuing the new packet. 424df930be7Sderaadt */ 4254e550751Sbluhm if (atomic_inc_int_nv(&la_hold_total) <= LA_HOLD_TOTAL) { 4264e550751Sbluhm if (mq_push(&la->la_mq, m) != 0) 4274e550751Sbluhm atomic_dec_int(&la_hold_total); 428aee9454dSclaudio } else { 4294e550751Sbluhm atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq) + 1); 4300c2c1695Sclaudio m_freem(m); 4316743972bSgollo } 4326743972bSgollo 433df930be7Sderaadt /* 434df930be7Sderaadt * Re-send the ARP request when appropriate. 435df930be7Sderaadt */ 436df930be7Sderaadt #ifdef DIAGNOSTIC 437df930be7Sderaadt if (rt->rt_expire == 0) { 438df930be7Sderaadt /* This should never happen. (Should it? -gwr) */ 43935075f95Smpi printf("%s: unresolved and rt_expire == 0\n", __func__); 440df930be7Sderaadt /* Set expiration time to now (expired). */ 44141c068b6Sbluhm rt->rt_expire = uptime; 442df930be7Sderaadt } 443df930be7Sderaadt #endif 444df930be7Sderaadt if (rt->rt_expire) { 445862bb2afSbluhm reject = ~RTF_REJECT; 44641c068b6Sbluhm if (la->la_asked == 0 || rt->rt_expire != uptime) { 44741c068b6Sbluhm rt->rt_expire = uptime; 448df930be7Sderaadt if (la->la_asked++ < arp_maxtries) 44916c9a88cSkn refresh = 1; 450df930be7Sderaadt else { 451862bb2afSbluhm reject = RTF_REJECT; 452df930be7Sderaadt rt->rt_expire += arpt_down; 453df930be7Sderaadt la->la_asked = 0; 454fe07dcc8Sclaudio la->la_refreshed = 0; 4554e550751Sbluhm atomic_sub_int(&la_hold_total, 4564e550751Sbluhm mq_purge(&la->la_mq)); 457df930be7Sderaadt } 458df930be7Sderaadt } 459df930be7Sderaadt } 460862bb2afSbluhm mtx_leave(&arp_mtx); 4615da3fb44Smpi 462862bb2afSbluhm if (reject == RTF_REJECT && !ISSET(rt->rt_flags, RTF_REJECT)) { 463862bb2afSbluhm KERNEL_LOCK(); 464862bb2afSbluhm SET(rt->rt_flags, RTF_REJECT); 465aa5e72b7Smvs KERNEL_UNLOCK(); 466862bb2afSbluhm } 467862bb2afSbluhm if (reject == ~RTF_REJECT && ISSET(rt->rt_flags, RTF_REJECT)) { 468862bb2afSbluhm KERNEL_LOCK(); 469862bb2afSbluhm CLR(rt->rt_flags, RTF_REJECT); 470862bb2afSbluhm KERNEL_UNLOCK(); 471862bb2afSbluhm } 47216c9a88cSkn if (refresh) 47316c9a88cSkn arprequest(ifp, &satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr, 47416c9a88cSkn &satosin(dst)->sin_addr.s_addr, ac->ac_enaddr); 4758c023157Smpi return (EAGAIN); 476bd2844f4Smpi 477bd2844f4Smpi bad: 478bd2844f4Smpi m_freem(m); 479bd2844f4Smpi return (EINVAL); 480df930be7Sderaadt } 481df930be7Sderaadt 4821a184782Sbluhm struct mbuf * 4831a184782Sbluhm arppullup(struct mbuf *m) 4841a184782Sbluhm { 4851a184782Sbluhm struct arphdr *ar; 4861a184782Sbluhm int len; 4871a184782Sbluhm 4881a184782Sbluhm #ifdef DIAGNOSTIC 4891a184782Sbluhm if ((m->m_flags & M_PKTHDR) == 0) 4901a184782Sbluhm panic("arp without packet header"); 4911a184782Sbluhm #endif 4921a184782Sbluhm 4931a184782Sbluhm len = sizeof(struct arphdr); 4941a184782Sbluhm if (m->m_len < len && (m = m_pullup(m, len)) == NULL) 4951a184782Sbluhm return NULL; 4961a184782Sbluhm 4971a184782Sbluhm ar = mtod(m, struct arphdr *); 4981a184782Sbluhm if (ntohs(ar->ar_hrd) != ARPHRD_ETHER || 4991a184782Sbluhm ntohs(ar->ar_pro) != ETHERTYPE_IP || 5001a184782Sbluhm ar->ar_hln != ETHER_ADDR_LEN || 5011a184782Sbluhm ar->ar_pln != sizeof(struct in_addr)) { 5021a184782Sbluhm m_freem(m); 5031a184782Sbluhm return NULL; 5041a184782Sbluhm } 5051a184782Sbluhm 5061a184782Sbluhm len += 2 * (ar->ar_hln + ar->ar_pln); 5071a184782Sbluhm if (m->m_len < len && (m = m_pullup(m, len)) == NULL) 5081a184782Sbluhm return NULL; 5091a184782Sbluhm 5101a184782Sbluhm return m; 5111a184782Sbluhm } 5121a184782Sbluhm 513df930be7Sderaadt /* 514df930be7Sderaadt * Common length and type checks are done here, 515df930be7Sderaadt * then the protocol-specific routine is called. 516df930be7Sderaadt */ 517df930be7Sderaadt void 5185a411d3cSmpi arpinput(struct ifnet *ifp, struct mbuf *m) 519df930be7Sderaadt { 5201a184782Sbluhm if ((m = arppullup(m)) == NULL) 521f97093e3Smpi return; 522c709125fSmpi niq_enqueue(&arpinq, m); 523c709125fSmpi } 524c709125fSmpi 525c709125fSmpi void 526c709125fSmpi arpintr(void) 527c709125fSmpi { 528c709125fSmpi struct mbuf_list ml; 529c709125fSmpi struct mbuf *m; 530c709125fSmpi struct ifnet *ifp; 531c709125fSmpi 532c709125fSmpi niq_delist(&arpinq, &ml); 533c709125fSmpi 534c709125fSmpi while ((m = ml_dequeue(&ml)) != NULL) { 535c709125fSmpi ifp = if_get(m->m_pkthdr.ph_ifidx); 536c709125fSmpi 537c709125fSmpi if (ifp != NULL) 5385a411d3cSmpi in_arpinput(ifp, m); 539c709125fSmpi else 540c709125fSmpi m_freem(m); 541c709125fSmpi 542c709125fSmpi if_put(ifp); 543c709125fSmpi } 544df930be7Sderaadt } 545df930be7Sderaadt 546df930be7Sderaadt /* 547197cb7b0Smpi * ARP for Internet protocols on Ethernet, RFC 826. 548df930be7Sderaadt * In addition, a sanity check is performed on the sender 549df930be7Sderaadt * protocol address, to catch impersonators. 550df930be7Sderaadt */ 5514a5a0f00Sniklas void 5525a411d3cSmpi in_arpinput(struct ifnet *ifp, struct mbuf *m) 553df930be7Sderaadt { 55464aa4cc7Sitojun struct ether_arp *ea; 555bd2844f4Smpi struct rtentry *rt = NULL; 556df6d650aSbluhm struct sockaddr_in sin; 557df6d650aSbluhm struct in_addr isaddr, itaddr; 558bbcf0337Smpi char addr[INET_ADDRSTRLEN]; 559590a0433Smpi int op, target = 0; 560590a0433Smpi unsigned int rdomain; 561df6d650aSbluhm 562df6d650aSbluhm rdomain = rtable_l2(m->m_pkthdr.ph_rtableid); 563df930be7Sderaadt 564df930be7Sderaadt ea = mtod(m, struct ether_arp *); 565df930be7Sderaadt op = ntohs(ea->arp_op); 56664483952Sho if ((op != ARPOP_REQUEST) && (op != ARPOP_REPLY)) 56764483952Sho goto out; 568759eb4f0Sjason 56969985460Stedu memcpy(&itaddr, ea->arp_tpa, sizeof(itaddr)); 57069985460Stedu memcpy(&isaddr, ea->arp_spa, sizeof(isaddr)); 571df6d650aSbluhm memset(&sin, 0, sizeof(sin)); 572df6d650aSbluhm sin.sin_len = sizeof(sin); 573df6d650aSbluhm sin.sin_family = AF_INET; 574759eb4f0Sjason 57578cd82e3Smpi if (ETHER_IS_MULTICAST(ea->arp_sha) && 57678cd82e3Smpi ETHER_IS_BROADCAST(ea->arp_sha)) { 577e0256ea8Smpi inet_ntop(AF_INET, &isaddr, addr, sizeof(addr)); 578590a0433Smpi log(LOG_ERR, "arp: ether address is broadcast for IP address " 579590a0433Smpi "%s!\n", addr); 580e0256ea8Smpi goto out; 581e0256ea8Smpi } 582e0256ea8Smpi 583ad1e2b0cSmpi if (!memcmp(ea->arp_sha, LLADDR(ifp->if_sadl), sizeof(ea->arp_sha))) 584691223e9Smpi goto out; /* it's from me, ignore it. */ 585691223e9Smpi 5862d8cfe87Sbluhm /* Check target against our interface addresses. */ 587df6d650aSbluhm sin.sin_addr = itaddr; 588df6d650aSbluhm rt = rtalloc(sintosa(&sin), 0, rdomain); 589df6d650aSbluhm if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) && 590df6d650aSbluhm rt->rt_ifidx == ifp->if_index) 591df6d650aSbluhm target = 1; 592df6d650aSbluhm rtfree(rt); 593df6d650aSbluhm rt = NULL; 594187d1bbfSmpi 5950e9d3fc7Smpi #if NCARP > 0 596df6d650aSbluhm if (target && op == ARPOP_REQUEST && ifp->if_type == IFT_CARP && 597e9c5d3c9Smpi !carp_iamatch(ifp)) 598e0c7ab61Smpf goto out; 5991f2f62f7Smcbride #endif 600759eb4f0Sjason 6012d8cfe87Sbluhm /* Do we have an ARP cache for the sender? Create if we are target. */ 602d2dce6aeSmpi rt = arplookup(&isaddr, target, 0, rdomain); 6032d8cfe87Sbluhm 6042d8cfe87Sbluhm /* Check sender against our interface addresses. */ 6052d8cfe87Sbluhm if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) && 6062d8cfe87Sbluhm rt->rt_ifidx == ifp->if_index && isaddr.s_addr != INADDR_ANY) { 607bbcf0337Smpi inet_ntop(AF_INET, &isaddr, addr, sizeof(addr)); 608143dedceSmpi log(LOG_ERR, "duplicate IP address %s sent from ethernet " 609143dedceSmpi "address %s\n", addr, ether_sprintf(ea->arp_sha)); 610df6d650aSbluhm itaddr = isaddr; 611590a0433Smpi } else if (rt != NULL) { 612ccf4d05aSbluhm if (arpcache(ifp, ea, rt)) 61350116f0eSho goto out; 614df930be7Sderaadt } 6152d8cfe87Sbluhm 616ad1e2b0cSmpi if (op == ARPOP_REQUEST) { 617ad1e2b0cSmpi uint8_t *eaddr; 618bd2844f4Smpi 619df6d650aSbluhm if (target) { 620ad1e2b0cSmpi /* We already have all info for the reply */ 621ad1e2b0cSmpi eaddr = LLADDR(ifp->if_sadl); 622df930be7Sderaadt } else { 623bd2844f4Smpi rtfree(rt); 624ad1e2b0cSmpi rt = arplookup(&itaddr, 0, SIN_PROXY, rdomain); 625ad1e2b0cSmpi /* 626ad1e2b0cSmpi * Protect from possible duplicates, only owner 627ad1e2b0cSmpi * should respond 628ad1e2b0cSmpi */ 629ad1e2b0cSmpi if ((rt == NULL) || (rt->rt_ifidx != ifp->if_index)) 630ad1e2b0cSmpi goto out; 631ad1e2b0cSmpi eaddr = LLADDR(satosdl(rt->rt_gateway)); 632df930be7Sderaadt } 6333b27b39dSstsp arpreply(ifp, m, &itaddr, eaddr, rdomain); 634ad1e2b0cSmpi rtfree(rt); 635590a0433Smpi return; 636ad1e2b0cSmpi } 637590a0433Smpi 638590a0433Smpi out: 639590a0433Smpi rtfree(rt); 640590a0433Smpi m_freem(m); 641df930be7Sderaadt } 642df930be7Sderaadt 643590a0433Smpi int 644590a0433Smpi arpcache(struct ifnet *ifp, struct ether_arp *ea, struct rtentry *rt) 645590a0433Smpi { 646590a0433Smpi struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo; 647590a0433Smpi struct sockaddr_dl *sdl = satosdl(rt->rt_gateway); 648590a0433Smpi struct in_addr *spa = (struct in_addr *)ea->arp_spa; 649590a0433Smpi char addr[INET_ADDRSTRLEN]; 650590a0433Smpi struct ifnet *rifp; 65141c068b6Sbluhm time_t uptime; 652590a0433Smpi int changed = 0; 653590a0433Smpi 654ccf4d05aSbluhm NET_ASSERT_LOCKED_EXCLUSIVE(); 655590a0433Smpi KASSERT(sdl != NULL); 656590a0433Smpi 657b3aa8519Smpi /* 658b3aa8519Smpi * This can happen if the entry has been deleted by another CPU 659b3aa8519Smpi * after we found it. 660b3aa8519Smpi */ 661b3aa8519Smpi if (la == NULL) 662b3aa8519Smpi return (0); 663b3aa8519Smpi 66441c068b6Sbluhm uptime = getuptime(); 665590a0433Smpi if (sdl->sdl_alen > 0) { 666590a0433Smpi if (memcmp(ea->arp_sha, LLADDR(sdl), sdl->sdl_alen)) { 667590a0433Smpi if (ISSET(rt->rt_flags, RTF_PERMANENT_ARP|RTF_LOCAL)) { 668590a0433Smpi inet_ntop(AF_INET, spa, addr, sizeof(addr)); 669590a0433Smpi log(LOG_WARNING, "arp: attempt to overwrite " 670590a0433Smpi "permanent entry for %s by %s on %s\n", addr, 671590a0433Smpi ether_sprintf(ea->arp_sha), ifp->if_xname); 672590a0433Smpi return (-1); 673590a0433Smpi } else if (rt->rt_ifidx != ifp->if_index) { 674590a0433Smpi #if NCARP > 0 675590a0433Smpi if (ifp->if_type != IFT_CARP) 676590a0433Smpi #endif 677590a0433Smpi { 678590a0433Smpi rifp = if_get(rt->rt_ifidx); 679590a0433Smpi if (rifp == NULL) 680590a0433Smpi return (-1); 681590a0433Smpi inet_ntop(AF_INET, spa, addr, 682590a0433Smpi sizeof(addr)); 683590a0433Smpi log(LOG_WARNING, "arp: attempt to " 684590a0433Smpi "overwrite entry for %s on %s by " 685590a0433Smpi "%s on %s\n", addr, rifp->if_xname, 686590a0433Smpi ether_sprintf(ea->arp_sha), 687590a0433Smpi ifp->if_xname); 688590a0433Smpi if_put(rifp); 689590a0433Smpi } 690590a0433Smpi return (-1); 691590a0433Smpi } else { 692590a0433Smpi inet_ntop(AF_INET, spa, addr, sizeof(addr)); 693590a0433Smpi log(LOG_INFO, "arp info overwritten for %s by " 694590a0433Smpi "%s on %s\n", addr, 695590a0433Smpi ether_sprintf(ea->arp_sha), ifp->if_xname); 696590a0433Smpi rt->rt_expire = 1;/* no longer static */ 697590a0433Smpi } 698590a0433Smpi changed = 1; 699590a0433Smpi } 700590a0433Smpi } else if (!if_isconnected(ifp, rt->rt_ifidx)) { 701590a0433Smpi rifp = if_get(rt->rt_ifidx); 702590a0433Smpi if (rifp == NULL) 703590a0433Smpi return (-1); 704590a0433Smpi inet_ntop(AF_INET, spa, addr, sizeof(addr)); 705590a0433Smpi log(LOG_WARNING, "arp: attempt to add entry for %s on %s by %s" 706590a0433Smpi " on %s\n", addr, rifp->if_xname, 707590a0433Smpi ether_sprintf(ea->arp_sha), ifp->if_xname); 708590a0433Smpi if_put(rifp); 709590a0433Smpi return (-1); 710590a0433Smpi } 711590a0433Smpi sdl->sdl_alen = sizeof(ea->arp_sha); 712590a0433Smpi memcpy(LLADDR(sdl), ea->arp_sha, sizeof(ea->arp_sha)); 713590a0433Smpi if (rt->rt_expire) 71441c068b6Sbluhm rt->rt_expire = uptime + arpt_keep; 715590a0433Smpi rt->rt_flags &= ~RTF_REJECT; 716590a0433Smpi 717590a0433Smpi /* Notify userland that an ARP resolution has been done. */ 718590a0433Smpi if (la->la_asked || changed) { 7193a711300Smpi rtm_send(rt, RTM_RESOLVE, 0, ifp->if_rdomain); 720590a0433Smpi } 721590a0433Smpi 722590a0433Smpi la->la_asked = 0; 723fe07dcc8Sclaudio la->la_refreshed = 0; 724b8646e37Sbluhm if_output_mq(ifp, &la->la_mq, &la_hold_total, rt_key(rt), rt); 725590a0433Smpi 726590a0433Smpi return (0); 727590a0433Smpi } 728c370e97fSmpi 729c370e97fSmpi void 730c370e97fSmpi arpinvalidate(struct rtentry *rt) 731c370e97fSmpi { 732e758f4c8Sbluhm struct llinfo_arp *la; 733c370e97fSmpi struct sockaddr_dl *sdl = satosdl(rt->rt_gateway); 734c370e97fSmpi 735e758f4c8Sbluhm mtx_enter(&arp_mtx); 736e758f4c8Sbluhm la = (struct llinfo_arp *)rt->rt_llinfo; 737e758f4c8Sbluhm if (la == NULL) { 738e758f4c8Sbluhm mtx_leave(&arp_mtx); 739e758f4c8Sbluhm return; 740e758f4c8Sbluhm } 7414e550751Sbluhm atomic_sub_int(&la_hold_total, mq_purge(&la->la_mq)); 742c370e97fSmpi sdl->sdl_alen = 0; 743c370e97fSmpi la->la_asked = 0; 744e758f4c8Sbluhm mtx_leave(&arp_mtx); 745c370e97fSmpi } 746c370e97fSmpi 747df930be7Sderaadt /* 748df930be7Sderaadt * Free an arp entry. 749df930be7Sderaadt */ 7504a5a0f00Sniklas void 7512ca83196Smpi arptfree(struct rtentry *rt) 752df930be7Sderaadt { 753df6c8f18Smpi struct ifnet *ifp; 754df930be7Sderaadt 7556348db95Sbluhm KASSERT(!ISSET(rt->rt_flags, RTF_LOCAL)); 756c370e97fSmpi arpinvalidate(rt); 75762b8693bSclaudio 758c370e97fSmpi ifp = if_get(rt->rt_ifidx); 75973fb5aaeSbluhm if (ifp == NULL) 76073fb5aaeSbluhm return; 761c370e97fSmpi if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED)) 762c4cbcae8Sbluhm rtdeletemsg(rt, ifp, ifp->if_rdomain); 763df6c8f18Smpi if_put(ifp); 764df930be7Sderaadt } 765df930be7Sderaadt 766df930be7Sderaadt /* 767df930be7Sderaadt * Lookup or enter a new address in arptab. 768df930be7Sderaadt */ 769c9ec952eSmpi struct rtentry * 770d2dce6aeSmpi arplookup(struct in_addr *inp, int create, int proxy, u_int tableid) 771df930be7Sderaadt { 77264aa4cc7Sitojun struct rtentry *rt; 773ab47b7feSmpi struct sockaddr_inarp sin; 7745148b194Smpi int flags; 775df930be7Sderaadt 776f8575965Stedu memset(&sin, 0, sizeof(sin)); 777df930be7Sderaadt sin.sin_len = sizeof(sin); 778df930be7Sderaadt sin.sin_family = AF_INET; 779d2dce6aeSmpi sin.sin_addr.s_addr = inp->s_addr; 780df930be7Sderaadt sin.sin_other = proxy ? SIN_PROXY : 0; 78112d3c25cSclaudio flags = (create) ? RT_RESOLVE : 0; 7825148b194Smpi 7835148b194Smpi rt = rtalloc((struct sockaddr *)&sin, flags, tableid); 78471612440Smpi if (!rtisvalid(rt) || ISSET(rt->rt_flags, RTF_GATEWAY) || 78571612440Smpi !ISSET(rt->rt_flags, RTF_LLINFO) || 786df930be7Sderaadt rt->rt_gateway->sa_family != AF_LINK) { 787bd2844f4Smpi rtfree(rt); 788c9ec952eSmpi return (NULL); 789df930be7Sderaadt } 790bde66f81Smpi 791bde66f81Smpi if (proxy && !ISSET(rt->rt_flags, RTF_ANNOUNCE)) { 7925ea6c205Smpi while ((rt = rtable_iterate(rt)) != NULL) { 7935ea6c205Smpi if (ISSET(rt->rt_flags, RTF_ANNOUNCE)) { 79483eaa5b1Smpi break; 79583eaa5b1Smpi } 79683eaa5b1Smpi } 797bde66f81Smpi } 798bde66f81Smpi 799c9ec952eSmpi return (rt); 800df930be7Sderaadt } 801df930be7Sderaadt 8024c1703feSmpi /* 8034c1703feSmpi * Check whether we do proxy ARP for this address and we point to ourselves. 8044c1703feSmpi */ 8054c1703feSmpi int 8064174ded9Smpi arpproxy(struct in_addr in, unsigned int rtableid) 8074c1703feSmpi { 8084174ded9Smpi struct sockaddr_dl *sdl; 809c9ec952eSmpi struct rtentry *rt; 8104c1703feSmpi struct ifnet *ifp; 8114c1703feSmpi int found = 0; 8124c1703feSmpi 813d2dce6aeSmpi rt = arplookup(&in, 0, SIN_PROXY, rtableid); 814df6c8f18Smpi if (!rtisvalid(rt)) { 815df6c8f18Smpi rtfree(rt); 8164c1703feSmpi return (0); 817df6c8f18Smpi } 8184c1703feSmpi 8194174ded9Smpi /* Check that arp information are correct. */ 820c7b7b779Sbluhm sdl = satosdl(rt->rt_gateway); 821bd2844f4Smpi if (sdl->sdl_alen != ETHER_ADDR_LEN) { 822bd2844f4Smpi rtfree(rt); 8234174ded9Smpi return (0); 824bd2844f4Smpi } 8254c1703feSmpi 826df6c8f18Smpi ifp = if_get(rt->rt_ifidx); 827df6c8f18Smpi if (ifp == NULL) { 828df6c8f18Smpi rtfree(rt); 829df6c8f18Smpi return (0); 830df6c8f18Smpi } 831df6c8f18Smpi 8324174ded9Smpi if (!memcmp(LLADDR(sdl), LLADDR(ifp->if_sadl), sdl->sdl_alen)) 8334c1703feSmpi found = 1; 8344c1703feSmpi 835df6c8f18Smpi if_put(ifp); 836bd2844f4Smpi rtfree(rt); 8374c1703feSmpi return (found); 8384c1703feSmpi } 8394c1703feSmpi 840df930be7Sderaadt /* 84166411b94Sderaadt * Called from Ethernet interrupt handlers 842df930be7Sderaadt * when ether packet type ETHERTYPE_REVARP 843df930be7Sderaadt * is received. Common length and type checks are done here, 844df930be7Sderaadt * then the protocol-specific routine is called. 845df930be7Sderaadt */ 846df930be7Sderaadt void 8475a411d3cSmpi revarpinput(struct ifnet *ifp, struct mbuf *m) 848df930be7Sderaadt { 8491a184782Sbluhm if ((m = arppullup(m)) == NULL) 850df930be7Sderaadt return; 8511a184782Sbluhm in_revarpinput(ifp, m); 852df930be7Sderaadt } 853df930be7Sderaadt 854df930be7Sderaadt /* 85566411b94Sderaadt * RARP for Internet protocols on Ethernet. 856df930be7Sderaadt * Algorithm is that given in RFC 903. 857df930be7Sderaadt * We are only using for bootstrap purposes to get an ip address for one of 858df930be7Sderaadt * our interfaces. Thus we support no user-interface. 859df930be7Sderaadt * 860df930be7Sderaadt * Since the contents of the RARP reply are specific to the interface that 861df930be7Sderaadt * sent the request, this code must ensure that they are properly associated. 862df930be7Sderaadt * 863df930be7Sderaadt * Note: also supports ARP via RARP packets, per the RFC. 864df930be7Sderaadt */ 865b400e158Sniklas void 8665a411d3cSmpi in_revarpinput(struct ifnet *ifp, struct mbuf *m) 867df930be7Sderaadt { 868df930be7Sderaadt struct ether_arp *ar; 869b400e158Sniklas int op; 870df930be7Sderaadt 871df930be7Sderaadt ar = mtod(m, struct ether_arp *); 872df930be7Sderaadt op = ntohs(ar->arp_op); 873df930be7Sderaadt switch (op) { 874df930be7Sderaadt case ARPOP_REQUEST: 875df930be7Sderaadt case ARPOP_REPLY: /* per RFC */ 876c709125fSmpi niq_enqueue(&arpinq, m); 877df930be7Sderaadt return; 878df930be7Sderaadt case ARPOP_REVREPLY: 879df930be7Sderaadt break; 880df930be7Sderaadt case ARPOP_REVREQUEST: /* handled by rarpd(8) */ 881df930be7Sderaadt default: 882df930be7Sderaadt goto out; 883df930be7Sderaadt } 884300480deSderaadt #ifdef NFSCLIENT 885df5ed9e3Smpi if (revarp_ifidx == 0) 886df930be7Sderaadt goto out; 887df5ed9e3Smpi if (revarp_ifidx != m->m_pkthdr.ph_ifidx) /* !same interface */ 888df930be7Sderaadt goto out; 889300480deSderaadt if (revarp_finished) 890df930be7Sderaadt goto wake; 891df5ed9e3Smpi if (memcmp(ar->arp_tha, LLADDR(ifp->if_sadl), sizeof(ar->arp_tha))) 892df930be7Sderaadt goto out; 89369985460Stedu memcpy(&revarp_srvip, ar->arp_spa, sizeof(revarp_srvip)); 89469985460Stedu memcpy(&revarp_myip, ar->arp_tpa, sizeof(revarp_myip)); 895300480deSderaadt revarp_finished = 1; 896df930be7Sderaadt wake: /* Do wakeup every time in case it was missed. */ 897300480deSderaadt wakeup((caddr_t)&revarp_myip); 8985a411d3cSmpi #endif /* NFSCLIENT */ 899df930be7Sderaadt 900df930be7Sderaadt out: 901df930be7Sderaadt m_freem(m); 902df930be7Sderaadt } 903df930be7Sderaadt 904df930be7Sderaadt /* 905df930be7Sderaadt * Send a RARP request for the ip address of the specified interface. 906df930be7Sderaadt * The request should be RFC 903-compliant. 907df930be7Sderaadt */ 908df930be7Sderaadt void 90920b3612bSdhill revarprequest(struct ifnet *ifp) 910df930be7Sderaadt { 911df930be7Sderaadt struct sockaddr sa; 912df930be7Sderaadt struct mbuf *m; 913df930be7Sderaadt struct ether_header *eh; 914df930be7Sderaadt struct ether_arp *ea; 915df930be7Sderaadt struct arpcom *ac = (struct arpcom *)ifp; 916df930be7Sderaadt 917df930be7Sderaadt if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) 918df930be7Sderaadt return; 919df930be7Sderaadt m->m_len = sizeof(*ea); 920df930be7Sderaadt m->m_pkthdr.len = sizeof(*ea); 921da17b520Sclaudio m->m_pkthdr.ph_rtableid = ifp->if_rdomain; 922a5e12720Svgross m->m_pkthdr.pf.prio = ifp->if_llprio; 923da17b520Sclaudio m_align(m, sizeof(*ea)); 924df930be7Sderaadt ea = mtod(m, struct ether_arp *); 925df930be7Sderaadt eh = (struct ether_header *)sa.sa_data; 926f8575965Stedu memset(ea, 0, sizeof(*ea)); 92769985460Stedu memcpy(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)); 928df930be7Sderaadt eh->ether_type = htons(ETHERTYPE_REVARP); 929df930be7Sderaadt ea->arp_hrd = htons(ARPHRD_ETHER); 930df930be7Sderaadt ea->arp_pro = htons(ETHERTYPE_IP); 931df930be7Sderaadt ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */ 932df930be7Sderaadt ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */ 933df930be7Sderaadt ea->arp_op = htons(ARPOP_REVREQUEST); 93469985460Stedu memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(ea->arp_tha)); 93569985460Stedu memcpy(ea->arp_sha, ac->ac_enaddr, sizeof(ea->arp_sha)); 93669985460Stedu memcpy(ea->arp_tha, ac->ac_enaddr, sizeof(ea->arp_tha)); 937718ccf97Spascoe sa.sa_family = pseudo_AF_HDRCMPLT; 938df930be7Sderaadt sa.sa_len = sizeof(sa); 939ba4a553cScamield m->m_flags |= M_BCAST; 940d9dc9856Smpi ifp->if_output(ifp, m, &sa, NULL); 941df930be7Sderaadt } 942df930be7Sderaadt 943300480deSderaadt #ifdef NFSCLIENT 944df930be7Sderaadt /* 945df930be7Sderaadt * RARP for the ip address of the specified interface, but also 946df930be7Sderaadt * save the ip address of the server that sent the answer. 947df930be7Sderaadt * Timeout if no response is received. 948df930be7Sderaadt */ 949df930be7Sderaadt int 95020b3612bSdhill revarpwhoarewe(struct ifnet *ifp, struct in_addr *serv_in, 95120b3612bSdhill struct in_addr *clnt_in) 952df930be7Sderaadt { 953df930be7Sderaadt int result, count = 20; 954df930be7Sderaadt 955300480deSderaadt if (revarp_finished) 956df930be7Sderaadt return EIO; 957df930be7Sderaadt 958df5ed9e3Smpi revarp_ifidx = ifp->if_index; 959df930be7Sderaadt while (count--) { 960df930be7Sderaadt revarprequest(ifp); 961bb293f58Smpi result = tsleep_nsec(&revarp_myip, PSOCK, "revarp", 962bb293f58Smpi MSEC_TO_NSEC(500)); 963df930be7Sderaadt if (result != EWOULDBLOCK) 964df930be7Sderaadt break; 965df930be7Sderaadt } 966df5ed9e3Smpi revarp_ifidx = 0; 967300480deSderaadt if (!revarp_finished) 968df930be7Sderaadt return ENETUNREACH; 969df930be7Sderaadt 97069985460Stedu memcpy(serv_in, &revarp_srvip, sizeof(*serv_in)); 97169985460Stedu memcpy(clnt_in, &revarp_myip, sizeof(*clnt_in)); 972df930be7Sderaadt return 0; 973df930be7Sderaadt } 974df930be7Sderaadt 975df930be7Sderaadt /* For compatibility: only saves interface address. */ 976df930be7Sderaadt int 97720b3612bSdhill revarpwhoami(struct in_addr *in, struct ifnet *ifp) 978df930be7Sderaadt { 979df930be7Sderaadt struct in_addr server; 980df930be7Sderaadt return (revarpwhoarewe(ifp, &server, in)); 981df930be7Sderaadt } 982300480deSderaadt #endif /* NFSCLIENT */ 983