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