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