xref: /openbsd-src/usr.sbin/rarpd/arptab.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
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