xref: /openbmc/inarp/inarp.c (revision 6e296617f041d7665b2540151646af452088482c)
1  /******************************************************************************
2   * Copyright 2016 Foxconn
3   * Copyright 2016 IBM Corporation
4   *
5   * Licensed under the Apache License, Version 2.0 (the "License");
6   * you may not use this file except in compliance with the License.
7   * You may obtain a copy of the License at
8   *
9   *	http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   ******************************************************************************/
17  
18  #include <errno.h>
19  #include <stdarg.h>
20  #include <stdio.h>
21  #include <stdbool.h>
22  #include <stdlib.h>
23  #include <string.h>
24  #include <syslog.h>
25  #include <time.h>
26  #include <unistd.h>
27  
28  #include <sys/ioctl.h>
29  #include <sys/poll.h>
30  #include <sys/socket.h>
31  
32  #include <arpa/inet.h>
33  #include <netinet/in.h>
34  
35  #include <linux/if_arp.h>
36  #include <linux/if_ether.h>
37  #include <linux/if_link.h>
38  #include <linux/if_packet.h>
39  #include <linux/netlink.h>
40  #include <linux/rtnetlink.h>
41  
42  struct eth_addr {
43  	uint8_t		eth_addr[ETH_ALEN];
44  } __attribute__((packed));
45  
46  struct arp_packet {
47  	struct ethhdr	eh;
48  	struct arphdr	arp;
49  	struct eth_addr	src_mac;
50  	struct in_addr	src_ip;
51  	struct eth_addr	dest_mac;
52  	struct in_addr	dest_ip;
53  } __attribute__((packed));
54  
55  struct interface {
56  	int		ifindex;
57  	char		ifname[IFNAMSIZ+1];
58  	struct eth_addr	eth_addr;
59  };
60  
61  struct inarp_ctx {
62  	int			arp_sd;
63  	int			nl_sd;
64  	struct interface	*interfaces;
65  	unsigned int		n_interfaces;
66  	bool			syslog;
67  	bool			debug;
68  };
69  
70  static __attribute__((format(printf, 3, 4)))
inarp_log(struct inarp_ctx * inarp,int priority,const char * format,...)71  	void inarp_log(struct inarp_ctx *inarp,
72  		int priority,
73  		const char *format, ...)
74  {
75  	va_list ap;
76  
77  	if (priority > LOG_INFO && !inarp->debug)
78  		return;
79  
80  	va_start(ap, format);
81  	if (inarp->syslog) {
82  		vsyslog(priority, format, ap);
83  	} else {
84  		vprintf(format, ap);
85  		printf("\n");
86  	}
87  
88  	va_end(ap);
89  }
90  
91  /* helpers for rtnetlink message iteration */
92  #define for_each_nlmsg(buf, nlmsg, len) \
93  	for (nlmsg = (struct nlmsghdr *)buf; \
94  		NLMSG_OK(nlmsg, len) && nlmsg->nlmsg_type != NLMSG_DONE; \
95  		nlmsg = NLMSG_NEXT(nlmsg, len))
96  
97  #define for_each_rta(buf, rta, attrlen) \
98  	for (rta = (struct rtattr *)(buf); RTA_OK(rta, attrlen); \
99  			rta = RTA_NEXT(rta, attrlen))
100  
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)101  static int send_arp_packet(struct inarp_ctx *inarp,
102  		int ifindex,
103  		const struct eth_addr *src_mac,
104  		const struct in_addr *src_ip,
105  		const struct eth_addr *dest_mac,
106  		const struct in_addr *dest_ip)
107  {
108  	struct sockaddr_ll addr;
109  	struct arp_packet arp;
110  	int rc;
111  
112  	memset(&arp, 0, sizeof(arp));
113  
114  	/* Prepare our link-layer address: raw packet interface,
115  	 * using the ifindex interface, receiving ARP packets
116  	 */
117  	addr.sll_family = PF_PACKET;
118  	addr.sll_protocol = htons(ETH_P_ARP);
119  	addr.sll_ifindex = ifindex;
120  	addr.sll_hatype = ARPHRD_ETHER;
121  	addr.sll_pkttype = PACKET_OTHERHOST;
122  	addr.sll_halen = ETH_ALEN;
123  	memcpy(addr.sll_addr, dest_mac, ETH_ALEN);
124  
125  	/* set the frame header */
126  	memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN);
127  	memcpy(arp.eh.h_source, src_mac, ETH_ALEN);
128  	arp.eh.h_proto = htons(ETH_P_ARP);
129  
130  	/* Fill InARP request data for ethernet + ipv4 */
131  	arp.arp.ar_hrd = htons(ARPHRD_ETHER);
132  	arp.arp.ar_pro = htons(ETH_P_ARP);
133  	arp.arp.ar_hln = ETH_ALEN;
134  	arp.arp.ar_pln = 4;
135  	arp.arp.ar_op = htons(ARPOP_InREPLY);
136  
137  	/* fill arp ethernet mac & ipv4 info */
138  	memcpy(&arp.src_mac, src_mac, sizeof(arp.src_mac));
139  	memcpy(&arp.src_ip, src_ip, sizeof(arp.src_ip));
140  	memcpy(&arp.dest_mac, dest_mac, sizeof(arp.dest_mac));
141  	memcpy(&arp.dest_ip, dest_ip, sizeof(arp.dest_ip));
142  
143  	/* send the packet */
144  	rc = sendto(inarp->arp_sd, &arp, sizeof(arp), 0,
145  			(struct sockaddr *)&addr, sizeof(addr));
146  	if (rc < 0)
147  		inarp_log(inarp, LOG_NOTICE,
148  				"Failure sending ARP response: %m");
149  
150  	return rc;
151  }
152  
eth_mac_to_str(const struct eth_addr * mac_addr)153  static const char *eth_mac_to_str(const struct eth_addr *mac_addr)
154  {
155  	static char mac_str[ETH_ALEN * (sizeof("00:") - 1)];
156  	const uint8_t *addr = mac_addr->eth_addr;
157  
158  	snprintf(mac_str, sizeof(mac_str),
159  			"%02x:%02x:%02x:%02x:%02x:%02x",
160  			addr[0], addr[1], addr[2],
161  			addr[3], addr[4], addr[5]);
162  
163  	return mac_str;
164  }
165  
do_ifreq(int fd,unsigned long type,const char * ifname,struct ifreq * ifreq)166  static int do_ifreq(int fd, unsigned long type,
167  		const char *ifname, struct ifreq *ifreq)
168  {
169  	memset(ifreq, 0, sizeof(*ifreq));
170  	strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name));
171  
172  	return ioctl(fd, type, ifreq);
173  }
174  
get_local_ipaddr(struct inarp_ctx * inarp,const char * ifname,struct in_addr * addr)175  static int get_local_ipaddr(struct inarp_ctx *inarp,
176  		const char *ifname, struct in_addr *addr)
177  {
178  	struct sockaddr_in *sa;
179  	struct ifreq ifreq;
180  	int rc;
181  
182  	rc = do_ifreq(inarp->arp_sd, SIOCGIFADDR, ifname, &ifreq);
183  	if (rc) {
184  		inarp_log(inarp, LOG_WARNING,
185  			"Error querying local IP address for %s: %m",
186  			ifname);
187  		return -1;
188  	}
189  
190  	if (ifreq.ifr_addr.sa_family != AF_INET) {
191  		inarp_log(inarp, LOG_WARNING,
192  			"Unknown address family %d in address response",
193  			ifreq.ifr_addr.sa_family);
194  		return -1;
195  	}
196  
197  	sa = (struct sockaddr_in *)&ifreq.ifr_addr;
198  	memcpy(addr, &sa->sin_addr, sizeof(*addr));
199  	return 0;
200  }
201  
find_interface_by_ifindex(struct inarp_ctx * inarp,int ifindex)202  static struct interface *find_interface_by_ifindex(struct inarp_ctx *inarp,
203  		int ifindex)
204  {
205  	unsigned int i;
206  
207  	for (i = 0; i < inarp->n_interfaces; i++) {
208  		struct interface *iface = &inarp->interfaces[i];
209  		if (iface->ifindex == ifindex)
210  			return iface;
211  	}
212  
213  	return NULL;
214  }
215  
init_netlink(struct inarp_ctx * inarp)216  static int init_netlink(struct inarp_ctx *inarp)
217  {
218  	struct sockaddr_nl addr;
219  	int rc;
220  	struct {
221  		struct nlmsghdr nlmsg;
222  		struct rtgenmsg rtmsg;
223  	} msg;
224  
225  	/* create our socket to listen for rtnetlink events */
226  	inarp->nl_sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
227  	if (inarp->nl_sd < 0) {
228  		inarp_log(inarp, LOG_ERR, "Error opening netlink socket: %m");
229  		return -1;
230  	}
231  
232  	memset(&addr, 0, sizeof(addr));
233  	addr.nl_family = AF_NETLINK;
234  	addr.nl_groups = RTMGRP_LINK;
235  
236  	rc = bind(inarp->nl_sd, (struct sockaddr *)&addr, sizeof(addr));
237  	if (rc) {
238  		inarp_log(inarp, LOG_ERR,
239  				"Error binding to netlink address: %m");
240  		goto err_close;
241  	}
242  
243  	/* send a query for current interfaces */
244  	memset(&msg, 0, sizeof(msg));
245  
246  	msg.nlmsg.nlmsg_len = sizeof(msg);
247  	msg.nlmsg.nlmsg_type = RTM_GETLINK;
248  	msg.nlmsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
249  	msg.rtmsg.rtgen_family = AF_UNSPEC;
250  
251  	rc = send(inarp->nl_sd, &msg, sizeof(msg), MSG_NOSIGNAL);
252  	if (rc != sizeof(msg)) {
253  		inarp_log(inarp, LOG_ERR, "Failed to query current links: %m");
254  		goto err_close;
255  	}
256  
257  	return 0;
258  
259  err_close:
260  	close(inarp->nl_sd);
261  	return -1;
262  }
263  
netlink_nlmsg_dellink(struct inarp_ctx * inarp,struct interface * iface)264  static void netlink_nlmsg_dellink(struct inarp_ctx *inarp,
265  		struct interface *iface)
266  {
267  	int i;
268  
269  	if (!iface)
270  		return;
271  
272  	inarp_log(inarp, LOG_NOTICE, "dropping interface: %s, [%s]",
273  			iface->ifname, eth_mac_to_str(&iface->eth_addr));
274  
275  	/* find the index of the array element to remove */
276  	i = iface - inarp->interfaces;
277  
278  	/* remove interface from our array */
279  	inarp->n_interfaces--;
280  	inarp->interfaces = realloc(inarp->interfaces,
281  			inarp->n_interfaces * sizeof(*iface));
282  	memmove(iface, iface + 1,
283  			sizeof(*iface) * (inarp->n_interfaces - i));
284  
285  }
netlink_nlmsg_newlink(struct inarp_ctx * inarp,struct interface * iface,struct ifinfomsg * ifmsg,int len)286  static void netlink_nlmsg_newlink(struct inarp_ctx *inarp,
287  		struct interface *iface, struct ifinfomsg *ifmsg, int len)
288  {
289  	struct rtattr *attr;
290  	bool new = false;
291  
292  	/*
293  	 * We shouldn't already have an interface for this ifindex; so create
294  	 * one. If we do, we'll update the hwaddr and name to the new values.
295  	 */
296  	if (!iface) {
297  		inarp->n_interfaces++;
298  		inarp->interfaces = realloc(inarp->interfaces,
299  				inarp->n_interfaces * sizeof(*iface));
300  		iface = &inarp->interfaces[inarp->n_interfaces-1];
301  		new = true;
302  	}
303  
304  	memset(iface, 0, sizeof(*iface));
305  	iface->ifindex = ifmsg->ifi_index;
306  
307  	for_each_rta(ifmsg + 1, attr, len) {
308  		void *data = RTA_DATA(attr);
309  
310  		switch (attr->rta_type) {
311  		case IFLA_ADDRESS:
312  			memcpy(&iface->eth_addr.eth_addr, data,
313  					sizeof(iface->eth_addr.eth_addr));
314  			break;
315  
316  		case IFLA_IFNAME:
317  			strncpy(iface->ifname, data, IFNAMSIZ);
318  			break;
319  		}
320  	}
321  
322  	inarp_log(inarp, LOG_NOTICE, "%s interface: %s, [%s]",
323  			new ? "adding" : "updating",
324  			iface->ifname,
325  			eth_mac_to_str(&iface->eth_addr));
326  }
327  
netlink_nlmsg(struct inarp_ctx * inarp,struct nlmsghdr * nlmsg)328  static void netlink_nlmsg(struct inarp_ctx *inarp, struct nlmsghdr *nlmsg)
329  {
330  	struct ifinfomsg *ifmsg;
331  	struct interface *iface;
332  	int len;
333  
334  	len = nlmsg->nlmsg_len - sizeof(*ifmsg);
335  	ifmsg = NLMSG_DATA(nlmsg);
336  
337  	iface = find_interface_by_ifindex(inarp, ifmsg->ifi_index);
338  
339  	switch (nlmsg->nlmsg_type) {
340  	case RTM_DELLINK:
341  		netlink_nlmsg_dellink(inarp, iface);
342  		break;
343  	case RTM_NEWLINK:
344  		netlink_nlmsg_newlink(inarp, iface, ifmsg, len);
345  		break;
346  	default:
347  		break;
348  	}
349  }
350  
netlink_recv(struct inarp_ctx * inarp)351  static void netlink_recv(struct inarp_ctx *inarp)
352  {
353  	struct nlmsghdr *nlmsg;
354  	uint8_t buf[16384];
355  	int len;
356  
357  	len = recv(inarp->nl_sd, &buf, sizeof(buf), 0);
358  	if (len < 0) {
359  		inarp_log(inarp, LOG_NOTICE, "Error receiving netlink msg");
360  		return;
361  	}
362  
363  	size_t len_unsigned = (size_t)len;
364  
365  	for_each_nlmsg(buf, nlmsg, len_unsigned)
366  		netlink_nlmsg(inarp, nlmsg);
367  }
368  
arp_recv(struct inarp_ctx * inarp)369  static void arp_recv(struct inarp_ctx *inarp)
370  {
371  	struct arp_packet inarp_req;
372  	struct sockaddr_ll addr;
373  	struct in_addr local_ip;
374  	struct interface *iface;
375  	socklen_t addrlen;
376  	int len, rc;
377  
378  	addrlen = sizeof(addr);
379  	len = recvfrom(inarp->arp_sd, &inarp_req,
380  			sizeof(inarp_req), 0,
381  			(struct sockaddr *)&addr, &addrlen);
382  	if (len <= 0) {
383  		if (errno == EINTR)
384  			return;
385  		inarp_log(inarp, LOG_WARNING,
386  				"Error receiving ARP packet");
387  	}
388  
389  	/*
390  	 * struct sockaddr_ll allows for 8 bytes of hardware address;
391  	 * we only need ETH_ALEN for a full ethernet address.
392  	 */
393  	if (addrlen < sizeof(addr) - (8 - ETH_ALEN))
394  		return;
395  
396  	if (addr.sll_family != AF_PACKET)
397  		return;
398  
399  	iface = find_interface_by_ifindex(inarp, addr.sll_ifindex);
400  	if (!iface)
401  		return;
402  
403  	/* Is this packet large enough for an inarp? */
404  	if ((size_t)len < sizeof(inarp_req))
405  		return;
406  
407  	/* ... is it an inarp request? */
408  	if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST)
409  		return;
410  
411  	/* ... for us? */
412  	if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN))
413  		return;
414  
415  	inarp_log(inarp, LOG_DEBUG,
416  			"request from src mac: %s",
417  			eth_mac_to_str(&inarp_req.src_mac));
418  
419  	rc = get_local_ipaddr(inarp, iface->ifname, &local_ip);
420  	/* if we don't have a local IP address to send, just drop the
421  	 * request */
422  	if (rc)
423  		return;
424  	inarp_log(inarp, LOG_DEBUG,
425  			"responding with %s ip %s",
426  			eth_mac_to_str(&iface->eth_addr),
427  			inet_ntoa(local_ip));
428  
429  	send_arp_packet(inarp, iface->ifindex,
430  			&inarp_req.dest_mac,
431  			&local_ip,
432  			&inarp_req.src_mac,
433  			&inarp_req.src_ip);
434  }
435  
main(int argc,char ** argv)436  int main(int argc, char **argv)
437  {
438  	struct inarp_ctx inarp;
439  	int ret, i;
440  
441  	memset(&inarp, 0, sizeof(inarp));
442  
443  	inarp.syslog = true;
444  
445  	for (i = 1; i < argc; i++) {
446  		if (!strcmp(argv[i], "--debug"))
447  			inarp.debug = true;
448  		else if (!strcmp(argv[i], "--no-syslog"))
449  			inarp.syslog = false;
450  	}
451  
452  	if (inarp.syslog)
453  		openlog("inarp", 0, LOG_DAEMON);
454  
455  	inarp.arp_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
456  	if (inarp.arp_sd < 0) {
457  		inarp_log(&inarp, LOG_ERR, "Error opening ARP socket");
458  		exit(EXIT_FAILURE);
459  	}
460  
461  	ret = init_netlink(&inarp);
462  	if (ret)
463  		exit(EXIT_FAILURE);
464  
465  	while (1) {
466  		struct pollfd pollfds[2];
467  
468  		pollfds[0].fd = inarp.arp_sd;
469  		pollfds[0].events = POLLIN;
470  		pollfds[1].fd = inarp.nl_sd;
471  		pollfds[1].events = POLLIN;
472  
473  		ret = poll(pollfds, 2, -1);
474  		if (ret < 0) {
475  			inarp_log(&inarp, LOG_ERR, "poll failed, exiting");
476  			break;
477  		}
478  
479  		if (pollfds[0].revents)
480  			arp_recv(&inarp);
481  
482  		if (pollfds[1].revents)
483  			netlink_recv(&inarp);
484  
485  
486  	}
487  	close(inarp.arp_sd);
488  	close(inarp.nl_sd);
489  	return 0;
490  }
491