Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 1 | /****************************************************************************** |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 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 | ******************************************************************************/ |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 17 | |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 18 | #include <errno.h> |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 19 | #include <stdarg.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 20 | #include <stdio.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 21 | #include <stdbool.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 24 | #include <syslog.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 25 | #include <time.h> |
| 26 | #include <unistd.h> |
| 27 | |
| 28 | #include <sys/ioctl.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 29 | #include <sys/poll.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 30 | #include <sys/socket.h> |
| 31 | |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 32 | #include <arpa/inet.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 33 | #include <netinet/in.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 34 | |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 35 | #include <linux/if_arp.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 36 | #include <linux/if_ether.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 37 | #include <linux/if_link.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 38 | #include <linux/if_packet.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 39 | #include <linux/netlink.h> |
| 40 | #include <linux/rtnetlink.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 41 | |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 42 | struct eth_addr { |
| 43 | uint8_t eth_addr[ETH_ALEN]; |
| 44 | } __attribute__((packed)); |
| 45 | |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 46 | struct arp_packet { |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 47 | struct ethhdr eh; |
| 48 | struct arphdr arp; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 49 | struct eth_addr src_mac; |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 50 | struct in_addr src_ip; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 51 | struct eth_addr dest_mac; |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 52 | struct in_addr dest_ip; |
| 53 | } __attribute__((packed)); |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 54 | |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 55 | struct interface { |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 56 | int ifindex; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 57 | char ifname[IFNAMSIZ+1]; |
| 58 | struct eth_addr eth_addr; |
| 59 | }; |
| 60 | |
| 61 | struct inarp_ctx { |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 62 | int arp_sd; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 63 | int nl_sd; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 64 | struct interface *interfaces; |
| 65 | unsigned int n_interfaces; |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 66 | bool syslog; |
| 67 | bool debug; |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 68 | }; |
| 69 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 70 | static __attribute__((format(printf, 3, 4))) |
| 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 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 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 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 101 | static int send_arp_packet(struct inarp_ctx *inarp, |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 102 | int ifindex, |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 103 | const struct eth_addr *src_mac, |
Jeremy Kerr | 88384bc | 2016-03-03 14:52:50 +0800 | [diff] [blame] | 104 | const struct in_addr *src_ip, |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 105 | const struct eth_addr *dest_mac, |
Jeremy Kerr | 88384bc | 2016-03-03 14:52:50 +0800 | [diff] [blame] | 106 | const struct in_addr *dest_ip) |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 107 | { |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 108 | struct sockaddr_ll addr; |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 109 | struct arp_packet arp; |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 110 | int rc; |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 111 | |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 112 | memset(&arp, 0, sizeof(arp)); |
| 113 | |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 114 | /* Prepare our link-layer address: raw packet interface, |
| 115 | * using the ifindex interface, receiving ARP packets |
| 116 | */ |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 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); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 124 | |
| 125 | /* set the frame header */ |
Jeremy Kerr | 103a958 | 2016-02-25 19:38:52 +0800 | [diff] [blame] | 126 | memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN); |
| 127 | memcpy(arp.eh.h_source, src_mac, ETH_ALEN); |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 128 | arp.eh.h_proto = htons(ETH_P_ARP); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 129 | |
| 130 | /* Fill InARP request data for ethernet + ipv4 */ |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 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); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 136 | |
| 137 | /* fill arp ethernet mac & ipv4 info */ |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 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)); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 142 | |
| 143 | /* send the packet */ |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 144 | rc = sendto(inarp->arp_sd, &arp, sizeof(arp), 0, |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 145 | (struct sockaddr *)&addr, sizeof(addr)); |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 146 | if (rc < 0) |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 147 | inarp_log(inarp, LOG_NOTICE, |
| 148 | "Failure sending ARP response: %m"); |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 149 | |
| 150 | return rc; |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 151 | } |
Jeremy Kerr | 5514f7b | 2016-02-25 12:41:44 +0800 | [diff] [blame] | 152 | |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 153 | static const char *eth_mac_to_str(const struct eth_addr *mac_addr) |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 154 | { |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 155 | static char mac_str[ETH_ALEN * (sizeof("00:") - 1)]; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 156 | const uint8_t *addr = mac_addr->eth_addr; |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 157 | |
| 158 | snprintf(mac_str, sizeof(mac_str), |
| 159 | "%02x:%02x:%02x:%02x:%02x:%02x", |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 160 | addr[0], addr[1], addr[2], |
| 161 | addr[3], addr[4], addr[5]); |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 162 | |
| 163 | return mac_str; |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 164 | } |
Jeremy Kerr | 5514f7b | 2016-02-25 12:41:44 +0800 | [diff] [blame] | 165 | |
Jeremy Kerr | f3b373f | 2016-02-25 19:24:59 +0800 | [diff] [blame] | 166 | static int do_ifreq(int fd, unsigned long type, |
| 167 | const char *ifname, struct ifreq *ifreq) |
| 168 | { |
| 169 | memset(ifreq, 0, sizeof(*ifreq)); |
Jeremy Kerr | 7275d5c | 2016-03-03 15:30:10 +0800 | [diff] [blame] | 170 | strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name)); |
Jeremy Kerr | f3b373f | 2016-02-25 19:24:59 +0800 | [diff] [blame] | 171 | |
| 172 | return ioctl(fd, type, ifreq); |
| 173 | } |
| 174 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 175 | static int get_local_ipaddr(struct inarp_ctx *inarp, |
| 176 | const char *ifname, struct in_addr *addr) |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 177 | { |
| 178 | struct sockaddr_in *sa; |
| 179 | struct ifreq ifreq; |
| 180 | int rc; |
| 181 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 182 | rc = do_ifreq(inarp->arp_sd, SIOCGIFADDR, ifname, &ifreq); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 183 | if (rc) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 184 | inarp_log(inarp, LOG_WARNING, |
| 185 | "Error querying local IP address for %s: %m", |
| 186 | ifname); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 187 | return -1; |
| 188 | } |
| 189 | |
| 190 | if (ifreq.ifr_addr.sa_family != AF_INET) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 191 | inarp_log(inarp, LOG_WARNING, |
| 192 | "Unknown address family %d in address response", |
| 193 | ifreq.ifr_addr.sa_family); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 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 | |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 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 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 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) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 228 | inarp_log(inarp, LOG_ERR, "Error opening netlink socket: %m"); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 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) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 238 | inarp_log(inarp, LOG_ERR, |
| 239 | "Error binding to netlink address: %m"); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 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)) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 253 | inarp_log(inarp, LOG_ERR, "Failed to query current links: %m"); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 254 | goto err_close; |
| 255 | } |
| 256 | |
| 257 | return 0; |
| 258 | |
| 259 | err_close: |
| 260 | close(inarp->nl_sd); |
| 261 | return -1; |
| 262 | } |
| 263 | |
| 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 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 272 | inarp_log(inarp, LOG_NOTICE, "dropping interface: %s, [%s]", |
| 273 | iface->ifname, eth_mac_to_str(&iface->eth_addr)); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 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 | } |
| 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 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 322 | inarp_log(inarp, LOG_NOTICE, "%s interface: %s, [%s]", |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 323 | new ? "adding" : "updating", |
| 324 | iface->ifname, |
| 325 | eth_mac_to_str(&iface->eth_addr)); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 326 | } |
| 327 | |
| 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 | |
| 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) { |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 359 | inarp_log(inarp, LOG_NOTICE, "Error receiving netlink msg"); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 360 | return; |
| 361 | } |
| 362 | |
| 363 | for_each_nlmsg(buf, nlmsg, len) |
| 364 | netlink_nlmsg(inarp, nlmsg); |
| 365 | } |
| 366 | |
| 367 | static void arp_recv(struct inarp_ctx *inarp) |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 368 | { |
Jeremy Kerr | 3f8a28e | 2016-02-25 18:56:01 +0800 | [diff] [blame] | 369 | struct arp_packet inarp_req; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 370 | struct sockaddr_ll addr; |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 371 | struct in_addr local_ip; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 372 | struct interface *iface; |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 373 | socklen_t addrlen; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 374 | int len, rc; |
| 375 | |
| 376 | addrlen = sizeof(addr); |
| 377 | len = recvfrom(inarp->arp_sd, &inarp_req, |
| 378 | sizeof(inarp_req), 0, |
| 379 | (struct sockaddr *)&addr, &addrlen); |
| 380 | if (len <= 0) { |
| 381 | if (errno == EINTR) |
| 382 | return; |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 383 | inarp_log(inarp, LOG_WARNING, |
| 384 | "Error recieving ARP packet"); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | /* |
| 388 | * struct sockaddr_ll allows for 8 bytes of hardware address; |
| 389 | * we only need ETH_ALEN for a full ethernet address. |
| 390 | */ |
| 391 | if (addrlen < sizeof(addr) - (8 - ETH_ALEN)) |
| 392 | return; |
| 393 | |
| 394 | if (addr.sll_family != AF_PACKET) |
| 395 | return; |
| 396 | |
| 397 | iface = find_interface_by_ifindex(inarp, addr.sll_ifindex); |
| 398 | if (!iface) |
| 399 | return; |
| 400 | |
| 401 | /* Is this packet large enough for an inarp? */ |
| 402 | if ((size_t)len < sizeof(inarp_req)) |
| 403 | return; |
| 404 | |
| 405 | /* ... is it an inarp request? */ |
| 406 | if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST) |
| 407 | return; |
| 408 | |
| 409 | /* ... for us? */ |
| 410 | if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN)) |
| 411 | return; |
| 412 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 413 | inarp_log(inarp, LOG_DEBUG, |
| 414 | "request from src mac: %s", |
| 415 | eth_mac_to_str(&inarp_req.src_mac)); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 416 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 417 | rc = get_local_ipaddr(inarp, iface->ifname, &local_ip); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 418 | /* if we don't have a local IP address to send, just drop the |
| 419 | * request */ |
| 420 | if (rc) |
| 421 | return; |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 422 | inarp_log(inarp, LOG_DEBUG, |
| 423 | "responding with %s ip %s", |
| 424 | eth_mac_to_str(&iface->eth_addr), |
| 425 | inet_ntoa(local_ip)); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 426 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 427 | send_arp_packet(inarp, iface->ifindex, |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 428 | &inarp_req.dest_mac, |
| 429 | &local_ip, |
| 430 | &inarp_req.src_mac, |
| 431 | &inarp_req.src_ip); |
| 432 | } |
| 433 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 434 | int main(int argc, char **argv) |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 435 | { |
| 436 | struct inarp_ctx inarp; |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 437 | int ret, i; |
Jeremy Kerr | 115522d | 2016-02-25 10:24:14 +0800 | [diff] [blame] | 438 | |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 439 | memset(&inarp, 0, sizeof(inarp)); |
| 440 | |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 441 | inarp.syslog = true; |
| 442 | |
| 443 | for (i = 1; i < argc; i++) { |
| 444 | if (!strcmp(argv[i], "--debug")) |
| 445 | inarp.debug = true; |
| 446 | else if (!strcmp(argv[i], "--no-syslog")) |
| 447 | inarp.syslog = false; |
| 448 | } |
| 449 | |
| 450 | if (inarp.syslog) |
| 451 | openlog("inarp", 0, LOG_DAEMON); |
| 452 | |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 453 | inarp.arp_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 454 | if (inarp.arp_sd < 0) { |
| 455 | inarp_log(&inarp, LOG_ERR, "Error opening ARP socket"); |
| 456 | exit(EXIT_FAILURE); |
| 457 | } |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 458 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 459 | ret = init_netlink(&inarp); |
Jeremy Kerr | 86489a1 | 2016-02-25 19:14:09 +0800 | [diff] [blame] | 460 | if (ret) |
| 461 | exit(EXIT_FAILURE); |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 462 | |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 463 | while (1) { |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 464 | struct pollfd pollfds[2]; |
Jeremy Kerr | 77cafea | 2016-02-25 17:15:28 +0800 | [diff] [blame] | 465 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 466 | pollfds[0].fd = inarp.arp_sd; |
| 467 | pollfds[0].events = POLLIN; |
| 468 | pollfds[1].fd = inarp.nl_sd; |
| 469 | pollfds[1].events = POLLIN; |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 470 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 471 | ret = poll(pollfds, 2, -1); |
Jeremy Kerr | e3f27cf | 2016-03-14 17:24:13 +0800 | [diff] [blame] | 472 | if (ret < 0) { |
| 473 | inarp_log(&inarp, LOG_ERR, "poll failed, exiting"); |
| 474 | break; |
| 475 | } |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 476 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 477 | if (pollfds[0].revents) |
| 478 | arp_recv(&inarp); |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 479 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 480 | if (pollfds[1].revents) |
| 481 | netlink_recv(&inarp); |
Jeremy Kerr | 3f8a28e | 2016-02-25 18:56:01 +0800 | [diff] [blame] | 482 | |
Jeremy Kerr | 77cafea | 2016-02-25 17:15:28 +0800 | [diff] [blame] | 483 | |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 484 | } |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 485 | close(inarp.arp_sd); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame] | 486 | close(inarp.nl_sd); |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 487 | return 0; |
| 488 | } |