1*df69c215Sderaadt /* $OpenBSD: arptab.c,v 1.31 2019/06/28 13:32:50 deraadt Exp $ */
26c899792Sbluhm
33b5da8e6Sderaadt /*
43b5da8e6Sderaadt * Copyright (c) 1984, 1993
53b5da8e6Sderaadt * The Regents of the University of California. All rights reserved.
63b5da8e6Sderaadt *
73b5da8e6Sderaadt * This code is derived from software contributed to Berkeley by
83b5da8e6Sderaadt * Sun Microsystems, Inc.
93b5da8e6Sderaadt *
103b5da8e6Sderaadt * Redistribution and use in source and binary forms, with or without
113b5da8e6Sderaadt * modification, are permitted provided that the following conditions
123b5da8e6Sderaadt * are met:
133b5da8e6Sderaadt * 1. Redistributions of source code must retain the above copyright
143b5da8e6Sderaadt * notice, this list of conditions and the following disclaimer.
153b5da8e6Sderaadt * 2. Redistributions in binary form must reproduce the above copyright
163b5da8e6Sderaadt * notice, this list of conditions and the following disclaimer in the
173b5da8e6Sderaadt * documentation and/or other materials provided with the distribution.
1829295d1cSmillert * 3. Neither the name of the University nor the names of its contributors
193b5da8e6Sderaadt * may be used to endorse or promote products derived from this software
203b5da8e6Sderaadt * without specific prior written permission.
213b5da8e6Sderaadt *
223b5da8e6Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233b5da8e6Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243b5da8e6Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253b5da8e6Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263b5da8e6Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273b5da8e6Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283b5da8e6Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293b5da8e6Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303b5da8e6Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313b5da8e6Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323b5da8e6Sderaadt * SUCH DAMAGE.
333b5da8e6Sderaadt */
343b5da8e6Sderaadt
353b5da8e6Sderaadt /*
363b5da8e6Sderaadt * set arp table entries
373b5da8e6Sderaadt */
383b5da8e6Sderaadt
393b5da8e6Sderaadt
403b5da8e6Sderaadt #include <sys/socket.h>
41c4b5d4d4Sguenther #include <sys/time.h>
423b5da8e6Sderaadt
433b5da8e6Sderaadt #include <net/if.h>
443b5da8e6Sderaadt #include <net/if_dl.h>
453b5da8e6Sderaadt #include <net/if_types.h>
463b5da8e6Sderaadt #include <net/route.h>
473b5da8e6Sderaadt
483b5da8e6Sderaadt #include <netinet/in.h>
493b5da8e6Sderaadt #include <netinet/if_ether.h>
503b5da8e6Sderaadt
513b5da8e6Sderaadt #include <arpa/inet.h>
523b5da8e6Sderaadt
533b5da8e6Sderaadt #include <netdb.h>
543b5da8e6Sderaadt #include <errno.h>
55f4147939Sguenther #include <fcntl.h>
563b5da8e6Sderaadt #include <stdio.h>
574172d2bdSderaadt #include <unistd.h>
584172d2bdSderaadt #include <stdlib.h>
593b5da8e6Sderaadt #include <syslog.h>
604bc5217dSderaadt #include <string.h>
61828aae0cSderaadt #include <err.h>
623b5da8e6Sderaadt
6399043edbSmpi /* ROUNDUP() is nasty, but it is identical to what's in the kernel. */
6499043edbSmpi #define ROUNDUP(a) \
6599043edbSmpi ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
6699043edbSmpi
6713ee8a54Sderaadt static pid_t pid;
683b5da8e6Sderaadt static int s = -1;
693b5da8e6Sderaadt
7099043edbSmpi int rtget(struct sockaddr_inarp **, struct sockaddr_dl **);
7199043edbSmpi
72cd93351eSderaadt void
arptab_init(void)73cd93351eSderaadt arptab_init(void)
7458467563Sderaadt {
75b47fcd70Skrw s = socket(AF_ROUTE, SOCK_RAW, 0);
76*df69c215Sderaadt if (s == -1)
77828aae0cSderaadt err(1, "arp: socket");
783b5da8e6Sderaadt }
793b5da8e6Sderaadt
803b5da8e6Sderaadt struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
813b5da8e6Sderaadt struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
823b5da8e6Sderaadt struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
8399043edbSmpi struct sockaddr_dl ifp_m = {sizeof(&ifp_m), AF_LINK};
84a80d0aebSbluhm time_t expire_time;
85a80d0aebSbluhm int flags, export_only, doing_proxy;
86828aae0cSderaadt
873b5da8e6Sderaadt struct {
883b5da8e6Sderaadt struct rt_msghdr m_rtm;
893b5da8e6Sderaadt char m_space[512];
903b5da8e6Sderaadt } m_rtmsg;
913b5da8e6Sderaadt
92c72b5b24Smillert int arptab_set(u_char *, u_int32_t);
93c72b5b24Smillert int rtmsg(int);
944172d2bdSderaadt
953b5da8e6Sderaadt /*
963b5da8e6Sderaadt * Set an individual arp entry
973b5da8e6Sderaadt */
984172d2bdSderaadt int
arptab_set(u_char * eaddr,u_int32_t host)99e2a2dac5Sderaadt arptab_set(u_char *eaddr, u_int32_t host)
1003b5da8e6Sderaadt {
1010ac0d02eSmpech struct sockaddr_inarp *sin = &sin_m;
1020ac0d02eSmpech struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
103828aae0cSderaadt struct sockaddr_dl *sdl;
1043b91be6eSbluhm struct timeval now;
1054172d2bdSderaadt int rt;
1063b5da8e6Sderaadt
1073b5da8e6Sderaadt pid = getpid();
1084172d2bdSderaadt
1093b5da8e6Sderaadt sdl_m = blank_sdl;
1103b5da8e6Sderaadt sin_m = blank_sin;
1113b5da8e6Sderaadt sin->sin_addr.s_addr = host;
1124172d2bdSderaadt memcpy((u_char *)LLADDR(&sdl_m), (char *)eaddr, 6);
1133b5da8e6Sderaadt sdl_m.sdl_alen = 6;
114a80d0aebSbluhm expire_time = 0;
115a80d0aebSbluhm doing_proxy = flags = export_only = 0;
1163b91be6eSbluhm gettimeofday(&now, 0);
1173b91be6eSbluhm expire_time = now.tv_sec + 20 * 60;
1183b5da8e6Sderaadt
1193b5da8e6Sderaadt tryagain:
12099043edbSmpi if (rtget(&sin, &sdl)) {
1213b5da8e6Sderaadt syslog(LOG_ERR,"%s: %m", inet_ntoa(sin->sin_addr));
1223b5da8e6Sderaadt return (1);
1233b5da8e6Sderaadt }
12499043edbSmpi
1253b5da8e6Sderaadt if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
1263b5da8e6Sderaadt if (sdl->sdl_family == AF_LINK &&
1273b5da8e6Sderaadt (rtm->rtm_flags & RTF_LLINFO) &&
128828aae0cSderaadt !(rtm->rtm_flags & RTF_GATEWAY))
129828aae0cSderaadt switch (sdl->sdl_type) {
130828aae0cSderaadt case IFT_ETHER:
131828aae0cSderaadt case IFT_FDDI:
132828aae0cSderaadt case IFT_ISO88023:
133828aae0cSderaadt case IFT_ISO88024:
134828aae0cSderaadt case IFT_ISO88025:
1353b5da8e6Sderaadt goto overwrite;
136828aae0cSderaadt default:
137828aae0cSderaadt break;
1383b5da8e6Sderaadt }
1393b5da8e6Sderaadt if (doing_proxy == 0) {
140ea0b9ac4Sderaadt syslog(LOG_ERR, "arptab_set: can only proxy for %s",
14158467563Sderaadt inet_ntoa(sin->sin_addr));
1423b5da8e6Sderaadt return (1);
1433b5da8e6Sderaadt }
1443b5da8e6Sderaadt if (sin_m.sin_other & SIN_PROXY) {
14558467563Sderaadt syslog(LOG_ERR,
146ea0b9ac4Sderaadt "arptab_set: proxy entry exists for non 802 device");
1473b5da8e6Sderaadt return(1);
1483b5da8e6Sderaadt }
1493b5da8e6Sderaadt sin_m.sin_other = SIN_PROXY;
1503b5da8e6Sderaadt export_only = 1;
1513b5da8e6Sderaadt goto tryagain;
1523b5da8e6Sderaadt }
1533b5da8e6Sderaadt overwrite:
1543b5da8e6Sderaadt if (sdl->sdl_family != AF_LINK) {
15558467563Sderaadt syslog(LOG_ERR,
156ea0b9ac4Sderaadt "arptab_set: cannot intuit interface index and type for %s",
15758467563Sderaadt inet_ntoa(sin->sin_addr));
1583b5da8e6Sderaadt return (1);
1593b5da8e6Sderaadt }
1603b5da8e6Sderaadt sdl_m.sdl_type = sdl->sdl_type;
1613b5da8e6Sderaadt sdl_m.sdl_index = sdl->sdl_index;
1624172d2bdSderaadt rt = rtmsg(RTM_ADD);
1634172d2bdSderaadt return (rt);
1643b5da8e6Sderaadt }
1653b5da8e6Sderaadt
1664172d2bdSderaadt int
rtmsg(int cmd)167e2a2dac5Sderaadt rtmsg(int cmd)
1683b5da8e6Sderaadt {
1693b5da8e6Sderaadt static int seq;
1700ac0d02eSmpech struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
1710ac0d02eSmpech char *cp = m_rtmsg.m_space;
1720ac0d02eSmpech int l;
1733b5da8e6Sderaadt
1748b5a6ab3Svisa retry:
1753b5da8e6Sderaadt errno = 0;
1763b5da8e6Sderaadt if (cmd == RTM_DELETE)
1773b5da8e6Sderaadt goto doit;
1784172d2bdSderaadt memset((char *)&m_rtmsg, 0, sizeof(m_rtmsg));
1793b5da8e6Sderaadt rtm->rtm_flags = flags;
1803b5da8e6Sderaadt rtm->rtm_version = RTM_VERSION;
1813b5da8e6Sderaadt
1823b5da8e6Sderaadt switch (cmd) {
1833b5da8e6Sderaadt default:
184ea0b9ac4Sderaadt syslog(LOG_ERR, "arptab_set: internal wrong cmd");
1853b5da8e6Sderaadt exit(1);
1863b5da8e6Sderaadt case RTM_ADD:
1873b5da8e6Sderaadt rtm->rtm_addrs |= RTA_GATEWAY;
1883b5da8e6Sderaadt rtm->rtm_rmx.rmx_expire = expire_time;
1893b5da8e6Sderaadt rtm->rtm_inits = RTV_EXPIRE;
1903b5da8e6Sderaadt rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
1913b5da8e6Sderaadt sin_m.sin_other = 0;
1923b5da8e6Sderaadt if (doing_proxy) {
1933b5da8e6Sderaadt if (export_only)
1943b5da8e6Sderaadt sin_m.sin_other = SIN_PROXY;
1953b5da8e6Sderaadt else {
1963b5da8e6Sderaadt rtm->rtm_addrs |= RTA_NETMASK;
1973b5da8e6Sderaadt rtm->rtm_flags &= ~RTF_HOST;
1983b5da8e6Sderaadt }
1993b5da8e6Sderaadt }
2003b5da8e6Sderaadt /* FALLTHROUGH */
2013b5da8e6Sderaadt case RTM_GET:
20299043edbSmpi rtm->rtm_addrs |= (RTA_DST | RTA_IFP);
2033b5da8e6Sderaadt }
2043b5da8e6Sderaadt #define NEXTADDR(w, s) \
2053b5da8e6Sderaadt if (rtm->rtm_addrs & (w)) { \
2064172d2bdSderaadt memcpy(cp, (char *)&s, sizeof(s)); \
2074172d2bdSderaadt cp += sizeof(s); \
2084172d2bdSderaadt }
2093b5da8e6Sderaadt
2103b5da8e6Sderaadt NEXTADDR(RTA_DST, sin_m);
2113b5da8e6Sderaadt NEXTADDR(RTA_GATEWAY, sdl_m);
2123b5da8e6Sderaadt NEXTADDR(RTA_NETMASK, so_mask);
21399043edbSmpi NEXTADDR(RTA_IFP, ifp_m);
2143b5da8e6Sderaadt
2153b5da8e6Sderaadt rtm->rtm_msglen = cp - (char *)&m_rtmsg;
2163b5da8e6Sderaadt doit:
2173b5da8e6Sderaadt l = rtm->rtm_msglen;
2183b5da8e6Sderaadt rtm->rtm_seq = ++seq;
2193b5da8e6Sderaadt rtm->rtm_type = cmd;
220*df69c215Sderaadt if (write(s, (char *)&m_rtmsg, l) == -1) {
2213b5da8e6Sderaadt if (errno != ESRCH && errno != EEXIST) {
2223b5da8e6Sderaadt syslog(LOG_ERR, "writing to routing socket: %m");
2233b5da8e6Sderaadt return (-1);
2243b5da8e6Sderaadt }
2253b5da8e6Sderaadt }
2263b5da8e6Sderaadt do {
2278b5a6ab3Svisa l = recv(s, (char *)&m_rtmsg, sizeof(m_rtmsg), MSG_DONTWAIT);
2287f9ac982Sclaudio } while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
2297f9ac982Sclaudio rtm->rtm_seq != seq || rtm->rtm_pid != pid));
230*df69c215Sderaadt if (l == -1) {
2318b5a6ab3Svisa if (errno == EAGAIN || errno == EINTR)
2328b5a6ab3Svisa goto retry;
2333b5da8e6Sderaadt syslog(LOG_ERR, "arptab_set: read from routing socket: %m");
2348b5a6ab3Svisa }
2353b5da8e6Sderaadt return (0);
2363b5da8e6Sderaadt }
23799043edbSmpi
23899043edbSmpi int
rtget(struct sockaddr_inarp ** sinp,struct sockaddr_dl ** sdlp)23999043edbSmpi rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp)
24099043edbSmpi {
24199043edbSmpi struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
24299043edbSmpi struct sockaddr_inarp *sin = NULL;
24399043edbSmpi struct sockaddr_dl *sdl = NULL;
24499043edbSmpi struct sockaddr *sa;
24599043edbSmpi char *cp;
246cac6055cSmmcc unsigned int i;
24799043edbSmpi
24899043edbSmpi if (rtmsg(RTM_GET) < 0)
24999043edbSmpi return (1);
25099043edbSmpi
25199043edbSmpi if (rtm->rtm_addrs) {
25299043edbSmpi cp = ((char *)rtm + rtm->rtm_hdrlen);
25399043edbSmpi for (i = 1; i; i <<= 1) {
25499043edbSmpi if (i & rtm->rtm_addrs) {
25599043edbSmpi sa = (struct sockaddr *)cp;
25699043edbSmpi switch (i) {
25799043edbSmpi case RTA_DST:
25899043edbSmpi sin = (struct sockaddr_inarp *)sa;
25999043edbSmpi break;
26099043edbSmpi case RTA_IFP:
26199043edbSmpi sdl = (struct sockaddr_dl *)sa;
26299043edbSmpi break;
26399043edbSmpi default:
26499043edbSmpi break;
26599043edbSmpi }
26699043edbSmpi cp += ROUNDUP(sa->sa_len);
26799043edbSmpi }
26899043edbSmpi }
26999043edbSmpi }
27099043edbSmpi
27199043edbSmpi if (sin == NULL || sdl == NULL)
27299043edbSmpi return (1);
27399043edbSmpi
27499043edbSmpi *sinp = sin;
27599043edbSmpi *sdlp = sdl;
27699043edbSmpi
27799043edbSmpi return (0);
27899043edbSmpi }
279