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 <err.h> 19 #include <errno.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <time.h> 24 #include <unistd.h> 25 26 #include <sys/ioctl.h> 27 #include <sys/socket.h> 28 29 #include <arpa/inet.h> 30 #include <netinet/in.h> 31 32 #include <linux/if_arp.h> 33 #include <linux/if_ether.h> 34 #include <linux/if_packet.h> 35 36 struct eth_addr { 37 uint8_t eth_addr[ETH_ALEN]; 38 } __attribute__((packed)); 39 40 struct arp_packet { 41 struct ethhdr eh; 42 struct arphdr arp; 43 struct eth_addr src_mac; 44 struct in_addr src_ip; 45 struct eth_addr dest_mac; 46 struct in_addr dest_ip; 47 } __attribute__((packed)); 48 49 struct interface { 50 int ifindex; 51 char ifname[IFNAMSIZ+1]; 52 struct eth_addr eth_addr; 53 }; 54 55 struct inarp_ctx { 56 int socket; 57 struct interface *interfaces; 58 unsigned int n_interfaces; 59 }; 60 61 static int send_arp_packet(int fd, 62 int ifindex, 63 const struct eth_addr *src_mac, 64 const struct in_addr *src_ip, 65 const struct eth_addr *dest_mac, 66 const struct in_addr *dest_ip) 67 { 68 struct sockaddr_ll addr; 69 struct arp_packet arp; 70 int rc; 71 72 memset(&arp, 0, sizeof(arp)); 73 74 /* Prepare our link-layer address: raw packet interface, 75 * using the ifindex interface, receiving ARP packets 76 */ 77 addr.sll_family = PF_PACKET; 78 addr.sll_protocol = htons(ETH_P_ARP); 79 addr.sll_ifindex = ifindex; 80 addr.sll_hatype = ARPHRD_ETHER; 81 addr.sll_pkttype = PACKET_OTHERHOST; 82 addr.sll_halen = ETH_ALEN; 83 memcpy(addr.sll_addr, dest_mac, ETH_ALEN); 84 85 /* set the frame header */ 86 memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN); 87 memcpy(arp.eh.h_source, src_mac, ETH_ALEN); 88 arp.eh.h_proto = htons(ETH_P_ARP); 89 90 /* Fill InARP request data for ethernet + ipv4 */ 91 arp.arp.ar_hrd = htons(ARPHRD_ETHER); 92 arp.arp.ar_pro = htons(ETH_P_ARP); 93 arp.arp.ar_hln = ETH_ALEN; 94 arp.arp.ar_pln = 4; 95 arp.arp.ar_op = htons(ARPOP_InREPLY); 96 97 /* fill arp ethernet mac & ipv4 info */ 98 memcpy(&arp.src_mac, src_mac, sizeof(arp.src_mac)); 99 memcpy(&arp.src_ip, src_ip, sizeof(arp.src_ip)); 100 memcpy(&arp.dest_mac, dest_mac, sizeof(arp.dest_mac)); 101 memcpy(&arp.dest_ip, dest_ip, sizeof(arp.dest_ip)); 102 103 /* send the packet */ 104 rc = sendto(fd, &arp, sizeof(arp), 0, 105 (struct sockaddr *)&addr, sizeof(addr)); 106 if (rc < 0) 107 warn("failure sending ARP response"); 108 109 return rc; 110 } 111 112 static const char *eth_mac_to_str(const struct eth_addr *mac_addr) 113 { 114 static char mac_str[ETH_ALEN * (sizeof("00:") - 1)]; 115 const uint8_t *addr = mac_addr->eth_addr; 116 117 snprintf(mac_str, sizeof(mac_str), 118 "%02x:%02x:%02x:%02x:%02x:%02x", 119 addr[0], addr[1], addr[2], 120 addr[3], addr[4], addr[5]); 121 122 return mac_str; 123 } 124 125 static int do_ifreq(int fd, unsigned long type, 126 const char *ifname, struct ifreq *ifreq) 127 { 128 memset(ifreq, 0, sizeof(*ifreq)); 129 strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name)); 130 131 return ioctl(fd, type, ifreq); 132 } 133 134 static int get_local_ipaddr(int fd, const char *ifname, struct in_addr *addr) 135 { 136 struct sockaddr_in *sa; 137 struct ifreq ifreq; 138 int rc; 139 140 rc = do_ifreq(fd, SIOCGIFADDR, ifname, &ifreq); 141 if (rc) { 142 warn("Error querying local IP address for %s", ifname); 143 return -1; 144 } 145 146 if (ifreq.ifr_addr.sa_family != AF_INET) { 147 warnx("Unknown address family %d in address response", 148 ifreq.ifr_addr.sa_family); 149 return -1; 150 } 151 152 sa = (struct sockaddr_in *)&ifreq.ifr_addr; 153 memcpy(addr, &sa->sin_addr, sizeof(*addr)); 154 return 0; 155 } 156 157 static int get_local_hwaddr(int fd, const char *ifname, struct eth_addr *addr) 158 { 159 struct ifreq ifreq; 160 int rc; 161 162 rc = do_ifreq(fd, SIOCGIFHWADDR, ifname, &ifreq); 163 if (rc) { 164 warn("Error querying local MAC address for %s", ifname); 165 return -1; 166 } 167 168 memcpy(addr, ifreq.ifr_hwaddr.sa_data, ETH_ALEN); 169 return 0; 170 } 171 172 static int get_ifindex(int fd, const char *ifname, int *ifindex) 173 { 174 struct ifreq ifreq; 175 int rc; 176 177 rc = do_ifreq(fd, SIOCGIFINDEX, ifname, &ifreq); 178 if (rc < 0) { 179 warn("Error querying interface %s", ifname); 180 return -1; 181 } 182 183 *ifindex = ifreq.ifr_ifindex; 184 return 0; 185 } 186 187 static void usage(const char *progname) 188 { 189 fprintf(stderr, "Usage: %s <interface>\n", progname); 190 } 191 192 int main(int argc, char **argv) 193 { 194 struct arp_packet inarp_req; 195 struct in_addr local_ip; 196 struct inarp_ctx inarp; 197 struct interface *iface; 198 ssize_t len; 199 int ret; 200 201 if (argc < 2) { 202 usage(argv[0]); 203 return EXIT_FAILURE; 204 } 205 206 if (strlen(argv[1]) > IFNAMSIZ) 207 errx(EXIT_FAILURE, "Interface name '%s' is invalid", 208 argv[1]); 209 210 memset(&inarp, 0, sizeof(inarp)); 211 212 /* prepare for a single interface */ 213 inarp.interfaces = calloc(1, sizeof(inarp.interfaces[0])); 214 inarp.n_interfaces = 1; 215 iface = &inarp.interfaces[0]; 216 217 strncpy(iface->ifname, argv[1], sizeof(iface->ifname)); 218 219 inarp.socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); 220 if (inarp.socket < 0) 221 err(EXIT_FAILURE, "Error opening ARP socket"); 222 223 ret = get_ifindex(inarp.socket, iface->ifname, &iface->ifindex); 224 if (ret) 225 exit(EXIT_FAILURE); 226 227 ret = get_local_hwaddr(inarp.socket, iface->ifname, &iface->eth_addr); 228 if (ret) 229 exit(EXIT_FAILURE); 230 231 printf("%s MAC address: %s\n", iface->ifname, 232 eth_mac_to_str(&iface->eth_addr)); 233 234 while (1) { 235 len = recvfrom(inarp.socket, &inarp_req, sizeof(inarp_req), 0, 236 NULL, NULL); 237 if (len <= 0) { 238 if (errno == EINTR) 239 continue; 240 err(EXIT_FAILURE, "Error recieving ARP packet"); 241 } 242 243 /* Is this packet large enough for an inarp? */ 244 if ((size_t)len < sizeof(inarp_req)) 245 continue; 246 247 /* ... is it an inarp request? */ 248 if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST) 249 continue; 250 251 /* ... for us? */ 252 if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN)) 253 continue; 254 255 printf("src mac: %s\n", eth_mac_to_str(&inarp_req.src_mac)); 256 printf("src ip: %s\n", inet_ntoa(inarp_req.src_ip)); 257 258 ret = get_local_ipaddr(inarp.socket, iface->ifname, &local_ip); 259 /* if we don't have a local IP address to send, just drop the 260 * request */ 261 if (ret) 262 continue; 263 264 printf("local ip: %s\n", inet_ntoa(local_ip)); 265 266 send_arp_packet(inarp.socket, iface->ifindex, 267 &inarp_req.dest_mac, 268 &local_ip, 269 &inarp_req.src_mac, 270 &inarp_req.src_ip); 271 } 272 close(inarp.socket); 273 return 0; 274 } 275