blob: 65c03cd2b422dd6cbcd6c4ab5368f1d2a7784086 [file] [log] [blame]
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +00001/*
2 * arpd.c ARP helper daemon.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <syslog.h>
14#include <malloc.h>
15#include <string.h>
16#include <unistd.h>
17#include <stdlib.h>
18#include <netdb.h>
osdl.org!shemminger16efac52004-06-09 21:28:46 +000019#include <db_185.h>
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000020#include <sys/ioctl.h>
21#include <sys/poll.h>
22#include <errno.h>
23#include <fcntl.h>
24#include <sys/uio.h>
25#include <sys/socket.h>
26#include <sys/time.h>
27#include <time.h>
28#include <signal.h>
29#include <linux/if.h>
osdl.net!shemminger17ce7fd2004-07-06 21:01:44 +000030#include <linux/if_ether.h>
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000031#include <linux/if_arp.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34#include <linux/if_packet.h>
35#include <linux/filter.h>
36
37#include "libnetlink.h"
38#include "utils.h"
Jiri Pirkodd502472014-05-15 15:10:20 +020039#include "rt_names.h"
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000040
41int resolve_hosts;
42
43DB *dbase;
44char *dbname = "/var/lib/arpd/arpd.db";
45
Stephen Hemmingerae665a52006-12-05 10:10:22 -080046int ifnum;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000047int *ifvec;
48char **ifnames;
49
Stephen Hemmingeracd1e432016-03-21 11:56:36 -070050struct dbkey {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000051 __u32 iface;
52 __u32 addr;
53};
54
Stephen Hemmingeracd1e432016-03-21 11:56:36 -070055#define IS_NEG(x) (((__u8 *)(x))[0] == 0xFF)
Stephen Hemmingerae665a52006-12-05 10:10:22 -080056#define NEG_TIME(x) (((x)[2]<<24)|((x)[3]<<16)|((x)[4]<<8)|(x)[5])
Stephen Hemmingeracd1e432016-03-21 11:56:36 -070057#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8 *)x))
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000058#define NEG_VALID(x) (NEG_AGE(x) < negative_timeout)
Stephen Hemmingeracd1e432016-03-21 11:56:36 -070059#define NEG_CNT(x) (((__u8 *)(x))[1])
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000060
61struct rtnl_handle rth;
62
63struct pollfd pset[2];
64int udp_sock = -1;
65
66volatile int do_exit;
67volatile int do_sync;
68volatile int do_stats;
69
70struct {
71 unsigned long arp_new;
72 unsigned long arp_change;
73
74 unsigned long app_recv;
75 unsigned long app_success;
76 unsigned long app_bad;
77 unsigned long app_neg;
78 unsigned long app_suppressed;
79
80 unsigned long kern_neg;
81 unsigned long kern_new;
82 unsigned long kern_change;
83
84 unsigned long probes_sent;
85 unsigned long probes_suppressed;
86} stats;
87
88int active_probing;
89int negative_timeout = 60;
90int no_kernel_broadcasts;
91int broadcast_rate = 1000;
92int broadcast_burst = 3000;
Stephen Hemminger0df7db32012-02-17 08:17:09 -080093int poll_timeout = 30000;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000094
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -080095static void usage(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000096{
97 fprintf(stderr,
Stephen Hemmingeracd1e432016-03-21 11:56:36 -070098 "Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [ -n time ] [-p interval ] [ -R rate ] [ interfaces ]\n");
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000099 exit(1);
100}
101
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800102static int handle_if(int ifindex)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000103{
104 int i;
105
106 if (ifnum == 0)
107 return 1;
108
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700109 for (i = 0; i < ifnum; i++)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000110 if (ifvec[i] == ifindex)
111 return 1;
112 return 0;
113}
114
115int sysctl_adjusted;
116
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800117static void do_sysctl_adjustments(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000118{
119 int i;
120
121 if (!ifnum)
122 return;
123
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700124 for (i = 0; i < ifnum; i++) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000125 char buf[128];
126 FILE *fp;
127
128 if (active_probing) {
129 sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]);
130 if ((fp = fopen(buf, "w")) != NULL) {
131 if (no_kernel_broadcasts)
132 strcpy(buf, "0\n");
133 else
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700134 sprintf(buf, "%d\n", active_probing >= 2 ? 1 : 3-active_probing);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000135 fputs(buf, fp);
136 fclose(fp);
137 }
138 }
139
140 sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]);
141 if ((fp = fopen(buf, "w")) != NULL) {
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700142 sprintf(buf, "%d\n", active_probing <= 1 ? 1 : active_probing);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000143 fputs(buf, fp);
144 fclose(fp);
145 }
146 }
147 sysctl_adjusted = 1;
148}
149
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800150static void undo_sysctl_adjustments(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000151{
152 int i;
153
154 if (!sysctl_adjusted)
155 return;
156
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700157 for (i = 0; i < ifnum; i++) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000158 char buf[128];
159 FILE *fp;
160
161 if (active_probing) {
162 sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]);
163 if ((fp = fopen(buf, "w")) != NULL) {
164 strcpy(buf, "3\n");
165 fputs(buf, fp);
166 fclose(fp);
167 }
168 }
169 sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]);
170 if ((fp = fopen(buf, "w")) != NULL) {
171 strcpy(buf, "0\n");
172 fputs(buf, fp);
173 fclose(fp);
174 }
175 }
176 sysctl_adjusted = 0;
177}
178
179
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800180static int send_probe(int ifindex, __u32 addr)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000181{
182 struct ifreq ifr;
183 struct sockaddr_in dst;
shemmingerf332d162005-07-05 22:37:15 +0000184 socklen_t len;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000185 unsigned char buf[256];
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700186 struct arphdr *ah = (struct arphdr *)buf;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000187 unsigned char *p = (unsigned char *)(ah+1);
188 struct sockaddr_ll sll;
189
190 memset(&ifr, 0, sizeof(ifr));
191 ifr.ifr_ifindex = ifindex;
192 if (ioctl(udp_sock, SIOCGIFNAME, &ifr))
193 return -1;
194 if (ioctl(udp_sock, SIOCGIFHWADDR, &ifr))
195 return -1;
196 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
197 return -1;
198 if (setsockopt(udp_sock, SOL_SOCKET, SO_BINDTODEVICE, ifr.ifr_name, strlen(ifr.ifr_name)+1) < 0)
199 return -1;
200
201 dst.sin_family = AF_INET;
202 dst.sin_port = htons(1025);
203 dst.sin_addr.s_addr = addr;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700204 if (connect(udp_sock, (struct sockaddr *)&dst, sizeof(dst)) < 0)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000205 return -1;
206 len = sizeof(dst);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700207 if (getsockname(udp_sock, (struct sockaddr *)&dst, &len) < 0)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000208 return -1;
209
210 ah->ar_hrd = htons(ifr.ifr_hwaddr.sa_family);
211 ah->ar_pro = htons(ETH_P_IP);
212 ah->ar_hln = 6;
213 ah->ar_pln = 4;
214 ah->ar_op = htons(ARPOP_REQUEST);
215
216 memcpy(p, ifr.ifr_hwaddr.sa_data, ah->ar_hln);
217 p += ah->ar_hln;
218
219 memcpy(p, &dst.sin_addr, 4);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700220 p += 4;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000221
222 sll.sll_family = AF_PACKET;
223 memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr));
224 sll.sll_ifindex = ifindex;
225 sll.sll_protocol = htons(ETH_P_ARP);
226 memcpy(p, &sll.sll_addr, ah->ar_hln);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700227 p += ah->ar_hln;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000228
229 memcpy(p, &addr, 4);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700230 p += 4;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000231
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700232 if (sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr *)&sll, sizeof(sll)) < 0)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000233 return -1;
234 stats.probes_sent++;
235 return 0;
236}
237
238/* Be very tough on sending probes: 1 per second with burst of 3. */
239
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800240static int queue_active_probe(int ifindex, __u32 addr)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000241{
242 static struct timeval prev;
243 static int buckets;
244 struct timeval now;
245
246 gettimeofday(&now, NULL);
247 if (prev.tv_sec) {
248 int diff = (now.tv_sec-prev.tv_sec)*1000+(now.tv_usec-prev.tv_usec)/1000;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700249
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000250 buckets += diff;
251 } else {
252 buckets = broadcast_burst;
253 }
254 if (buckets > broadcast_burst)
255 buckets = broadcast_burst;
256 if (buckets >= broadcast_rate && !send_probe(ifindex, addr)) {
257 buckets -= broadcast_rate;
258 prev = now;
259 return 0;
260 }
261 stats.probes_suppressed++;
262 return -1;
263}
264
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800265static int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000266{
267 struct {
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700268 struct nlmsghdr n;
269 struct ndmsg ndm;
270 char buf[256];
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000271 } req;
272
273 memset(&req.n, 0, sizeof(req.n));
274 memset(&req.ndm, 0, sizeof(req.ndm));
275
276 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
277 req.n.nlmsg_flags = NLM_F_REQUEST;
278 req.n.nlmsg_type = RTM_NEWNEIGH;
279 req.ndm.ndm_family = AF_INET;
280 req.ndm.ndm_state = NUD_STALE;
281 req.ndm.ndm_ifindex = ifindex;
282 req.ndm.ndm_type = RTN_UNICAST;
283
284 addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4);
285 addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen);
Stephen Hemminger6cf83982011-12-23 10:40:04 -0800286 return rtnl_send(&rth, &req, req.n.nlmsg_len) <= 0;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000287}
288
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800289static void prepare_neg_entry(__u8 *ndata, __u32 stamp)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000290{
291 ndata[0] = 0xFF;
292 ndata[1] = 0;
293 ndata[2] = stamp>>24;
294 ndata[3] = stamp>>16;
295 ndata[4] = stamp>>8;
296 ndata[5] = stamp;
297}
298
299
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800300static int do_one_request(struct nlmsghdr *n)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000301{
302 struct ndmsg *ndm = NLMSG_DATA(n);
303 int len = n->nlmsg_len;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700304 struct rtattr *tb[NDA_MAX+1];
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000305 struct dbkey key;
306 DBT dbkey, dbdat;
307 int do_acct = 0;
308
309 if (n->nlmsg_type == NLMSG_DONE) {
310 dbase->sync(dbase, 0);
311
312 /* Now we have at least mirror of kernel db, so that
313 * may start real resolution.
314 */
315 do_sysctl_adjustments();
316 return 0;
317 }
318
319 if (n->nlmsg_type != RTM_GETNEIGH && n->nlmsg_type != RTM_NEWNEIGH)
320 return 0;
321
322 len -= NLMSG_LENGTH(sizeof(*ndm));
323 if (len < 0)
324 return -1;
325
326 if (ndm->ndm_family != AF_INET ||
327 (ifnum && !handle_if(ndm->ndm_ifindex)) ||
328 ndm->ndm_flags ||
329 ndm->ndm_type != RTN_UNICAST ||
330 !(ndm->ndm_state&~NUD_NOARP))
331 return 0;
332
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000333 parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
334
335 if (!tb[NDA_DST])
336 return 0;
337
338 key.iface = ndm->ndm_ifindex;
339 memcpy(&key.addr, RTA_DATA(tb[NDA_DST]), 4);
340 dbkey.data = &key;
341 dbkey.size = sizeof(key);
342
343 if (dbase->get(dbase, &dbkey, &dbdat, 0) != 0) {
344 dbdat.data = 0;
345 dbdat.size = 0;
346 }
347
348 if (n->nlmsg_type == RTM_GETNEIGH) {
349 if (!(n->nlmsg_flags&NLM_F_REQUEST))
350 return 0;
351
352 if (!(ndm->ndm_state&(NUD_PROBE|NUD_INCOMPLETE))) {
353 stats.app_bad++;
354 return 0;
355 }
356
357 if (ndm->ndm_state&NUD_PROBE) {
358 /* If we get this, kernel still has some valid
359 * address, but unicast probing failed and host
360 * is either dead or changed its mac address.
361 * Kernel is going to initiate broadcast resolution.
362 * OK, we invalidate our information as well.
363 */
364 if (dbdat.data && !IS_NEG(dbdat.data))
365 stats.app_neg++;
366
367 dbase->del(dbase, &dbkey, 0);
368 } else {
369 /* If we get this kernel does not have any information.
370 * If we have something tell this to kernel. */
371 stats.app_recv++;
372 if (dbdat.data && !IS_NEG(dbdat.data)) {
373 stats.app_success++;
374 respond_to_kernel(key.iface, key.addr, dbdat.data, dbdat.size);
375 return 0;
376 }
377
378 /* Sheeit! We have nothing to tell. */
379 /* If we have recent negative entry, be silent. */
380 if (dbdat.data && NEG_VALID(dbdat.data)) {
381 if (NEG_CNT(dbdat.data) >= active_probing) {
382 stats.app_suppressed++;
383 return 0;
384 }
385 do_acct = 1;
386 }
387 }
388
389 if (active_probing &&
390 queue_active_probe(ndm->ndm_ifindex, key.addr) == 0 &&
391 do_acct) {
392 NEG_CNT(dbdat.data)++;
393 dbase->put(dbase, &dbkey, &dbdat, 0);
394 }
395 } else if (n->nlmsg_type == RTM_NEWNEIGH) {
396 if (n->nlmsg_flags&NLM_F_REQUEST)
397 return 0;
398
399 if (ndm->ndm_state&NUD_FAILED) {
400 /* Kernel was not able to resolve. Host is dead.
401 * Create negative entry if it is not present
402 * or renew it if it is too old. */
403 if (!dbdat.data ||
404 !IS_NEG(dbdat.data) ||
405 !NEG_VALID(dbdat.data)) {
406 __u8 ndata[6];
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700407
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000408 stats.kern_neg++;
409 prepare_neg_entry(ndata, time(NULL));
410 dbdat.data = ndata;
411 dbdat.size = sizeof(ndata);
412 dbase->put(dbase, &dbkey, &dbdat, 0);
413 }
414 } else if (tb[NDA_LLADDR]) {
415 if (dbdat.data && !IS_NEG(dbdat.data)) {
416 if (memcmp(RTA_DATA(tb[NDA_LLADDR]), dbdat.data, dbdat.size) == 0)
417 return 0;
418 stats.kern_change++;
419 } else {
420 stats.kern_new++;
421 }
422 dbdat.data = RTA_DATA(tb[NDA_LLADDR]);
423 dbdat.size = RTA_PAYLOAD(tb[NDA_LLADDR]);
424 dbase->put(dbase, &dbkey, &dbdat, 0);
425 }
426 }
427 return 0;
428}
429
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800430static void load_initial_table(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000431{
Stephen Hemmingerd2468da2013-12-20 08:15:02 -0800432 if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH) < 0) {
433 perror("dump request failed");
434 exit(1);
435 }
Stephen Hemminger3d0b7432014-12-20 15:47:17 -0800436
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000437}
438
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800439static void get_kern_msg(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000440{
441 int status;
442 struct nlmsghdr *h;
443 struct sockaddr_nl nladdr;
444 struct iovec iov;
445 char buf[8192];
446 struct msghdr msg = {
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700447 (void *)&nladdr, sizeof(nladdr),
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000448 &iov, 1,
449 NULL, 0,
450 0
451 };
452
453 memset(&nladdr, 0, sizeof(nladdr));
454
455 iov.iov_base = buf;
456 iov.iov_len = sizeof(buf);
457
458 status = recvmsg(rth.fd, &msg, MSG_DONTWAIT);
459
460 if (status <= 0)
461 return;
462
463 if (msg.msg_namelen != sizeof(nladdr))
464 return;
465
466 if (nladdr.nl_pid)
467 return;
468
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700469 for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000470 int len = h->nlmsg_len;
471 int l = len - sizeof(*h);
472
473 if (l < 0 || len > status)
474 return;
475
476 if (do_one_request(h) < 0)
477 return;
478
479 status -= NLMSG_ALIGN(len);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700480 h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000481 }
482}
483
484/* Receive gratuitous ARP messages and store them, that's all. */
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800485static void get_arp_pkt(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000486{
487 unsigned char buf[1024];
488 struct sockaddr_ll sll;
shemmingerf332d162005-07-05 22:37:15 +0000489 socklen_t sll_len = sizeof(sll);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700490 struct arphdr *a = (struct arphdr *)buf;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000491 struct dbkey key;
492 DBT dbkey, dbdat;
493 int n;
494
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800495 n = recvfrom(pset[0].fd, buf, sizeof(buf), MSG_DONTWAIT,
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700496 (struct sockaddr *)&sll, &sll_len);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000497 if (n < 0) {
498 if (errno != EINTR && errno != EAGAIN)
499 syslog(LOG_ERR, "recvfrom: %m");
500 return;
501 }
502
503 if (ifnum && !handle_if(sll.sll_ifindex))
504 return;
505
506 /* Sanity checks */
507
508 if (n < sizeof(*a) ||
509 (a->ar_op != htons(ARPOP_REQUEST) &&
510 a->ar_op != htons(ARPOP_REPLY)) ||
511 a->ar_pln != 4 ||
512 a->ar_pro != htons(ETH_P_IP) ||
513 a->ar_hln != sll.sll_halen ||
514 sizeof(*a) + 2*4 + 2*a->ar_hln > n)
515 return;
516
517 key.iface = sll.sll_ifindex;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700518 memcpy(&key.addr, (char *)(a+1) + a->ar_hln, 4);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000519
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800520 /* DAD message, ignore. */
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000521 if (key.addr == 0)
522 return;
523
524 dbkey.data = &key;
525 dbkey.size = sizeof(key);
526
527 if (dbase->get(dbase, &dbkey, &dbdat, 0) == 0 && !IS_NEG(dbdat.data)) {
528 if (memcmp(dbdat.data, a+1, dbdat.size) == 0)
529 return;
530 stats.arp_change++;
531 } else {
532 stats.arp_new++;
533 }
534
535 dbdat.data = a+1;
536 dbdat.size = a->ar_hln;
537 dbase->put(dbase, &dbkey, &dbdat, 0);
538}
539
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800540static void catch_signal(int sig, void (*handler)(int))
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000541{
542 struct sigaction sa;
543
544 memset(&sa, 0, sizeof(sa));
545 sa.sa_handler = handler;
546#ifdef SA_INTERRUPT
547 sa.sa_flags = SA_INTERRUPT;
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800548#endif
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000549 sigaction(sig, &sa, NULL);
550}
551
552#include <setjmp.h>
553sigjmp_buf env;
554volatile int in_poll;
555
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800556static void sig_exit(int signo)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000557{
558 do_exit = 1;
559 if (in_poll)
560 siglongjmp(env, 1);
561}
562
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800563static void sig_sync(int signo)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000564{
565 do_sync = 1;
566 if (in_poll)
567 siglongjmp(env, 1);
568}
569
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800570static void sig_stats(int signo)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000571{
572 do_sync = 1;
573 do_stats = 1;
574 if (in_poll)
575 siglongjmp(env, 1);
576}
577
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -0800578static void send_stats(void)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000579{
580 syslog(LOG_INFO, "arp_rcv: n%lu c%lu app_rcv: tot %lu hits %lu bad %lu neg %lu sup %lu",
581 stats.arp_new, stats.arp_change,
582
583 stats.app_recv, stats.app_success,
584 stats.app_bad, stats.app_neg, stats.app_suppressed
585 );
586 syslog(LOG_INFO, "kern: n%lu c%lu neg %lu arp_send: %lu rlim %lu",
587 stats.kern_new, stats.kern_change, stats.kern_neg,
588
589 stats.probes_sent, stats.probes_suppressed
590 );
591 do_stats = 0;
592}
593
594
595int main(int argc, char **argv)
596{
597 int opt;
598 int do_list = 0;
599 char *do_load = NULL;
600
Stephen Hemminger0df7db32012-02-17 08:17:09 -0800601 while ((opt = getopt(argc, argv, "h?b:lf:a:n:p:kR:B:")) != EOF) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000602 switch (opt) {
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700603 case 'b':
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000604 dbname = optarg;
605 break;
606 case 'f':
607 if (do_load) {
608 fprintf(stderr, "Duplicate option -f\n");
609 usage();
610 }
611 do_load = optarg;
612 break;
613 case 'l':
614 do_list = 1;
615 break;
616 case 'a':
617 active_probing = atoi(optarg);
618 break;
619 case 'n':
620 negative_timeout = atoi(optarg);
621 break;
622 case 'k':
623 no_kernel_broadcasts = 1;
624 break;
Stephen Hemminger0df7db32012-02-17 08:17:09 -0800625 case 'p':
626 if ((poll_timeout = 1000 * strtod(optarg, NULL)) < 100) {
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700627 fprintf(stderr, "Invalid poll timeout\n");
Stephen Hemminger0df7db32012-02-17 08:17:09 -0800628 exit(-1);
629 }
630 break;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000631 case 'R':
632 if ((broadcast_rate = atoi(optarg)) <= 0 ||
633 (broadcast_rate = 1000/broadcast_rate) <= 0) {
634 fprintf(stderr, "Invalid ARP rate\n");
635 exit(-1);
636 }
637 break;
638 case 'B':
639 if ((broadcast_burst = atoi(optarg)) <= 0 ||
640 (broadcast_burst = 1000*broadcast_burst) <= 0) {
641 fprintf(stderr, "Invalid ARP burst\n");
642 exit(-1);
643 }
644 break;
645 case 'h':
646 case '?':
647 default:
648 usage();
649 }
650 }
651 argc -= optind;
652 argv += optind;
653
654 if (argc > 0) {
655 ifnum = argc;
656 ifnames = argv;
657 ifvec = malloc(argc*sizeof(int));
658 if (!ifvec) {
659 perror("malloc");
660 exit(-1);
661 }
662 }
663
664 if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
665 perror("socket");
666 exit(-1);
667 }
668
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700669 if (ifnum) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000670 int i;
671 struct ifreq ifr;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700672
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000673 memset(&ifr, 0, sizeof(ifr));
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700674 for (i = 0; i < ifnum; i++) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000675 strncpy(ifr.ifr_name, ifnames[i], IFNAMSIZ);
676 if (ioctl(udp_sock, SIOCGIFINDEX, &ifr)) {
677 perror("ioctl(SIOCGIFINDEX)");
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700678 exit(-1);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000679 }
680 ifvec[i] = ifr.ifr_ifindex;
681 }
682 }
683
684 dbase = dbopen(dbname, O_CREAT|O_RDWR, 0644, DB_HASH, NULL);
685 if (dbase == NULL) {
686 perror("db_open");
687 exit(-1);
688 }
689
690 if (do_load) {
691 char buf[128];
692 FILE *fp;
693 struct dbkey k;
694 DBT dbkey, dbdat;
695
696 dbkey.data = &k;
697 dbkey.size = sizeof(k);
698
699 if (strcmp(do_load, "-") == 0 || strcmp(do_load, "--") == 0) {
700 fp = stdin;
701 } else if ((fp = fopen(do_load, "r")) == NULL) {
702 perror("fopen");
703 goto do_abort;
704 }
705
706 buf[sizeof(buf)-1] = 0;
Phil Sutter61170fd2015-11-28 01:00:05 +0100707 while (fgets(buf, sizeof(buf), fp)) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000708 __u8 b1[6];
709 char ipbuf[128];
710 char macbuf[128];
711
712 if (buf[0] == '#')
713 continue;
714
715 if (sscanf(buf, "%u%s%s", &k.iface, ipbuf, macbuf) != 3) {
716 fprintf(stderr, "Wrong format of input file \"%s\"\n", do_load);
717 goto do_abort;
718 }
719 if (strncmp(macbuf, "FAILED:", 7) == 0)
720 continue;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700721 if (!inet_aton(ipbuf, (struct in_addr *)&k.addr)) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000722 fprintf(stderr, "Invalid IP address: \"%s\"\n", ipbuf);
723 goto do_abort;
724 }
shemmingerf332d162005-07-05 22:37:15 +0000725
Jiri Pirkodd502472014-05-15 15:10:20 +0200726 if (ll_addr_a2n((char *) b1, 6, macbuf) != 6)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000727 goto do_abort;
728 dbdat.size = 6;
729
730 if (dbase->put(dbase, &dbkey, &dbdat, 0)) {
731 perror("hash->put");
732 goto do_abort;
733 }
734 }
735 dbase->sync(dbase, 0);
736 if (fp != stdin)
737 fclose(fp);
738 }
739
740 if (do_list) {
741 DBT dbkey, dbdat;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700742
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000743 printf("%-8s %-15s %s\n", "#Ifindex", "IP", "MAC");
744 while (dbase->seq(dbase, &dbkey, &dbdat, R_NEXT) == 0) {
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800745 struct dbkey *key = dbkey.data;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700746
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000747 if (handle_if(key->iface)) {
748 if (!IS_NEG(dbdat.data)) {
shemmingerf332d162005-07-05 22:37:15 +0000749 char b1[18];
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700750
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000751 printf("%-8d %-15s %s\n",
752 key->iface,
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700753 inet_ntoa(*(struct in_addr *)&key->addr),
Jiri Pirkodd502472014-05-15 15:10:20 +0200754 ll_addr_n2a(dbdat.data, 6, ARPHRD_ETHER, b1, 18));
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000755 } else {
756 printf("%-8d %-15s FAILED: %dsec ago\n",
757 key->iface,
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700758 inet_ntoa(*(struct in_addr *)&key->addr),
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000759 NEG_AGE(dbdat.data));
760 }
761 }
762 }
763 }
764
765 if (do_load || do_list)
766 goto out;
767
768 pset[0].fd = socket(PF_PACKET, SOCK_DGRAM, 0);
769 if (pset[0].fd < 0) {
770 perror("socket");
771 exit(-1);
772 }
773
774 if (1) {
775 struct sockaddr_ll sll;
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700776
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000777 memset(&sll, 0, sizeof(sll));
778 sll.sll_family = AF_PACKET;
779 sll.sll_protocol = htons(ETH_P_ARP);
780 sll.sll_ifindex = (ifnum == 1 ? ifvec[0] : 0);
Stephen Hemmingeracd1e432016-03-21 11:56:36 -0700781 if (bind(pset[0].fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000782 perror("bind");
783 goto do_abort;
784 }
785 }
786
787 if (rtnl_open(&rth, RTMGRP_NEIGH) < 0) {
788 perror("rtnl_open");
789 goto do_abort;
790 }
791 pset[1].fd = rth.fd;
792
793 load_initial_table();
794
Mike Frysingera7a9ddb2009-11-06 06:04:39 -0500795 if (daemon(0, 0)) {
796 perror("arpd: daemon");
797 goto do_abort;
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000798 }
799
800 openlog("arpd", LOG_PID | LOG_CONS, LOG_DAEMON);
801 catch_signal(SIGINT, sig_exit);
802 catch_signal(SIGTERM, sig_exit);
803 catch_signal(SIGHUP, sig_sync);
804 catch_signal(SIGUSR1, sig_stats);
805
806#define EVENTS (POLLIN|POLLPRI|POLLERR|POLLHUP)
807 pset[0].events = EVENTS;
808 pset[0].revents = 0;
809 pset[1].events = EVENTS;
810 pset[1].revents = 0;
811
812 sigsetjmp(env, 1);
813
814 for (;;) {
815 in_poll = 1;
816
817 if (do_exit)
818 break;
819 if (do_sync) {
820 in_poll = 0;
821 dbase->sync(dbase, 0);
822 do_sync = 0;
823 in_poll = 1;
824 }
825 if (do_stats)
826 send_stats();
Stephen Hemminger0df7db32012-02-17 08:17:09 -0800827 if (poll(pset, 2, poll_timeout) > 0) {
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000828 in_poll = 0;
829 if (pset[0].revents&EVENTS)
830 get_arp_pkt();
831 if (pset[1].revents&EVENTS)
832 get_kern_msg();
833 } else {
834 do_sync = 1;
835 }
836 }
837
838 undo_sysctl_adjustments();
839out:
840 dbase->close(dbase);
841 exit(0);
842
843do_abort:
844 dbase->close(dbase);
845 exit(-1);
846}