/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "structs/structs.h" #include "structs/type/array.h" #include "net/if_util.h" #include "util/typed_mem.h" #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) #define ADDADDR(cp, s) \ do { \ memcpy(cp, &s, sizeof(s)); \ cp += ROUNDUP(sizeof(s)); \ } while (0) #define TEMP_EXPIRE (20 * 60) struct rt { struct rt_msghdr m_rtm; char m_space[512]; }; /* * Internal functions */ static int arp_set(int sock, struct in_addr ip, const u_char *ether, int temp, int publish); static int arp_delete(int sock, struct in_addr ip); static int arp_rtmsg(int sock, struct rt *rt, struct sockaddr_inarp *sin, struct sockaddr_dl *sdl, int cmd, int flags, int export_only, int doing_proxy, u_long expiry); /* * Internal variables */ static const struct sockaddr_in so_mask = { 8, 0, 0, { 0xffffffff} }; static const struct sockaddr_inarp zero_sin = { sizeof(zero_sin), AF_INET }; static const struct sockaddr_dl zero_sdl = { sizeof(zero_sdl), AF_LINK }; /* * Get an ARP entry. */ int if_get_arp(struct in_addr ip, u_char *ether) { int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_inarp *sin; struct sockaddr_dl *sdl; /* Get ARP table */ mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) return (-1); needed += 128; if ((buf = MALLOC(TYPED_MEM_TEMP, needed)) == NULL) return (-1); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { FREE(TYPED_MEM_TEMP, buf); return (-1); } /* Find desired entry */ lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; sin = (struct sockaddr_inarp *)(rtm + 1); sdl = (struct sockaddr_dl *)(void *) ((char *)sin + ROUNDUP(sin->sin_len)); if (sin->sin_addr.s_addr != ip.s_addr) continue; if (sdl->sdl_alen == 0) break; memcpy(ether, LLADDR(sdl), ETHER_ADDR_LEN); FREE(TYPED_MEM_TEMP, buf); return (0); } /* Not found */ FREE(TYPED_MEM_TEMP, buf); errno = ENOENT; return (-1); } /* * Set or remove an ARP entry. */ int if_set_arp(struct in_addr ip, const u_char *ether, int temp, int publish) { int ret = -1; int sock; /* Get socket */ if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return (-1); /* Delete any existing entries */ while ((ret = arp_delete(sock, ip)) != -1); if (errno != ENOENT) goto done; /* If not setting a new one, done */ if (ether == NULL) { ret = 0; goto done; } /* Set a new one */ ret = arp_set(sock, ip, ether, temp, publish); done: /* Done */ (void)close(sock); return (ret); } /* * Set an individual arp entry */ static int arp_set(int sock, struct in_addr ip, const u_char *ether, int temp, int publish) { struct rt m_rtmsg; struct sockaddr_inarp sin_m; struct sockaddr_dl sdl_m; struct sockaddr_inarp *sin = &sin_m; struct rt_msghdr *const rtm = &m_rtmsg.m_rtm; struct sockaddr_dl *sdl; int doing_proxy; int export_only; int expiry; int flags; sdl_m = zero_sdl; sin_m = zero_sin; sin->sin_addr = ip; doing_proxy = flags = export_only = expiry = 0; if (temp) expiry = time(NULL) + TEMP_EXPIRE; if (publish) { flags |= RTF_ANNOUNCE; doing_proxy = SIN_PROXY; } memcpy(LLADDR(&sdl_m), ether, ETHER_ADDR_LEN); sdl_m.sdl_alen = 6; tryagain: if (arp_rtmsg(sock, &m_rtmsg, &sin_m, &sdl_m, RTM_GET, flags, export_only, doing_proxy, expiry) < 0) return (-1); sin = (struct sockaddr_inarp *)(rtm + 1); sdl = (struct sockaddr_dl *)(void *) (ROUNDUP(sin->sin_len) + (char *)sin); if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { if (sdl->sdl_family == AF_LINK && #ifdef RTF_LLINFO (rtm->rtm_flags & RTF_LLINFO) != 0 && #endif (rtm->rtm_flags & RTF_GATEWAY) == 0) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN: goto overwrite; default: break; } } if (doing_proxy == 0) { errno = EINVAL; return (-1); } if (sin_m.sin_other & SIN_PROXY) { errno = EINVAL; return (-1); } sin_m.sin_other = SIN_PROXY; export_only = 1; goto tryagain; } overwrite: if (sdl->sdl_family != AF_LINK) { errno = ENOENT; return (-1); } sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (arp_rtmsg(sock, &m_rtmsg, &sin_m, &sdl_m, RTM_ADD, flags, export_only, doing_proxy, expiry)); } /* * Delete an arp entry */ static int arp_delete(int sock, struct in_addr ip) { struct rt m_rtmsg; struct sockaddr_inarp sin_m; struct sockaddr_dl sdl_m; struct sockaddr_inarp *sin = &sin_m; struct rt_msghdr *const rtm = &m_rtmsg.m_rtm; struct sockaddr_dl *sdl; sdl_m = zero_sdl; sin_m = zero_sin; sin->sin_addr = ip; tryagain: if (arp_rtmsg(sock, &m_rtmsg, &sin_m, &sdl_m, RTM_GET, 0, 0, 0, 0) < 0) return (-1); sin = (struct sockaddr_inarp *)(rtm + 1); sdl = (struct sockaddr_dl *)(void *) (ROUNDUP(sin->sin_len) + (char *)sin); if (sdl->sdl_family == AF_LINK && #ifdef RTF_LLINFO (rtm->rtm_flags & RTF_LLINFO) && #endif !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: sin->sin_addr.s_addr = sin_m.sin_addr.s_addr; goto delete; } if (sin_m.sin_other & SIN_PROXY) { errno = ENOENT; return (-1); } sin_m.sin_other = SIN_PROXY; goto tryagain; delete: if (sdl->sdl_family != AF_LINK) { errno = ENOENT; return (-1); } if (arp_rtmsg(sock, &m_rtmsg, &sin_m, &sdl_m, RTM_DELETE, 0, 0, 0, 0) == -1) return (-1); return (0); } static int arp_rtmsg(int sock, struct rt *rt, struct sockaddr_inarp *sin, struct sockaddr_dl *sdl, int cmd, int flags, int export_only, int doing_proxy, u_long expiry) { struct rt_msghdr *const rtm = &rt->m_rtm; const pid_t pid = getpid(); u_char *cp = (u_char *)rt->m_space; static int seq; int l; int rlen; if (cmd == RTM_DELETE) goto doit; memset(rtm, 0, sizeof(*rtm)); rtm->rtm_flags = flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; rtm->rtm_rmx.rmx_expire = expiry; rtm->rtm_inits = RTV_EXPIRE; rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); sin->sin_other = 0; if (doing_proxy) { if (export_only) sin->sin_other = SIN_PROXY; else { rtm->rtm_addrs |= RTA_NETMASK; rtm->rtm_flags &= ~RTF_HOST; } } /* FALLTHROUGH */ case RTM_GET: rtm->rtm_addrs |= RTA_DST; break; default: assert(0); } #define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ memcpy(cp, s, sizeof(*s)); cp += ROUNDUP(sizeof(*s));} NEXTADDR(RTA_DST, sin); NEXTADDR(RTA_GATEWAY, sdl); NEXTADDR(RTA_NETMASK, &so_mask); rtm->rtm_msglen = cp - (u_char *)rtm; doit: l = rtm->rtm_msglen; rtm->rtm_seq = ++seq; rtm->rtm_type = cmd; if ((rlen = write(sock, (char *)rt, l)) < 0) { if (errno != ESRCH || cmd != RTM_DELETE) return (-1); } do { l = read(sock, (char *)rt, sizeof(*rt)); } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); if (l < 0) return (-1); return (0); } /* * Flush all ARP entries. */ int if_flush_arp(void) { int errno_save = errno; int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_inarp *sin; struct sockaddr_dl *sdl; int sock, rtn = -1; /* Get socket */ if ((sock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) return (-1); /* Get ARP table */ mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) goto done; needed += 128; if ((buf = MALLOC(TYPED_MEM_TEMP, needed)) == NULL) goto done; if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { goto done2; } /* Find desired entry */ lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; sin = (struct sockaddr_inarp *)(rtm + 1); sdl = (struct sockaddr_dl *)(void *) ((char *)sin + ROUNDUP(sin->sin_len)); if (sdl->sdl_alen == 0) break; arp_delete(sock, sin->sin_addr); } rtn = 0; done2: FREE(TYPED_MEM_TEMP, buf); done: (void)close(sock); errno = errno_save; return (rtn); }