xref: /openbmc/inarp/inarp.c (revision 6e296617)
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