blob: c63d0a2e83542f8a7a43fe23a06f34c85d6412ef [file] [log] [blame]
Jeremy Kerr42dc98c2016-02-25 10:23:14 +08001/******************************************************************************
Jeremy Kerrf2101cf2016-03-03 14:49:52 +08002 * 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 ******************************************************************************/
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080017
Jeremy Kerr52e24782016-03-03 14:51:35 +080018#include <err.h>
19#include <errno.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080020#include <stdio.h>
Jeremy Kerrae153682016-03-14 16:59:19 +080021#include <stdbool.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080022#include <stdlib.h>
23#include <string.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080024#include <time.h>
25#include <unistd.h>
26
27#include <sys/ioctl.h>
Jeremy Kerrae153682016-03-14 16:59:19 +080028#include <sys/poll.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080029#include <sys/socket.h>
30
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080031#include <arpa/inet.h>
Jeremy Kerr52e24782016-03-03 14:51:35 +080032#include <netinet/in.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080033
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080034#include <linux/if_arp.h>
Jeremy Kerr52e24782016-03-03 14:51:35 +080035#include <linux/if_ether.h>
Jeremy Kerrae153682016-03-14 16:59:19 +080036#include <linux/if_link.h>
Jeremy Kerr52e24782016-03-03 14:51:35 +080037#include <linux/if_packet.h>
Jeremy Kerrae153682016-03-14 16:59:19 +080038#include <linux/netlink.h>
39#include <linux/rtnetlink.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080040
Jeremy Kerr71f385b2016-03-03 15:18:39 +080041struct eth_addr {
42 uint8_t eth_addr[ETH_ALEN];
43} __attribute__((packed));
44
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080045struct arp_packet {
Jeremy Kerrf2101cf2016-03-03 14:49:52 +080046 struct ethhdr eh;
47 struct arphdr arp;
Jeremy Kerr71f385b2016-03-03 15:18:39 +080048 struct eth_addr src_mac;
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080049 struct in_addr src_ip;
Jeremy Kerr71f385b2016-03-03 15:18:39 +080050 struct eth_addr dest_mac;
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080051 struct in_addr dest_ip;
52} __attribute__((packed));
Jeremy Kerr42dc98c2016-02-25 10:23:14 +080053
Jeremy Kerr17cd0b02016-03-04 11:56:37 +080054struct interface {
Jeremy Kerre09d30d2016-03-04 11:42:03 +080055 int ifindex;
Jeremy Kerr17cd0b02016-03-04 11:56:37 +080056 char ifname[IFNAMSIZ+1];
57 struct eth_addr eth_addr;
58};
59
60struct inarp_ctx {
Jeremy Kerr84963a82016-03-14 14:46:09 +080061 int arp_sd;
Jeremy Kerrae153682016-03-14 16:59:19 +080062 int nl_sd;
Jeremy Kerr17cd0b02016-03-04 11:56:37 +080063 struct interface *interfaces;
64 unsigned int n_interfaces;
Jeremy Kerre09d30d2016-03-04 11:42:03 +080065};
66
Jeremy Kerrae153682016-03-14 16:59:19 +080067/* helpers for rtnetlink message iteration */
68#define for_each_nlmsg(buf, nlmsg, len) \
69 for (nlmsg = (struct nlmsghdr *)buf; \
70 NLMSG_OK(nlmsg, len) && nlmsg->nlmsg_type != NLMSG_DONE; \
71 nlmsg = NLMSG_NEXT(nlmsg, len))
72
73#define for_each_rta(buf, rta, attrlen) \
74 for (rta = (struct rtattr *)(buf); RTA_OK(rta, attrlen); \
75 rta = RTA_NEXT(rta, attrlen))
76
Jeremy Kerr7b3bcf72016-02-25 14:29:55 +080077static int send_arp_packet(int fd,
Jeremy Kerrf2101cf2016-03-03 14:49:52 +080078 int ifindex,
Jeremy Kerr71f385b2016-03-03 15:18:39 +080079 const struct eth_addr *src_mac,
Jeremy Kerr88384bc2016-03-03 14:52:50 +080080 const struct in_addr *src_ip,
Jeremy Kerr71f385b2016-03-03 15:18:39 +080081 const struct eth_addr *dest_mac,
Jeremy Kerr88384bc2016-03-03 14:52:50 +080082 const struct in_addr *dest_ip)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +080083{
Jeremy Kerr89dd5142016-02-25 19:40:02 +080084 struct sockaddr_ll addr;
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080085 struct arp_packet arp;
Jeremy Kerrd7865332016-02-25 14:52:50 +080086 int rc;
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080087
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080088 memset(&arp, 0, sizeof(arp));
89
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080090 /* Prepare our link-layer address: raw packet interface,
91 * using the ifindex interface, receiving ARP packets
92 */
Jeremy Kerr89dd5142016-02-25 19:40:02 +080093 addr.sll_family = PF_PACKET;
94 addr.sll_protocol = htons(ETH_P_ARP);
95 addr.sll_ifindex = ifindex;
96 addr.sll_hatype = ARPHRD_ETHER;
97 addr.sll_pkttype = PACKET_OTHERHOST;
98 addr.sll_halen = ETH_ALEN;
99 memcpy(addr.sll_addr, dest_mac, ETH_ALEN);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800100
101 /* set the frame header */
Jeremy Kerr103a9582016-02-25 19:38:52 +0800102 memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN);
103 memcpy(arp.eh.h_source, src_mac, ETH_ALEN);
Jeremy Kerr5bb869d2016-02-25 18:47:08 +0800104 arp.eh.h_proto = htons(ETH_P_ARP);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800105
106 /* Fill InARP request data for ethernet + ipv4 */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +0800107 arp.arp.ar_hrd = htons(ARPHRD_ETHER);
108 arp.arp.ar_pro = htons(ETH_P_ARP);
109 arp.arp.ar_hln = ETH_ALEN;
110 arp.arp.ar_pln = 4;
111 arp.arp.ar_op = htons(ARPOP_InREPLY);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800112
113 /* fill arp ethernet mac & ipv4 info */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +0800114 memcpy(&arp.src_mac, src_mac, sizeof(arp.src_mac));
115 memcpy(&arp.src_ip, src_ip, sizeof(arp.src_ip));
116 memcpy(&arp.dest_mac, dest_mac, sizeof(arp.dest_mac));
117 memcpy(&arp.dest_ip, dest_ip, sizeof(arp.dest_ip));
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800118
119 /* send the packet */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +0800120 rc = sendto(fd, &arp, sizeof(arp), 0,
Jeremy Kerr89dd5142016-02-25 19:40:02 +0800121 (struct sockaddr *)&addr, sizeof(addr));
Jeremy Kerrd7865332016-02-25 14:52:50 +0800122 if (rc < 0)
123 warn("failure sending ARP response");
124
125 return rc;
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800126}
Jeremy Kerr5514f7b2016-02-25 12:41:44 +0800127
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800128static const char *eth_mac_to_str(const struct eth_addr *mac_addr)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800129{
Jeremy Kerrdf046132016-03-03 15:03:22 +0800130 static char mac_str[ETH_ALEN * (sizeof("00:") - 1)];
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800131 const uint8_t *addr = mac_addr->eth_addr;
Jeremy Kerrdf046132016-03-03 15:03:22 +0800132
133 snprintf(mac_str, sizeof(mac_str),
134 "%02x:%02x:%02x:%02x:%02x:%02x",
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800135 addr[0], addr[1], addr[2],
136 addr[3], addr[4], addr[5]);
Jeremy Kerrdf046132016-03-03 15:03:22 +0800137
138 return mac_str;
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800139}
Jeremy Kerr5514f7b2016-02-25 12:41:44 +0800140
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800141static int do_ifreq(int fd, unsigned long type,
142 const char *ifname, struct ifreq *ifreq)
143{
144 memset(ifreq, 0, sizeof(*ifreq));
Jeremy Kerr7275d5c2016-03-03 15:30:10 +0800145 strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name));
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800146
147 return ioctl(fd, type, ifreq);
148}
149
Jeremy Kerre54e4832016-02-25 17:23:10 +0800150static int get_local_ipaddr(int fd, const char *ifname, struct in_addr *addr)
151{
152 struct sockaddr_in *sa;
153 struct ifreq ifreq;
154 int rc;
155
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800156 rc = do_ifreq(fd, SIOCGIFADDR, ifname, &ifreq);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800157 if (rc) {
Jeremy Kerr1b127c52016-02-25 19:29:02 +0800158 warn("Error querying local IP address for %s", ifname);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800159 return -1;
160 }
161
162 if (ifreq.ifr_addr.sa_family != AF_INET) {
163 warnx("Unknown address family %d in address response",
Jeremy Kerrf2101cf2016-03-03 14:49:52 +0800164 ifreq.ifr_addr.sa_family);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800165 return -1;
166 }
167
168 sa = (struct sockaddr_in *)&ifreq.ifr_addr;
169 memcpy(addr, &sa->sin_addr, sizeof(*addr));
170 return 0;
171}
172
Jeremy Kerra7c07192016-03-04 14:18:41 +0800173static struct interface *find_interface_by_ifindex(struct inarp_ctx *inarp,
174 int ifindex)
175{
176 unsigned int i;
177
178 for (i = 0; i < inarp->n_interfaces; i++) {
179 struct interface *iface = &inarp->interfaces[i];
180 if (iface->ifindex == ifindex)
181 return iface;
182 }
183
184 return NULL;
185}
186
Jeremy Kerrae153682016-03-14 16:59:19 +0800187static int init_netlink(struct inarp_ctx *inarp)
188{
189 struct sockaddr_nl addr;
190 int rc;
191 struct {
192 struct nlmsghdr nlmsg;
193 struct rtgenmsg rtmsg;
194 } msg;
195
196 /* create our socket to listen for rtnetlink events */
197 inarp->nl_sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
198 if (inarp->nl_sd < 0) {
199 warn("Error opening netlink socket");
200 return -1;
201 }
202
203 memset(&addr, 0, sizeof(addr));
204 addr.nl_family = AF_NETLINK;
205 addr.nl_groups = RTMGRP_LINK;
206
207 rc = bind(inarp->nl_sd, (struct sockaddr *)&addr, sizeof(addr));
208 if (rc) {
209 warn("Error binding to netlink address");
210 goto err_close;
211 }
212
213 /* send a query for current interfaces */
214 memset(&msg, 0, sizeof(msg));
215
216 msg.nlmsg.nlmsg_len = sizeof(msg);
217 msg.nlmsg.nlmsg_type = RTM_GETLINK;
218 msg.nlmsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
219 msg.rtmsg.rtgen_family = AF_UNSPEC;
220
221 rc = send(inarp->nl_sd, &msg, sizeof(msg), MSG_NOSIGNAL);
222 if (rc != sizeof(msg)) {
223 warn("Failed to query current links");
224 goto err_close;
225 }
226
227 return 0;
228
229err_close:
230 close(inarp->nl_sd);
231 return -1;
232}
233
234static void netlink_nlmsg_dellink(struct inarp_ctx *inarp,
235 struct interface *iface)
236{
237 int i;
238
239 if (!iface)
240 return;
241
242 printf("dropping interface: %s, [%s]\n", iface->ifname,
243 eth_mac_to_str(&iface->eth_addr));
244
245 /* find the index of the array element to remove */
246 i = iface - inarp->interfaces;
247
248 /* remove interface from our array */
249 inarp->n_interfaces--;
250 inarp->interfaces = realloc(inarp->interfaces,
251 inarp->n_interfaces * sizeof(*iface));
252 memmove(iface, iface + 1,
253 sizeof(*iface) * (inarp->n_interfaces - i));
254
255}
256static void netlink_nlmsg_newlink(struct inarp_ctx *inarp,
257 struct interface *iface, struct ifinfomsg *ifmsg, int len)
258{
259 struct rtattr *attr;
260 bool new = false;
261
262 /*
263 * We shouldn't already have an interface for this ifindex; so create
264 * one. If we do, we'll update the hwaddr and name to the new values.
265 */
266 if (!iface) {
267 inarp->n_interfaces++;
268 inarp->interfaces = realloc(inarp->interfaces,
269 inarp->n_interfaces * sizeof(*iface));
270 iface = &inarp->interfaces[inarp->n_interfaces-1];
271 new = true;
272 }
273
274 memset(iface, 0, sizeof(*iface));
275 iface->ifindex = ifmsg->ifi_index;
276
277 for_each_rta(ifmsg + 1, attr, len) {
278 void *data = RTA_DATA(attr);
279
280 switch (attr->rta_type) {
281 case IFLA_ADDRESS:
282 memcpy(&iface->eth_addr.eth_addr, data,
283 sizeof(iface->eth_addr.eth_addr));
284 break;
285
286 case IFLA_IFNAME:
287 strncpy(iface->ifname, data, IFNAMSIZ);
288 break;
289 }
290 }
291
292 printf("%s interface: %s, [%s]\n",
293 new ? "adding" : "updating",
294 iface->ifname,
295 eth_mac_to_str(&iface->eth_addr));
296 fflush(stdout);
297}
298
299static void netlink_nlmsg(struct inarp_ctx *inarp, struct nlmsghdr *nlmsg)
300{
301 struct ifinfomsg *ifmsg;
302 struct interface *iface;
303 int len;
304
305 len = nlmsg->nlmsg_len - sizeof(*ifmsg);
306 ifmsg = NLMSG_DATA(nlmsg);
307
308 iface = find_interface_by_ifindex(inarp, ifmsg->ifi_index);
309
310 switch (nlmsg->nlmsg_type) {
311 case RTM_DELLINK:
312 netlink_nlmsg_dellink(inarp, iface);
313 break;
314 case RTM_NEWLINK:
315 netlink_nlmsg_newlink(inarp, iface, ifmsg, len);
316 break;
317 default:
318 break;
319 }
320}
321
322static void netlink_recv(struct inarp_ctx *inarp)
323{
324 struct nlmsghdr *nlmsg;
325 uint8_t buf[16384];
326 int len;
327
328 len = recv(inarp->nl_sd, &buf, sizeof(buf), 0);
329 if (len < 0) {
330 warn("Error receiving netlink msg");
331 return;
332 }
333
334 for_each_nlmsg(buf, nlmsg, len)
335 netlink_nlmsg(inarp, nlmsg);
336}
337
338static void arp_recv(struct inarp_ctx *inarp)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800339{
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800340 struct arp_packet inarp_req;
Jeremy Kerrae153682016-03-14 16:59:19 +0800341 struct sockaddr_ll addr;
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800342 struct in_addr local_ip;
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800343 struct interface *iface;
Jeremy Kerra7c07192016-03-04 14:18:41 +0800344 socklen_t addrlen;
Jeremy Kerrae153682016-03-14 16:59:19 +0800345 int len, rc;
346
347 addrlen = sizeof(addr);
348 len = recvfrom(inarp->arp_sd, &inarp_req,
349 sizeof(inarp_req), 0,
350 (struct sockaddr *)&addr, &addrlen);
351 if (len <= 0) {
352 if (errno == EINTR)
353 return;
354 err(EXIT_FAILURE, "Error recieving ARP packet");
355 }
356
357 /*
358 * struct sockaddr_ll allows for 8 bytes of hardware address;
359 * we only need ETH_ALEN for a full ethernet address.
360 */
361 if (addrlen < sizeof(addr) - (8 - ETH_ALEN))
362 return;
363
364 if (addr.sll_family != AF_PACKET)
365 return;
366
367 iface = find_interface_by_ifindex(inarp, addr.sll_ifindex);
368 if (!iface)
369 return;
370
371 /* Is this packet large enough for an inarp? */
372 if ((size_t)len < sizeof(inarp_req))
373 return;
374
375 /* ... is it an inarp request? */
376 if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST)
377 return;
378
379 /* ... for us? */
380 if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN))
381 return;
382
383 printf("src mac: %s\n", eth_mac_to_str(&inarp_req.src_mac));
384 printf("src ip: %s\n", inet_ntoa(inarp_req.src_ip));
385
386 rc = get_local_ipaddr(inarp->arp_sd, iface->ifname,
387 &local_ip);
388 /* if we don't have a local IP address to send, just drop the
389 * request */
390 if (rc)
391 return;
392
393 printf("local mac: %s\n", eth_mac_to_str(&iface->eth_addr));
394 printf("local ip: %s\n", inet_ntoa(local_ip));
395
396 send_arp_packet(inarp->arp_sd, iface->ifindex,
397 &inarp_req.dest_mac,
398 &local_ip,
399 &inarp_req.src_mac,
400 &inarp_req.src_ip);
401}
402
403int main(void)
404{
405 struct inarp_ctx inarp;
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800406 int ret;
Jeremy Kerr115522d2016-02-25 10:24:14 +0800407
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800408 memset(&inarp, 0, sizeof(inarp));
409
Jeremy Kerr84963a82016-03-14 14:46:09 +0800410 inarp.arp_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
411 if (inarp.arp_sd < 0)
Jeremy Kerrd7865332016-02-25 14:52:50 +0800412 err(EXIT_FAILURE, "Error opening ARP socket");
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800413
Jeremy Kerrae153682016-03-14 16:59:19 +0800414 ret = init_netlink(&inarp);
Jeremy Kerr86489a12016-02-25 19:14:09 +0800415 if (ret)
416 exit(EXIT_FAILURE);
Jeremy Kerrd7865332016-02-25 14:52:50 +0800417
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800418 while (1) {
Jeremy Kerrae153682016-03-14 16:59:19 +0800419 struct pollfd pollfds[2];
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800420
Jeremy Kerrae153682016-03-14 16:59:19 +0800421 pollfds[0].fd = inarp.arp_sd;
422 pollfds[0].events = POLLIN;
423 pollfds[1].fd = inarp.nl_sd;
424 pollfds[1].events = POLLIN;
Jeremy Kerra7c07192016-03-04 14:18:41 +0800425
Jeremy Kerrae153682016-03-14 16:59:19 +0800426 ret = poll(pollfds, 2, -1);
427 if (ret < 0)
428 err(EXIT_FAILURE, "Poll failed");
Jeremy Kerra7c07192016-03-04 14:18:41 +0800429
Jeremy Kerrae153682016-03-14 16:59:19 +0800430 if (pollfds[0].revents)
431 arp_recv(&inarp);
Jeremy Kerra7c07192016-03-04 14:18:41 +0800432
Jeremy Kerrae153682016-03-14 16:59:19 +0800433 if (pollfds[1].revents)
434 netlink_recv(&inarp);
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800435
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800436
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800437 }
Jeremy Kerr84963a82016-03-14 14:46:09 +0800438 close(inarp.arp_sd);
Jeremy Kerrae153682016-03-14 16:59:19 +0800439 close(inarp.nl_sd);
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800440 return 0;
441}