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 <err.h> |
| 19 | #include <errno.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 | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 24 | #include <time.h> |
| 25 | #include <unistd.h> |
| 26 | |
| 27 | #include <sys/ioctl.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 28 | #include <sys/poll.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 29 | #include <sys/socket.h> |
| 30 | |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 31 | #include <arpa/inet.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 32 | #include <netinet/in.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 33 | |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 34 | #include <linux/if_arp.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 35 | #include <linux/if_ether.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 36 | #include <linux/if_link.h> |
Jeremy Kerr | 52e2478 | 2016-03-03 14:51:35 +0800 | [diff] [blame] | 37 | #include <linux/if_packet.h> |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 38 | #include <linux/netlink.h> |
| 39 | #include <linux/rtnetlink.h> |
Jeremy Kerr | 982b7bc | 2016-02-25 10:54:39 +0800 | [diff] [blame] | 40 | |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 41 | struct eth_addr { |
| 42 | uint8_t eth_addr[ETH_ALEN]; |
| 43 | } __attribute__((packed)); |
| 44 | |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 45 | struct arp_packet { |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 46 | struct ethhdr eh; |
| 47 | struct arphdr arp; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 48 | struct eth_addr src_mac; |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 49 | struct in_addr src_ip; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 50 | struct eth_addr dest_mac; |
Jeremy Kerr | 760d6ac | 2016-02-25 14:45:33 +0800 | [diff] [blame] | 51 | struct in_addr dest_ip; |
| 52 | } __attribute__((packed)); |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 53 | |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 54 | struct interface { |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 55 | int ifindex; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 56 | char ifname[IFNAMSIZ+1]; |
| 57 | struct eth_addr eth_addr; |
| 58 | }; |
| 59 | |
| 60 | struct inarp_ctx { |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 61 | int arp_sd; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 62 | int nl_sd; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 63 | struct interface *interfaces; |
| 64 | unsigned int n_interfaces; |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 65 | }; |
| 66 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 67 | /* 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 Kerr | 7b3bcf7 | 2016-02-25 14:29:55 +0800 | [diff] [blame] | 77 | static int send_arp_packet(int fd, |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 78 | int ifindex, |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 79 | const struct eth_addr *src_mac, |
Jeremy Kerr | 88384bc | 2016-03-03 14:52:50 +0800 | [diff] [blame] | 80 | const struct in_addr *src_ip, |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 81 | const struct eth_addr *dest_mac, |
Jeremy Kerr | 88384bc | 2016-03-03 14:52:50 +0800 | [diff] [blame] | 82 | const struct in_addr *dest_ip) |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 83 | { |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 84 | struct sockaddr_ll addr; |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 85 | struct arp_packet arp; |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 86 | int rc; |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 87 | |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 88 | memset(&arp, 0, sizeof(arp)); |
| 89 | |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 90 | /* Prepare our link-layer address: raw packet interface, |
| 91 | * using the ifindex interface, receiving ARP packets |
| 92 | */ |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 93 | 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 Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 100 | |
| 101 | /* set the frame header */ |
Jeremy Kerr | 103a958 | 2016-02-25 19:38:52 +0800 | [diff] [blame] | 102 | memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN); |
| 103 | memcpy(arp.eh.h_source, src_mac, ETH_ALEN); |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 104 | arp.eh.h_proto = htons(ETH_P_ARP); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 105 | |
| 106 | /* Fill InARP request data for ethernet + ipv4 */ |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 107 | 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 Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 112 | |
| 113 | /* fill arp ethernet mac & ipv4 info */ |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 114 | 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 Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 118 | |
| 119 | /* send the packet */ |
Jeremy Kerr | 5bb869d | 2016-02-25 18:47:08 +0800 | [diff] [blame] | 120 | rc = sendto(fd, &arp, sizeof(arp), 0, |
Jeremy Kerr | 89dd514 | 2016-02-25 19:40:02 +0800 | [diff] [blame] | 121 | (struct sockaddr *)&addr, sizeof(addr)); |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 122 | if (rc < 0) |
| 123 | warn("failure sending ARP response"); |
| 124 | |
| 125 | return rc; |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 126 | } |
Jeremy Kerr | 5514f7b | 2016-02-25 12:41:44 +0800 | [diff] [blame] | 127 | |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 128 | 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] | 129 | { |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 130 | static char mac_str[ETH_ALEN * (sizeof("00:") - 1)]; |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 131 | const uint8_t *addr = mac_addr->eth_addr; |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 132 | |
| 133 | snprintf(mac_str, sizeof(mac_str), |
| 134 | "%02x:%02x:%02x:%02x:%02x:%02x", |
Jeremy Kerr | 71f385b | 2016-03-03 15:18:39 +0800 | [diff] [blame] | 135 | addr[0], addr[1], addr[2], |
| 136 | addr[3], addr[4], addr[5]); |
Jeremy Kerr | df04613 | 2016-03-03 15:03:22 +0800 | [diff] [blame] | 137 | |
| 138 | return mac_str; |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 139 | } |
Jeremy Kerr | 5514f7b | 2016-02-25 12:41:44 +0800 | [diff] [blame] | 140 | |
Jeremy Kerr | f3b373f | 2016-02-25 19:24:59 +0800 | [diff] [blame] | 141 | static int do_ifreq(int fd, unsigned long type, |
| 142 | const char *ifname, struct ifreq *ifreq) |
| 143 | { |
| 144 | memset(ifreq, 0, sizeof(*ifreq)); |
Jeremy Kerr | 7275d5c | 2016-03-03 15:30:10 +0800 | [diff] [blame] | 145 | strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name)); |
Jeremy Kerr | f3b373f | 2016-02-25 19:24:59 +0800 | [diff] [blame] | 146 | |
| 147 | return ioctl(fd, type, ifreq); |
| 148 | } |
| 149 | |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 150 | static 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 Kerr | f3b373f | 2016-02-25 19:24:59 +0800 | [diff] [blame] | 156 | rc = do_ifreq(fd, SIOCGIFADDR, ifname, &ifreq); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 157 | if (rc) { |
Jeremy Kerr | 1b127c5 | 2016-02-25 19:29:02 +0800 | [diff] [blame] | 158 | warn("Error querying local IP address for %s", ifname); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 159 | return -1; |
| 160 | } |
| 161 | |
| 162 | if (ifreq.ifr_addr.sa_family != AF_INET) { |
| 163 | warnx("Unknown address family %d in address response", |
Jeremy Kerr | f2101cf | 2016-03-03 14:49:52 +0800 | [diff] [blame] | 164 | ifreq.ifr_addr.sa_family); |
Jeremy Kerr | e54e483 | 2016-02-25 17:23:10 +0800 | [diff] [blame] | 165 | 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 Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 173 | static 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 Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 187 | static 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 | |
| 229 | err_close: |
| 230 | close(inarp->nl_sd); |
| 231 | return -1; |
| 232 | } |
| 233 | |
| 234 | static 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 | } |
| 256 | static 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 | |
| 299 | static 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 | |
| 322 | static 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 | |
| 338 | static void arp_recv(struct inarp_ctx *inarp) |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 339 | { |
Jeremy Kerr | 3f8a28e | 2016-02-25 18:56:01 +0800 | [diff] [blame] | 340 | struct arp_packet inarp_req; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 341 | struct sockaddr_ll addr; |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 342 | struct in_addr local_ip; |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 343 | struct interface *iface; |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 344 | socklen_t addrlen; |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 345 | 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 | |
| 403 | int main(void) |
| 404 | { |
| 405 | struct inarp_ctx inarp; |
Jeremy Kerr | e09d30d | 2016-03-04 11:42:03 +0800 | [diff] [blame] | 406 | int ret; |
Jeremy Kerr | 115522d | 2016-02-25 10:24:14 +0800 | [diff] [blame] | 407 | |
Jeremy Kerr | 17cd0b0 | 2016-03-04 11:56:37 +0800 | [diff] [blame] | 408 | memset(&inarp, 0, sizeof(inarp)); |
| 409 | |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 410 | inarp.arp_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); |
| 411 | if (inarp.arp_sd < 0) |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 412 | err(EXIT_FAILURE, "Error opening ARP socket"); |
Jeremy Kerr | c10bedb | 2016-02-25 14:26:38 +0800 | [diff] [blame] | 413 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 414 | ret = init_netlink(&inarp); |
Jeremy Kerr | 86489a1 | 2016-02-25 19:14:09 +0800 | [diff] [blame] | 415 | if (ret) |
| 416 | exit(EXIT_FAILURE); |
Jeremy Kerr | d786533 | 2016-02-25 14:52:50 +0800 | [diff] [blame] | 417 | |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 418 | while (1) { |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 419 | struct pollfd pollfds[2]; |
Jeremy Kerr | 77cafea | 2016-02-25 17:15:28 +0800 | [diff] [blame] | 420 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 421 | pollfds[0].fd = inarp.arp_sd; |
| 422 | pollfds[0].events = POLLIN; |
| 423 | pollfds[1].fd = inarp.nl_sd; |
| 424 | pollfds[1].events = POLLIN; |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 425 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 426 | ret = poll(pollfds, 2, -1); |
| 427 | if (ret < 0) |
| 428 | err(EXIT_FAILURE, "Poll failed"); |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 429 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 430 | if (pollfds[0].revents) |
| 431 | arp_recv(&inarp); |
Jeremy Kerr | a7c0719 | 2016-03-04 14:18:41 +0800 | [diff] [blame] | 432 | |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 433 | if (pollfds[1].revents) |
| 434 | netlink_recv(&inarp); |
Jeremy Kerr | 3f8a28e | 2016-02-25 18:56:01 +0800 | [diff] [blame] | 435 | |
Jeremy Kerr | 77cafea | 2016-02-25 17:15:28 +0800 | [diff] [blame] | 436 | |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 437 | } |
Jeremy Kerr | 84963a8 | 2016-03-14 14:46:09 +0800 | [diff] [blame] | 438 | close(inarp.arp_sd); |
Jeremy Kerr | ae15368 | 2016-03-14 16:59:19 +0800 | [diff] [blame^] | 439 | close(inarp.nl_sd); |
Jeremy Kerr | 42dc98c | 2016-02-25 10:23:14 +0800 | [diff] [blame] | 440 | return 0; |
| 441 | } |