blob: 212b10f45d20e333578456331bde0240b85fe0a6 [file] [log] [blame]
Jeremy Kerr42dc98c2016-02-25 10:23:14 +08001/******************************************************************************
Jeremy Kerrf2101cf2016-03-03 14:49:52 +08002 * 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 Kerr982b7bc2016-02-25 10:54:39 +080017
Jeremy Kerr52e24782016-03-03 14:51:35 +080018#include <err.h>
19#include <errno.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080020#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080023#include <time.h>
24#include <unistd.h>
25
26#include <sys/ioctl.h>
27#include <sys/socket.h>
28
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080029#include <arpa/inet.h>
Jeremy Kerr52e24782016-03-03 14:51:35 +080030#include <netinet/in.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080031
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080032#include <linux/if_arp.h>
Jeremy Kerr52e24782016-03-03 14:51:35 +080033#include <linux/if_ether.h>
34#include <linux/if_packet.h>
Jeremy Kerr982b7bc2016-02-25 10:54:39 +080035
Jeremy Kerr71f385b2016-03-03 15:18:39 +080036struct eth_addr {
37 uint8_t eth_addr[ETH_ALEN];
38} __attribute__((packed));
39
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080040struct arp_packet {
Jeremy Kerrf2101cf2016-03-03 14:49:52 +080041 struct ethhdr eh;
42 struct arphdr arp;
Jeremy Kerr71f385b2016-03-03 15:18:39 +080043 struct eth_addr src_mac;
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080044 struct in_addr src_ip;
Jeremy Kerr71f385b2016-03-03 15:18:39 +080045 struct eth_addr dest_mac;
Jeremy Kerr760d6ac2016-02-25 14:45:33 +080046 struct in_addr dest_ip;
47} __attribute__((packed));
Jeremy Kerr42dc98c2016-02-25 10:23:14 +080048
Jeremy Kerr17cd0b02016-03-04 11:56:37 +080049struct interface {
Jeremy Kerre09d30d2016-03-04 11:42:03 +080050 int ifindex;
Jeremy Kerr17cd0b02016-03-04 11:56:37 +080051 char ifname[IFNAMSIZ+1];
52 struct eth_addr eth_addr;
53};
54
55struct inarp_ctx {
56 int socket;
57 struct interface *interfaces;
58 unsigned int n_interfaces;
Jeremy Kerre09d30d2016-03-04 11:42:03 +080059};
60
Jeremy Kerr7b3bcf72016-02-25 14:29:55 +080061static int send_arp_packet(int fd,
Jeremy Kerrf2101cf2016-03-03 14:49:52 +080062 int ifindex,
Jeremy Kerr71f385b2016-03-03 15:18:39 +080063 const struct eth_addr *src_mac,
Jeremy Kerr88384bc2016-03-03 14:52:50 +080064 const struct in_addr *src_ip,
Jeremy Kerr71f385b2016-03-03 15:18:39 +080065 const struct eth_addr *dest_mac,
Jeremy Kerr88384bc2016-03-03 14:52:50 +080066 const struct in_addr *dest_ip)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +080067{
Jeremy Kerr89dd5142016-02-25 19:40:02 +080068 struct sockaddr_ll addr;
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080069 struct arp_packet arp;
Jeremy Kerrd7865332016-02-25 14:52:50 +080070 int rc;
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080071
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080072 memset(&arp, 0, sizeof(arp));
73
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080074 /* Prepare our link-layer address: raw packet interface,
75 * using the ifindex interface, receiving ARP packets
76 */
Jeremy Kerr89dd5142016-02-25 19:40:02 +080077 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);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080084
85 /* set the frame header */
Jeremy Kerr103a9582016-02-25 19:38:52 +080086 memcpy(arp.eh.h_dest, dest_mac, ETH_ALEN);
87 memcpy(arp.eh.h_source, src_mac, ETH_ALEN);
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080088 arp.eh.h_proto = htons(ETH_P_ARP);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080089
90 /* Fill InARP request data for ethernet + ipv4 */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080091 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);
Jeremy Kerrc10bedb2016-02-25 14:26:38 +080096
97 /* fill arp ethernet mac & ipv4 info */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +080098 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));
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800102
103 /* send the packet */
Jeremy Kerr5bb869d2016-02-25 18:47:08 +0800104 rc = sendto(fd, &arp, sizeof(arp), 0,
Jeremy Kerr89dd5142016-02-25 19:40:02 +0800105 (struct sockaddr *)&addr, sizeof(addr));
Jeremy Kerrd7865332016-02-25 14:52:50 +0800106 if (rc < 0)
107 warn("failure sending ARP response");
108
109 return rc;
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800110}
Jeremy Kerr5514f7b2016-02-25 12:41:44 +0800111
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800112static const char *eth_mac_to_str(const struct eth_addr *mac_addr)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800113{
Jeremy Kerrdf046132016-03-03 15:03:22 +0800114 static char mac_str[ETH_ALEN * (sizeof("00:") - 1)];
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800115 const uint8_t *addr = mac_addr->eth_addr;
Jeremy Kerrdf046132016-03-03 15:03:22 +0800116
117 snprintf(mac_str, sizeof(mac_str),
118 "%02x:%02x:%02x:%02x:%02x:%02x",
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800119 addr[0], addr[1], addr[2],
120 addr[3], addr[4], addr[5]);
Jeremy Kerrdf046132016-03-03 15:03:22 +0800121
122 return mac_str;
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800123}
Jeremy Kerr5514f7b2016-02-25 12:41:44 +0800124
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800125static int do_ifreq(int fd, unsigned long type,
126 const char *ifname, struct ifreq *ifreq)
127{
128 memset(ifreq, 0, sizeof(*ifreq));
Jeremy Kerr7275d5c2016-03-03 15:30:10 +0800129 strncpy(ifreq->ifr_name, ifname, sizeof(ifreq->ifr_name));
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800130
131 return ioctl(fd, type, ifreq);
132}
133
Jeremy Kerre54e4832016-02-25 17:23:10 +0800134static 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
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800140 rc = do_ifreq(fd, SIOCGIFADDR, ifname, &ifreq);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800141 if (rc) {
Jeremy Kerr1b127c52016-02-25 19:29:02 +0800142 warn("Error querying local IP address for %s", ifname);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800143 return -1;
144 }
145
146 if (ifreq.ifr_addr.sa_family != AF_INET) {
147 warnx("Unknown address family %d in address response",
Jeremy Kerrf2101cf2016-03-03 14:49:52 +0800148 ifreq.ifr_addr.sa_family);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800149 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
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800157static int get_local_hwaddr(int fd, const char *ifname, struct eth_addr *addr)
Jeremy Kerr86489a12016-02-25 19:14:09 +0800158{
159 struct ifreq ifreq;
160 int rc;
161
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800162 rc = do_ifreq(fd, SIOCGIFHWADDR, ifname, &ifreq);
Jeremy Kerr86489a12016-02-25 19:14:09 +0800163 if (rc) {
Jeremy Kerr1b127c52016-02-25 19:29:02 +0800164 warn("Error querying local MAC address for %s", ifname);
Jeremy Kerr86489a12016-02-25 19:14:09 +0800165 return -1;
166 }
167
168 memcpy(addr, ifreq.ifr_hwaddr.sa_data, ETH_ALEN);
169 return 0;
170}
171
Jeremy Kerrd8343782016-02-25 19:17:19 +0800172static int get_ifindex(int fd, const char *ifname, int *ifindex)
173{
174 struct ifreq ifreq;
175 int rc;
176
Jeremy Kerrf3b373f2016-02-25 19:24:59 +0800177 rc = do_ifreq(fd, SIOCGIFINDEX, ifname, &ifreq);
Jeremy Kerrd8343782016-02-25 19:17:19 +0800178 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
Jeremy Kerra7c07192016-03-04 14:18:41 +0800187static int init_interfaces(struct inarp_ctx *inarp)
Jeremy Kerr115522d2016-02-25 10:24:14 +0800188{
Jeremy Kerra7c07192016-03-04 14:18:41 +0800189 int alloc_len, rc, retry = 3;
190 struct ifconf ifconf;
191 unsigned int i;
192
193 /* find the names of all interfaces */
194 for (;;) {
195 ifconf.ifc_len = 0;
196 ifconf.ifc_buf = NULL;
197 rc = ioctl(inarp->socket, SIOCGIFCONF, &ifconf);
198 if (rc < 0) {
199 warn("Error querying interface configuration size");
200 return -1;
201 }
202
203 /* the only way we can detect truncated ifconf data is by
204 * allocating for one extra ifreq, then seeing if it gets
205 * filled; if so, subsequent ones may have been dropped.
206 */
207 alloc_len = ifconf.ifc_len + sizeof(struct ifreq);
208
209 ifconf.ifc_len = alloc_len;
210 ifconf.ifc_buf = malloc(alloc_len);
211
212 rc = ioctl(inarp->socket, SIOCGIFCONF, &ifconf);
213 if (rc < 0) {
214 warn("Error quering interface configuration");
215 free(ifconf.ifc_buf);
216 return -1;
217 }
218
219 if (ifconf.ifc_len < alloc_len)
220 break;
221
222 if (retry--) {
223 /* we may have lost ifconf data, as our buffer was
224 * full; retry */
225 free(ifconf.ifc_buf);
226 continue;
227 }
228
229 warnx("Can't allocate data for interface configuration");
230 return -1;
231 }
232
233 /* populate interface data */
234 for (i = 0; i < ifconf.ifc_len / sizeof(struct ifreq); i++) {
235 struct ifreq *ifreq = &ifconf.ifc_req[i];
236 struct interface iface;
237
238 strncpy(iface.ifname, ifreq->ifr_name, IFNAMSIZ);
239
240 rc = get_ifindex(inarp->socket, iface.ifname, &iface.ifindex);
241 if (rc)
242 continue;
243
244 rc = get_local_hwaddr(inarp->socket, iface.ifname,
245 &iface.eth_addr);
246 if (rc)
247 continue;
248
249 inarp->n_interfaces++;
250 inarp->interfaces = realloc(inarp->interfaces,
251 sizeof(*inarp->interfaces) *
252 inarp->n_interfaces);
253 memcpy(&inarp->interfaces[inarp->n_interfaces - 1],
254 &iface, sizeof(iface));
255 }
256
257 free(ifconf.ifc_buf);
258
259 if (!inarp->n_interfaces) {
260 warnx("No interfaces defined");
261 return -1;
262 }
263
264 printf("%d interfaces found:\n", inarp->n_interfaces);
265 for (i = 0; i < inarp->n_interfaces; i++) {
266 struct interface *iface = &inarp->interfaces[i];
267 printf(" %d: %-*s [%s]\n", iface->ifindex,
268 IFNAMSIZ,
269 iface->ifname,
270 eth_mac_to_str(&iface->eth_addr));
271 }
272
273 return 0;
Jeremy Kerr115522d2016-02-25 10:24:14 +0800274}
275
Jeremy Kerra7c07192016-03-04 14:18:41 +0800276static struct interface *find_interface_by_ifindex(struct inarp_ctx *inarp,
277 int ifindex)
278{
279 unsigned int i;
280
281 for (i = 0; i < inarp->n_interfaces; i++) {
282 struct interface *iface = &inarp->interfaces[i];
283 if (iface->ifindex == ifindex)
284 return iface;
285 }
286
287 return NULL;
288}
289
290int main(void)
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800291{
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800292 struct arp_packet inarp_req;
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800293 struct in_addr local_ip;
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800294 struct interface *iface;
Jeremy Kerra7c07192016-03-04 14:18:41 +0800295 struct sockaddr_ll addr;
296 struct inarp_ctx inarp;
297 socklen_t addrlen;
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800298 ssize_t len;
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800299 int ret;
Jeremy Kerr115522d2016-02-25 10:24:14 +0800300
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800301 memset(&inarp, 0, sizeof(inarp));
302
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800303 inarp.socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
304 if (inarp.socket < 0)
Jeremy Kerrd7865332016-02-25 14:52:50 +0800305 err(EXIT_FAILURE, "Error opening ARP socket");
Jeremy Kerrc10bedb2016-02-25 14:26:38 +0800306
Jeremy Kerra7c07192016-03-04 14:18:41 +0800307 ret = init_interfaces(&inarp);
Jeremy Kerr86489a12016-02-25 19:14:09 +0800308 if (ret)
309 exit(EXIT_FAILURE);
Jeremy Kerrd7865332016-02-25 14:52:50 +0800310
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800311 while (1) {
Jeremy Kerra7c07192016-03-04 14:18:41 +0800312 addrlen = sizeof(addr);
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800313 len = recvfrom(inarp.socket, &inarp_req, sizeof(inarp_req), 0,
Jeremy Kerra7c07192016-03-04 14:18:41 +0800314 (struct sockaddr *)&addr, &addrlen);
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800315 if (len <= 0) {
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800316 if (errno == EINTR)
317 continue;
318 err(EXIT_FAILURE, "Error recieving ARP packet");
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800319 }
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800320
Jeremy Kerra7c07192016-03-04 14:18:41 +0800321 /*
322 * struct sockaddr_ll allows for 8 bytes of hardware address;
323 * we only need ETH_ALEN for a full ethernet address.
324 */
325 if (addrlen < sizeof(addr) - (8 - ETH_ALEN))
326 continue;
327
328 if (addr.sll_family != AF_PACKET)
329 continue;
330
331 iface = find_interface_by_ifindex(&inarp, addr.sll_ifindex);
332 if (!iface)
333 continue;
334
Jeremy Kerr3f8a28e2016-02-25 18:56:01 +0800335 /* Is this packet large enough for an inarp? */
336 if ((size_t)len < sizeof(inarp_req))
337 continue;
338
339 /* ... is it an inarp request? */
340 if (ntohs(inarp_req.arp.ar_op) != ARPOP_InREQUEST)
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800341 continue;
342
343 /* ... for us? */
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800344 if (memcmp(&iface->eth_addr, inarp_req.eh.h_dest, ETH_ALEN))
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800345 continue;
346
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800347 printf("src mac: %s\n", eth_mac_to_str(&inarp_req.src_mac));
Jeremy Kerr492de5d2016-02-25 19:35:44 +0800348 printf("src ip: %s\n", inet_ntoa(inarp_req.src_ip));
Jeremy Kerr77cafea2016-02-25 17:15:28 +0800349
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800350 ret = get_local_ipaddr(inarp.socket, iface->ifname, &local_ip);
Jeremy Kerre54e4832016-02-25 17:23:10 +0800351 /* if we don't have a local IP address to send, just drop the
352 * request */
353 if (ret)
354 continue;
355
Jeremy Kerra7c07192016-03-04 14:18:41 +0800356 printf("local mac: %s\n", eth_mac_to_str(&iface->eth_addr));
Jeremy Kerr492de5d2016-02-25 19:35:44 +0800357 printf("local ip: %s\n", inet_ntoa(local_ip));
358
Jeremy Kerr17cd0b02016-03-04 11:56:37 +0800359 send_arp_packet(inarp.socket, iface->ifindex,
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800360 &inarp_req.dest_mac,
Jeremy Kerrf2101cf2016-03-03 14:49:52 +0800361 &local_ip,
Jeremy Kerr71f385b2016-03-03 15:18:39 +0800362 &inarp_req.src_mac,
Jeremy Kerrf2101cf2016-03-03 14:49:52 +0800363 &inarp_req.src_ip);
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800364 }
Jeremy Kerre09d30d2016-03-04 11:42:03 +0800365 close(inarp.socket);
Jeremy Kerr42dc98c2016-02-25 10:23:14 +0800366 return 0;
367}