xref: /openbmc/inarp/inarp.c (revision 17cd0b01)
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