xref: /openbmc/inarp/inarp.c (revision 6e296617f041d7665b2540151646af452088482c)
142dc98c0SJeremy Kerr /******************************************************************************
242dc98c0SJeremy Kerr  * Copyright 2016 Foxconn
355ed2945SJeremy Kerr  * Copyright 2016 IBM Corporation
442dc98c0SJeremy Kerr  *
542dc98c0SJeremy Kerr  * Licensed under the Apache License, Version 2.0 (the "License");
642dc98c0SJeremy Kerr  * you may not use this file except in compliance with the License.
742dc98c0SJeremy Kerr  * You may obtain a copy of the License at
842dc98c0SJeremy Kerr  *
942dc98c0SJeremy Kerr  *	http://www.apache.org/licenses/LICENSE-2.0
1042dc98c0SJeremy Kerr  *
1142dc98c0SJeremy Kerr  * Unless required by applicable law or agreed to in writing, software
1242dc98c0SJeremy Kerr  * distributed under the License is distributed on an "AS IS" BASIS,
1342dc98c0SJeremy Kerr  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1442dc98c0SJeremy Kerr  * See the License for the specific language governing permissions and
1542dc98c0SJeremy Kerr  * limitations under the License.
1642dc98c0SJeremy Kerr  ******************************************************************************/
17982b7bc6SJeremy Kerr 
1852e24785SJeremy Kerr #include <errno.h>
19e3f27cf0SJeremy Kerr #include <stdarg.h>
20982b7bc6SJeremy Kerr #include <stdio.h>
21ae153687SJeremy Kerr #include <stdbool.h>
22982b7bc6SJeremy Kerr #include <stdlib.h>
23982b7bc6SJeremy Kerr #include <string.h>
24e3f27cf0SJeremy Kerr #include <syslog.h>
25982b7bc6SJeremy Kerr #include <time.h>
26982b7bc6SJeremy Kerr #include <unistd.h>
27982b7bc6SJeremy Kerr 
28982b7bc6SJeremy Kerr #include <sys/ioctl.h>
29ae153687SJeremy Kerr #include <sys/poll.h>
30982b7bc6SJeremy Kerr #include <sys/socket.h>
31982b7bc6SJeremy Kerr 
32760d6acbSJeremy Kerr #include <arpa/inet.h>
3352e24785SJeremy Kerr #include <netinet/in.h>
34982b7bc6SJeremy Kerr 
35982b7bc6SJeremy Kerr #include <linux/if_arp.h>
3652e24785SJeremy Kerr #include <linux/if_ether.h>
37ae153687SJeremy Kerr #include <linux/if_link.h>
3852e24785SJeremy Kerr #include <linux/if_packet.h>
39ae153687SJeremy Kerr #include <linux/netlink.h>
40ae153687SJeremy Kerr #include <linux/rtnetlink.h>
41982b7bc6SJeremy Kerr 
4271f385b4SJeremy Kerr struct eth_addr {
4371f385b4SJeremy Kerr 	uint8_t		eth_addr[ETH_ALEN];
4471f385b4SJeremy Kerr } __attribute__((packed));
4571f385b4SJeremy Kerr 
46760d6acbSJeremy Kerr struct arp_packet {
4742dc98c0SJeremy Kerr 	struct ethhdr	eh;
4842dc98c0SJeremy Kerr 	struct arphdr	arp;
4971f385b4SJeremy Kerr 	struct eth_addr	src_mac;
50760d6acbSJeremy Kerr 	struct in_addr	src_ip;
5171f385b4SJeremy Kerr 	struct eth_addr	dest_mac;
52760d6acbSJeremy Kerr 	struct in_addr	dest_ip;
53760d6acbSJeremy Kerr } __attribute__((packed));
5442dc98c0SJeremy Kerr 
5517cd0b01SJeremy Kerr struct interface {
5617cd0b01SJeremy Kerr 	int		ifindex;
5717cd0b01SJeremy Kerr 	char		ifname[IFNAMSIZ+1];
5817cd0b01SJeremy Kerr 	struct eth_addr	eth_addr;
5917cd0b01SJeremy Kerr };
6017cd0b01SJeremy Kerr 
61e09d30d1SJeremy Kerr struct inarp_ctx {
6284963a80SJeremy Kerr 	int			arp_sd;
63ae153687SJeremy Kerr 	int			nl_sd;
6417cd0b01SJeremy Kerr 	struct interface	*interfaces;
6517cd0b01SJeremy Kerr 	unsigned int		n_interfaces;
66e3f27cf0SJeremy Kerr 	bool			syslog;
67e3f27cf0SJeremy Kerr 	bool			debug;
68e09d30d1SJeremy Kerr };
69e09d30d1SJeremy Kerr 
70e3f27cf0SJeremy Kerr static __attribute__((format(printf, 3, 4)))
inarp_log(struct inarp_ctx * inarp,int priority,const char * format,...)71e3f27cf0SJeremy Kerr 	void inarp_log(struct inarp_ctx *inarp,
72e3f27cf0SJeremy Kerr 		int priority,
73e3f27cf0SJeremy Kerr 		const char *format, ...)
74e3f27cf0SJeremy Kerr {
75e3f27cf0SJeremy Kerr 	va_list ap;
76e3f27cf0SJeremy Kerr 
77e3f27cf0SJeremy Kerr 	if (priority > LOG_INFO && !inarp->debug)
78e3f27cf0SJeremy Kerr 		return;
79e3f27cf0SJeremy Kerr 
80e3f27cf0SJeremy Kerr 	va_start(ap, format);
81e3f27cf0SJeremy Kerr 	if (inarp->syslog) {
82e3f27cf0SJeremy Kerr 		vsyslog(priority, format, ap);
83e3f27cf0SJeremy Kerr 	} else {
84e3f27cf0SJeremy Kerr 		vprintf(format, ap);
85e3f27cf0SJeremy Kerr 		printf("\n");
86e3f27cf0SJeremy Kerr 	}
87e3f27cf0SJeremy Kerr 
88e3f27cf0SJeremy Kerr 	va_end(ap);
89e3f27cf0SJeremy Kerr }
90e3f27cf0SJeremy Kerr 
91ae153687SJeremy Kerr /* helpers for rtnetlink message iteration */
92ae153687SJeremy Kerr #define for_each_nlmsg(buf, nlmsg, len) \
93ae153687SJeremy Kerr 	for (nlmsg = (struct nlmsghdr *)buf; \
94ae153687SJeremy Kerr 		NLMSG_OK(nlmsg, len) && nlmsg->nlmsg_type != NLMSG_DONE; \
95ae153687SJeremy Kerr 		nlmsg = NLMSG_NEXT(nlmsg, len))
96ae153687SJeremy Kerr 
97ae153687SJeremy Kerr #define for_each_rta(buf, rta, attrlen) \
98ae153687SJeremy Kerr 	for (rta = (struct rtattr *)(buf); RTA_OK(rta, attrlen); \
99ae153687SJeremy Kerr 			rta = RTA_NEXT(rta, attrlen))
100ae153687SJeremy Kerr 
send_arp_packet(struct inarp_ctx * inarp,int ifindex,const struct eth_addr * src_mac,const struct in_addr * src_ip,const struct eth_addr * dest_mac,const struct in_addr * dest_ip)101e3f27cf0SJeremy Kerr static int send_arp_packet(struct inarp_ctx *inarp,
10242dc98c0SJeremy Kerr 		int ifindex,
10371f385b4SJeremy Kerr 		const struct eth_addr *src_mac,
10488384bccSJeremy Kerr 		const struct in_addr *src_ip,
10571f385b4SJeremy Kerr 		const struct eth_addr *dest_mac,
10688384bccSJeremy Kerr 		const struct in_addr *dest_ip)
10742dc98c0SJeremy Kerr {
10889dd514fSJeremy Kerr 	struct sockaddr_ll addr;
1095bb869d0SJeremy Kerr 	struct arp_packet arp;
110d7865339SJeremy Kerr 	int rc;
111c10bedb4SJeremy Kerr 
1125bb869d0SJeremy Kerr 	memset(&arp, 0, sizeof(arp));
1135bb869d0SJeremy Kerr 
114c10bedb4SJeremy Kerr 	/* Prepare our link-layer address: raw packet interface,
115c10bedb4SJeremy Kerr 	 * using the ifindex interface, receiving ARP packets
116c10bedb4SJeremy Kerr 	 */
11789dd514fSJeremy Kerr 	addr.sll_family = PF_PACKET;
11889dd514fSJeremy Kerr 	addr.sll_protocol = htons(ETH_P_ARP);
11989dd514fSJeremy Kerr 	addr.sll_ifindex = ifindex;
12089dd514fSJeremy Kerr 	addr.sll_hatype = ARPHRD_ETHER;
12189dd514fSJeremy Kerr 	addr.sll_pkttype = PACKET_OTHERHOST;
12289dd514fSJeremy Kerr 	addr.sll_halen = ETH_ALEN;
12389dd514fSJeremy Kerr 	memcpy(addr.sll_addr, dest_mac, ETH_ALEN);
124c10bedb4SJeremy Kerr 
12542dc98c0SJeremy Kerr 	/* set the frame header */
126103a9581SJeremy Kerr 	memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN);
127103a9581SJeremy Kerr 	memcpy(arp.eh.h_source, src_mac, ETH_ALEN);
1285bb869d0SJeremy Kerr 	arp.eh.h_proto = htons(ETH_P_ARP);
129c10bedb4SJeremy Kerr 
130c10bedb4SJeremy Kerr 	/* Fill InARP request data for ethernet + ipv4 */
1315bb869d0SJeremy Kerr 	arp.arp.ar_hrd = htons(ARPHRD_ETHER);
1325bb869d0SJeremy Kerr 	arp.arp.ar_pro = htons(ETH_P_ARP);
1335bb869d0SJeremy Kerr 	arp.arp.ar_hln = ETH_ALEN;
1345bb869d0SJeremy Kerr 	arp.arp.ar_pln = 4;
1355bb869d0SJeremy Kerr 	arp.arp.ar_op = htons(ARPOP_InREPLY);
136c10bedb4SJeremy Kerr 
137c10bedb4SJeremy Kerr 	/* fill arp ethernet mac & ipv4 info */
1385bb869d0SJeremy Kerr 	memcpy(&arp.src_mac, src_mac, sizeof(arp.src_mac));
1395bb869d0SJeremy Kerr 	memcpy(&arp.src_ip, src_ip, sizeof(arp.src_ip));
1405bb869d0SJeremy Kerr 	memcpy(&arp.dest_mac, dest_mac, sizeof(arp.dest_mac));
1415bb869d0SJeremy Kerr 	memcpy(&arp.dest_ip, dest_ip, sizeof(arp.dest_ip));
142c10bedb4SJeremy Kerr 
14342dc98c0SJeremy Kerr 	/* send the packet */
144e3f27cf0SJeremy Kerr 	rc = sendto(inarp->arp_sd, &arp, sizeof(arp), 0,
14589dd514fSJeremy Kerr 			(struct sockaddr *)&addr, sizeof(addr));
146d7865339SJeremy Kerr 	if (rc < 0)
147e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_NOTICE,
148e3f27cf0SJeremy Kerr 				"Failure sending ARP response: %m");
149d7865339SJeremy Kerr 
150d7865339SJeremy Kerr 	return rc;
15142dc98c0SJeremy Kerr }
1525514f7b5SJeremy Kerr 
eth_mac_to_str(const struct eth_addr * mac_addr)15371f385b4SJeremy Kerr static const char *eth_mac_to_str(const struct eth_addr *mac_addr)
15442dc98c0SJeremy Kerr {
155df04613cSJeremy Kerr 	static char mac_str[ETH_ALEN * (sizeof("00:") - 1)];
15671f385b4SJeremy Kerr 	const uint8_t *addr = mac_addr->eth_addr;
157df04613cSJeremy Kerr 
158df04613cSJeremy Kerr 	snprintf(mac_str, sizeof(mac_str),
159df04613cSJeremy Kerr 			"%02x:%02x:%02x:%02x:%02x:%02x",
16071f385b4SJeremy Kerr 			addr[0], addr[1], addr[2],
16171f385b4SJeremy Kerr 			addr[3], addr[4], addr[5]);
162df04613cSJeremy Kerr 
163df04613cSJeremy Kerr 	return mac_str;
16442dc98c0SJeremy Kerr }
1655514f7b5SJeremy Kerr 
do_ifreq(int fd,unsigned long type,const char * ifname,struct ifreq * ifreq)166f3b373f4SJeremy Kerr static int do_ifreq(int fd, unsigned long type,
167f3b373f4SJeremy Kerr 		const char *ifname, struct ifreq *ifreq)
168f3b373f4SJeremy Kerr {
169f3b373f4SJeremy Kerr 	memset(ifreq, 0, sizeof(*ifreq));
1707275d5c2SJeremy Kerr 	strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name));
171f3b373f4SJeremy Kerr 
172f3b373f4SJeremy Kerr 	return ioctl(fd, type, ifreq);
173f3b373f4SJeremy Kerr }
174f3b373f4SJeremy Kerr 
get_local_ipaddr(struct inarp_ctx * inarp,const char * ifname,struct in_addr * addr)175e3f27cf0SJeremy Kerr static int get_local_ipaddr(struct inarp_ctx *inarp,
176e3f27cf0SJeremy Kerr 		const char *ifname, struct in_addr *addr)
177e54e4831SJeremy Kerr {
178e54e4831SJeremy Kerr 	struct sockaddr_in *sa;
179e54e4831SJeremy Kerr 	struct ifreq ifreq;
180e54e4831SJeremy Kerr 	int rc;
181e54e4831SJeremy Kerr 
182e3f27cf0SJeremy Kerr 	rc = do_ifreq(inarp->arp_sd, SIOCGIFADDR, ifname, &ifreq);
183e54e4831SJeremy Kerr 	if (rc) {
184e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_WARNING,
185e3f27cf0SJeremy Kerr 			"Error querying local IP address for %s: %m",
186e3f27cf0SJeremy Kerr 			ifname);
187e54e4831SJeremy Kerr 		return -1;
188e54e4831SJeremy Kerr 	}
189e54e4831SJeremy Kerr 
190e54e4831SJeremy Kerr 	if (ifreq.ifr_addr.sa_family != AF_INET) {
191e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_WARNING,
192e3f27cf0SJeremy Kerr 			"Unknown address family %d in address response",
193e54e4831SJeremy Kerr 			ifreq.ifr_addr.sa_family);
194e54e4831SJeremy Kerr 		return -1;
195e54e4831SJeremy Kerr 	}
196e54e4831SJeremy Kerr 
197e54e4831SJeremy Kerr 	sa = (struct sockaddr_in *)&ifreq.ifr_addr;
198e54e4831SJeremy Kerr 	memcpy(addr, &sa->sin_addr, sizeof(*addr));
199e54e4831SJeremy Kerr 	return 0;
200e54e4831SJeremy Kerr }
201e54e4831SJeremy Kerr 
find_interface_by_ifindex(struct inarp_ctx * inarp,int ifindex)202a7c07191SJeremy Kerr static struct interface *find_interface_by_ifindex(struct inarp_ctx *inarp,
203a7c07191SJeremy Kerr 		int ifindex)
204a7c07191SJeremy Kerr {
205a7c07191SJeremy Kerr 	unsigned int i;
206a7c07191SJeremy Kerr 
207a7c07191SJeremy Kerr 	for (i = 0; i < inarp->n_interfaces; i++) {
208a7c07191SJeremy Kerr 		struct interface *iface = &inarp->interfaces[i];
209a7c07191SJeremy Kerr 		if (iface->ifindex == ifindex)
210a7c07191SJeremy Kerr 			return iface;
211a7c07191SJeremy Kerr 	}
212a7c07191SJeremy Kerr 
213a7c07191SJeremy Kerr 	return NULL;
214a7c07191SJeremy Kerr }
215a7c07191SJeremy Kerr 
init_netlink(struct inarp_ctx * inarp)216ae153687SJeremy Kerr static int init_netlink(struct inarp_ctx *inarp)
217ae153687SJeremy Kerr {
218ae153687SJeremy Kerr 	struct sockaddr_nl addr;
219ae153687SJeremy Kerr 	int rc;
220ae153687SJeremy Kerr 	struct {
221ae153687SJeremy Kerr 		struct nlmsghdr nlmsg;
222ae153687SJeremy Kerr 		struct rtgenmsg rtmsg;
223ae153687SJeremy Kerr 	} msg;
224ae153687SJeremy Kerr 
225ae153687SJeremy Kerr 	/* create our socket to listen for rtnetlink events */
226ae153687SJeremy Kerr 	inarp->nl_sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
227ae153687SJeremy Kerr 	if (inarp->nl_sd < 0) {
228e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_ERR, "Error opening netlink socket: %m");
229ae153687SJeremy Kerr 		return -1;
230ae153687SJeremy Kerr 	}
231ae153687SJeremy Kerr 
232ae153687SJeremy Kerr 	memset(&addr, 0, sizeof(addr));
233ae153687SJeremy Kerr 	addr.nl_family = AF_NETLINK;
234ae153687SJeremy Kerr 	addr.nl_groups = RTMGRP_LINK;
235ae153687SJeremy Kerr 
236ae153687SJeremy Kerr 	rc = bind(inarp->nl_sd, (struct sockaddr *)&addr, sizeof(addr));
237ae153687SJeremy Kerr 	if (rc) {
238e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_ERR,
239e3f27cf0SJeremy Kerr 				"Error binding to netlink address: %m");
240ae153687SJeremy Kerr 		goto err_close;
241ae153687SJeremy Kerr 	}
242ae153687SJeremy Kerr 
243ae153687SJeremy Kerr 	/* send a query for current interfaces */
244ae153687SJeremy Kerr 	memset(&msg, 0, sizeof(msg));
245ae153687SJeremy Kerr 
246ae153687SJeremy Kerr 	msg.nlmsg.nlmsg_len = sizeof(msg);
247ae153687SJeremy Kerr 	msg.nlmsg.nlmsg_type = RTM_GETLINK;
248ae153687SJeremy Kerr 	msg.nlmsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
249ae153687SJeremy Kerr 	msg.rtmsg.rtgen_family = AF_UNSPEC;
250ae153687SJeremy Kerr 
251ae153687SJeremy Kerr 	rc = send(inarp->nl_sd, &msg, sizeof(msg), MSG_NOSIGNAL);
252ae153687SJeremy Kerr 	if (rc != sizeof(msg)) {
253e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_ERR, "Failed to query current links: %m");
254ae153687SJeremy Kerr 		goto err_close;
255ae153687SJeremy Kerr 	}
256ae153687SJeremy Kerr 
257ae153687SJeremy Kerr 	return 0;
258ae153687SJeremy Kerr 
259ae153687SJeremy Kerr err_close:
260ae153687SJeremy Kerr 	close(inarp->nl_sd);
261ae153687SJeremy Kerr 	return -1;
262ae153687SJeremy Kerr }
263ae153687SJeremy Kerr 
netlink_nlmsg_dellink(struct inarp_ctx * inarp,struct interface * iface)264ae153687SJeremy Kerr static void netlink_nlmsg_dellink(struct inarp_ctx *inarp,
265ae153687SJeremy Kerr 		struct interface *iface)
266ae153687SJeremy Kerr {
267ae153687SJeremy Kerr 	int i;
268ae153687SJeremy Kerr 
269ae153687SJeremy Kerr 	if (!iface)
270ae153687SJeremy Kerr 		return;
271ae153687SJeremy Kerr 
272e3f27cf0SJeremy Kerr 	inarp_log(inarp, LOG_NOTICE, "dropping interface: %s, [%s]",
273e3f27cf0SJeremy Kerr 			iface->ifname, eth_mac_to_str(&iface->eth_addr));
274ae153687SJeremy Kerr 
275ae153687SJeremy Kerr 	/* find the index of the array element to remove */
276ae153687SJeremy Kerr 	i = iface - inarp->interfaces;
277ae153687SJeremy Kerr 
278ae153687SJeremy Kerr 	/* remove interface from our array */
279ae153687SJeremy Kerr 	inarp->n_interfaces--;
280ae153687SJeremy Kerr 	inarp->interfaces = realloc(inarp->interfaces,
281ae153687SJeremy Kerr 			inarp->n_interfaces * sizeof(*iface));
282ae153687SJeremy Kerr 	memmove(iface, iface + 1,
283ae153687SJeremy Kerr 			sizeof(*iface) * (inarp->n_interfaces - i));
284ae153687SJeremy Kerr 
285ae153687SJeremy Kerr }
netlink_nlmsg_newlink(struct inarp_ctx * inarp,struct interface * iface,struct ifinfomsg * ifmsg,int len)286ae153687SJeremy Kerr static void netlink_nlmsg_newlink(struct inarp_ctx *inarp,
287ae153687SJeremy Kerr 		struct interface *iface, struct ifinfomsg *ifmsg, int len)
288ae153687SJeremy Kerr {
289ae153687SJeremy Kerr 	struct rtattr *attr;
290ae153687SJeremy Kerr 	bool new = false;
291ae153687SJeremy Kerr 
292ae153687SJeremy Kerr 	/*
293ae153687SJeremy Kerr 	 * We shouldn't already have an interface for this ifindex; so create
294ae153687SJeremy Kerr 	 * one. If we do, we'll update the hwaddr and name to the new values.
295ae153687SJeremy Kerr 	 */
296ae153687SJeremy Kerr 	if (!iface) {
297ae153687SJeremy Kerr 		inarp->n_interfaces++;
298ae153687SJeremy Kerr 		inarp->interfaces = realloc(inarp->interfaces,
299ae153687SJeremy Kerr 				inarp->n_interfaces * sizeof(*iface));
300ae153687SJeremy Kerr 		iface = &inarp->interfaces[inarp->n_interfaces-1];
301ae153687SJeremy Kerr 		new = true;
302ae153687SJeremy Kerr 	}
303ae153687SJeremy Kerr 
304ae153687SJeremy Kerr 	memset(iface, 0, sizeof(*iface));
305ae153687SJeremy Kerr 	iface->ifindex = ifmsg->ifi_index;
306ae153687SJeremy Kerr 
307ae153687SJeremy Kerr 	for_each_rta(ifmsg + 1, attr, len) {
308ae153687SJeremy Kerr 		void *data = RTA_DATA(attr);
309ae153687SJeremy Kerr 
310ae153687SJeremy Kerr 		switch (attr->rta_type) {
311ae153687SJeremy Kerr 		case IFLA_ADDRESS:
312ae153687SJeremy Kerr 			memcpy(&iface->eth_addr.eth_addr, data,
313ae153687SJeremy Kerr 					sizeof(iface->eth_addr.eth_addr));
314ae153687SJeremy Kerr 			break;
315ae153687SJeremy Kerr 
316ae153687SJeremy Kerr 		case IFLA_IFNAME:
317ae153687SJeremy Kerr 			strncpy(iface->ifname, data, IFNAMSIZ);
318ae153687SJeremy Kerr 			break;
319ae153687SJeremy Kerr 		}
320ae153687SJeremy Kerr 	}
321ae153687SJeremy Kerr 
322e3f27cf0SJeremy Kerr 	inarp_log(inarp, LOG_NOTICE, "%s interface: %s, [%s]",
323ae153687SJeremy Kerr 			new ? "adding" : "updating",
324ae153687SJeremy Kerr 			iface->ifname,
325ae153687SJeremy Kerr 			eth_mac_to_str(&iface->eth_addr));
326ae153687SJeremy Kerr }
327ae153687SJeremy Kerr 
netlink_nlmsg(struct inarp_ctx * inarp,struct nlmsghdr * nlmsg)328ae153687SJeremy Kerr static void netlink_nlmsg(struct inarp_ctx *inarp, struct nlmsghdr *nlmsg)
329ae153687SJeremy Kerr {
330ae153687SJeremy Kerr 	struct ifinfomsg *ifmsg;
331ae153687SJeremy Kerr 	struct interface *iface;
332ae153687SJeremy Kerr 	int len;
333ae153687SJeremy Kerr 
334ae153687SJeremy Kerr 	len = nlmsg->nlmsg_len - sizeof(*ifmsg);
335ae153687SJeremy Kerr 	ifmsg = NLMSG_DATA(nlmsg);
336ae153687SJeremy Kerr 
337ae153687SJeremy Kerr 	iface = find_interface_by_ifindex(inarp, ifmsg->ifi_index);
338ae153687SJeremy Kerr 
339ae153687SJeremy Kerr 	switch (nlmsg->nlmsg_type) {
340ae153687SJeremy Kerr 	case RTM_DELLINK:
341ae153687SJeremy Kerr 		netlink_nlmsg_dellink(inarp, iface);
342ae153687SJeremy Kerr 		break;
343ae153687SJeremy Kerr 	case RTM_NEWLINK:
344ae153687SJeremy Kerr 		netlink_nlmsg_newlink(inarp, iface, ifmsg, len);
345ae153687SJeremy Kerr 		break;
346ae153687SJeremy Kerr 	default:
347ae153687SJeremy Kerr 		break;
348ae153687SJeremy Kerr 	}
349ae153687SJeremy Kerr }
350ae153687SJeremy Kerr 
netlink_recv(struct inarp_ctx * inarp)351ae153687SJeremy Kerr static void netlink_recv(struct inarp_ctx *inarp)
352ae153687SJeremy Kerr {
353ae153687SJeremy Kerr 	struct nlmsghdr *nlmsg;
354ae153687SJeremy Kerr 	uint8_t buf[16384];
355ae153687SJeremy Kerr 	int len;
356ae153687SJeremy Kerr 
357ae153687SJeremy Kerr 	len = recv(inarp->nl_sd, &buf, sizeof(buf), 0);
358ae153687SJeremy Kerr 	if (len < 0) {
359e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_NOTICE, "Error receiving netlink msg");
360ae153687SJeremy Kerr 		return;
361ae153687SJeremy Kerr 	}
362ae153687SJeremy Kerr 
36304d1f97fSPatrick Williams 	size_t len_unsigned = (size_t)len;
36404d1f97fSPatrick Williams 
36504d1f97fSPatrick Williams 	for_each_nlmsg(buf, nlmsg, len_unsigned)
366ae153687SJeremy Kerr 		netlink_nlmsg(inarp, nlmsg);
367ae153687SJeremy Kerr }
368ae153687SJeremy Kerr 
arp_recv(struct inarp_ctx * inarp)369ae153687SJeremy Kerr static void arp_recv(struct inarp_ctx *inarp)
37042dc98c0SJeremy Kerr {
3713f8a28e6SJeremy Kerr 	struct arp_packet inarp_req;
372ae153687SJeremy Kerr 	struct sockaddr_ll addr;
373e09d30d1SJeremy Kerr 	struct in_addr local_ip;
37417cd0b01SJeremy Kerr 	struct interface *iface;
375a7c07191SJeremy Kerr 	socklen_t addrlen;
376ae153687SJeremy Kerr 	int len, rc;
377115522dfSJeremy Kerr 
378a7c07191SJeremy Kerr 	addrlen = sizeof(addr);
379ae153687SJeremy Kerr 	len = recvfrom(inarp->arp_sd, &inarp_req,
38084963a80SJeremy Kerr 			sizeof(inarp_req), 0,
381a7c07191SJeremy Kerr 			(struct sockaddr *)&addr, &addrlen);
3823f8a28e6SJeremy Kerr 	if (len <= 0) {
38377cafea5SJeremy Kerr 		if (errno == EINTR)
384ae153687SJeremy Kerr 			return;
385e3f27cf0SJeremy Kerr 		inarp_log(inarp, LOG_WARNING,
386*6e296617SGunnar Mills 				"Error receiving ARP packet");
38777cafea5SJeremy Kerr 	}
38877cafea5SJeremy Kerr 
389a7c07191SJeremy Kerr 	/*
390a7c07191SJeremy Kerr 	 * struct sockaddr_ll allows for 8 bytes of hardware address;
391a7c07191SJeremy Kerr 	 * we only need ETH_ALEN for a full ethernet address.
392a7c07191SJeremy Kerr 	 */
393a7c07191SJeremy Kerr 	if (addrlen < sizeof(addr) - (8 - ETH_ALEN))
394ae153687SJeremy Kerr 		return;
395a7c07191SJeremy Kerr 
396a7c07191SJeremy Kerr 	if (addr.sll_family != AF_PACKET)
397ae153687SJeremy Kerr 		return;
398a7c07191SJeremy Kerr 
399ae153687SJeremy Kerr 	iface = find_interface_by_ifindex(inarp, addr.sll_ifindex);
400a7c07191SJeremy Kerr 	if (!iface)
401ae153687SJeremy Kerr 		return;
402a7c07191SJeremy Kerr 
4033f8a28e6SJeremy Kerr 	/* Is this packet large enough for an inarp? */
4043f8a28e6SJeremy Kerr 	if ((size_t)len < sizeof(inarp_req))
405ae153687SJeremy Kerr 		return;
4063f8a28e6SJeremy Kerr 
4073f8a28e6SJeremy Kerr 	/* ... is it an inarp request? */
4083f8a28e6SJeremy Kerr 	if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST)
409ae153687SJeremy Kerr 		return;
41077cafea5SJeremy Kerr 
41177cafea5SJeremy Kerr 	/* ... for us? */
41217cd0b01SJeremy Kerr 	if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN))
413ae153687SJeremy Kerr 		return;
41477cafea5SJeremy Kerr 
415e3f27cf0SJeremy Kerr 	inarp_log(inarp, LOG_DEBUG,
416e3f27cf0SJeremy Kerr 			"request from src mac: %s",
417e3f27cf0SJeremy Kerr 			eth_mac_to_str(&inarp_req.src_mac));
41877cafea5SJeremy Kerr 
419e3f27cf0SJeremy Kerr 	rc = get_local_ipaddr(inarp, iface->ifname, &local_ip);
420e54e4831SJeremy Kerr 	/* if we don't have a local IP address to send, just drop the
421e54e4831SJeremy Kerr 	 * request */
422ae153687SJeremy Kerr 	if (rc)
423ae153687SJeremy Kerr 		return;
424e3f27cf0SJeremy Kerr 	inarp_log(inarp, LOG_DEBUG,
425e3f27cf0SJeremy Kerr 			"responding with %s ip %s",
426e3f27cf0SJeremy Kerr 			eth_mac_to_str(&iface->eth_addr),
427e3f27cf0SJeremy Kerr 			inet_ntoa(local_ip));
428492de5dfSJeremy Kerr 
429e3f27cf0SJeremy Kerr 	send_arp_packet(inarp, iface->ifindex,
43071f385b4SJeremy Kerr 			&inarp_req.dest_mac,
431e54e4831SJeremy Kerr 			&local_ip,
43271f385b4SJeremy Kerr 			&inarp_req.src_mac,
4333f8a28e6SJeremy Kerr 			&inarp_req.src_ip);
43442dc98c0SJeremy Kerr }
435ae153687SJeremy Kerr 
main(int argc,char ** argv)436e3f27cf0SJeremy Kerr int main(int argc, char **argv)
437ae153687SJeremy Kerr {
438ae153687SJeremy Kerr 	struct inarp_ctx inarp;
439e3f27cf0SJeremy Kerr 	int ret, i;
440ae153687SJeremy Kerr 
441ae153687SJeremy Kerr 	memset(&inarp, 0, sizeof(inarp));
442ae153687SJeremy Kerr 
443e3f27cf0SJeremy Kerr 	inarp.syslog = true;
444e3f27cf0SJeremy Kerr 
445e3f27cf0SJeremy Kerr 	for (i = 1; i < argc; i++) {
446e3f27cf0SJeremy Kerr 		if (!strcmp(argv[i], "--debug"))
447e3f27cf0SJeremy Kerr 			inarp.debug = true;
448e3f27cf0SJeremy Kerr 		else if (!strcmp(argv[i], "--no-syslog"))
449e3f27cf0SJeremy Kerr 			inarp.syslog = false;
450e3f27cf0SJeremy Kerr 	}
451e3f27cf0SJeremy Kerr 
452e3f27cf0SJeremy Kerr 	if (inarp.syslog)
453e3f27cf0SJeremy Kerr 		openlog("inarp", 0, LOG_DAEMON);
454e3f27cf0SJeremy Kerr 
455ae153687SJeremy Kerr 	inarp.arp_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
456e3f27cf0SJeremy Kerr 	if (inarp.arp_sd < 0) {
457e3f27cf0SJeremy Kerr 		inarp_log(&inarp, LOG_ERR, "Error opening ARP socket");
458e3f27cf0SJeremy Kerr 		exit(EXIT_FAILURE);
459e3f27cf0SJeremy Kerr 	}
460ae153687SJeremy Kerr 
461ae153687SJeremy Kerr 	ret = init_netlink(&inarp);
462ae153687SJeremy Kerr 	if (ret)
463ae153687SJeremy Kerr 		exit(EXIT_FAILURE);
464ae153687SJeremy Kerr 
465ae153687SJeremy Kerr 	while (1) {
466ae153687SJeremy Kerr 		struct pollfd pollfds[2];
467ae153687SJeremy Kerr 
468ae153687SJeremy Kerr 		pollfds[0].fd = inarp.arp_sd;
469ae153687SJeremy Kerr 		pollfds[0].events = POLLIN;
470ae153687SJeremy Kerr 		pollfds[1].fd = inarp.nl_sd;
471ae153687SJeremy Kerr 		pollfds[1].events = POLLIN;
472ae153687SJeremy Kerr 
473ae153687SJeremy Kerr 		ret = poll(pollfds, 2, -1);
474e3f27cf0SJeremy Kerr 		if (ret < 0) {
475e3f27cf0SJeremy Kerr 			inarp_log(&inarp, LOG_ERR, "poll failed, exiting");
476e3f27cf0SJeremy Kerr 			break;
477e3f27cf0SJeremy Kerr 		}
478ae153687SJeremy Kerr 
479ae153687SJeremy Kerr 		if (pollfds[0].revents)
480ae153687SJeremy Kerr 			arp_recv(&inarp);
481ae153687SJeremy Kerr 
482ae153687SJeremy Kerr 		if (pollfds[1].revents)
483ae153687SJeremy Kerr 			netlink_recv(&inarp);
484ae153687SJeremy Kerr 
485ae153687SJeremy Kerr 
486ae153687SJeremy Kerr 	}
48784963a80SJeremy Kerr 	close(inarp.arp_sd);
488ae153687SJeremy Kerr 	close(inarp.nl_sd);
48942dc98c0SJeremy Kerr 	return 0;
49042dc98c0SJeremy Kerr }
491