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