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