-
Notifications
You must be signed in to change notification settings - Fork 3
/
arp.c
798 lines (749 loc) · 26.7 KB
/
arp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
// Copyright 2010-2018 Nicholas J. Kain <njkain at gmail dot com>
// SPDX-License-Identifier: MIT
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/filter.h>
#include <errno.h>
#include "nk/log.h"
#include "nk/io.h"
#include "arp.h"
#include "state.h"
#include "dhcp.h"
#include "sys.h"
#include "ifchange.h"
#include "options.h"
#include "leasefile.h"
#include "sockd.h"
#include "netlink.h"
#define ARP_MSG_SIZE 0x2a
#define ARP_RETRANS_DELAY 5000 // ms
#define ARP_MAX_TRIES 3
// From RFC5227
unsigned arp_probe_wait = 1000; // initial random delay (ms)
unsigned arp_probe_num = 3; // number of probe packets
unsigned arp_probe_min = 1000; // minimum delay until repeated probe (ms)
unsigned arp_probe_max = 2000; // maximum delay until repeated probe (ms)
#define ANNOUNCE_WAIT 2000 // delay before announcing
#define ANNOUNCE_NUM 2 // number of Announcement packets
#define ANNOUNCE_INTERVAL 2000 // time between Announcement packets
#define MAX_CONFLICTS 10 // max conflicts before rate-limiting
#define RATE_LIMIT_INTERVAL 60000 // delay between successive attempts
#define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs
static struct arp_data garp = {
.wake_ts = { -1, -1, -1, -1, -1, -1, -1 },
.send_stats = {{0,0},{0,0},{0,0}},
.last_conflict_ts = 0,
.gw_check_initpings = 0,
.arp_check_start_ts = 0,
.total_conflicts = 0,
.probe_wait_time = 0,
.using_bpf = false,
.relentless_def = false,
.router_replied = false,
.server_replied = false,
};
void set_arp_relentless_def(bool v) { garp.relentless_def = v; }
static void arp_min_close_fd(struct client_state_t *cs)
{
if (cs->arpFd < 0)
return;
close(cs->arpFd);
cs->arpFd = -1;
cs->arp_is_defense = false;
}
static void arp_close_fd(struct client_state_t *cs)
{
arp_min_close_fd(cs);
for (int i = 0; i < AS_MAX; ++i)
garp.wake_ts[i] = -1;
}
void arp_reset_state(struct client_state_t *cs)
{
arp_close_fd(cs);
memset(&garp.reply, 0, sizeof garp.reply);
garp.last_conflict_ts = 0;
garp.gw_check_initpings = 0;
garp.arp_check_start_ts = 0;
garp.total_conflicts = 0;
garp.probe_wait_time = 0;
garp.server_replied = false;
garp.router_replied = false;
for (int i = 0; i < ASEND_MAX; ++i) {
garp.send_stats[i].ts = 0;
garp.send_stats[i].count = 0;
}
}
static int get_arp_basic_socket(struct client_state_t *cs)
{
char resp;
int fd = request_sockd_fd("a", 1, &resp);
switch (resp) {
case 'A': garp.using_bpf = true; break;
case 'a': garp.using_bpf = false; break;
default: suicide("%s: (%s) expected a or A sockd reply but got %c\n",
client_config.interface, __func__, resp);
}
cs->arp_is_defense = false;
return fd;
}
static int get_arp_defense_socket(struct client_state_t *cs)
{
char buf[32];
size_t buflen = 0;
buf[0] = 'd';
buflen += 1;
memcpy(buf + buflen, &cs->clientAddr, sizeof cs->clientAddr);
buflen += sizeof cs->clientAddr;
memcpy(buf + buflen, client_config.arp, 6);
buflen += 6;
char resp;
int fd = request_sockd_fd(buf, buflen, &resp);
switch (resp) {
case 'D': garp.using_bpf = true; break;
case 'd': garp.using_bpf = false; break;
default: suicide("%s: (%s) expected d or D sockd reply but got %c\n",
client_config.interface, __func__, resp);
}
cs->arp_is_defense = true;
return fd;
}
static int arp_open_fd(struct client_state_t *cs, bool defense)
{
if (cs->arpFd >= 0 && defense == cs->arp_is_defense)
return 0;
arp_min_close_fd(cs);
cs->arpFd = defense ? get_arp_defense_socket(cs)
: get_arp_basic_socket(cs);
if (cs->arpFd < 0) {
log_line("%s: (%s) Failed to create socket: %s\n",
client_config.interface, __func__, strerror(errno));
return -1;
}
return 0;
}
static int arp_send(struct client_state_t *cs,
struct arpMsg *arp)
{
int ret = -1;
struct sockaddr_ll addr = {
.sll_family = AF_PACKET,
.sll_ifindex = client_config.ifindex,
.sll_halen = 6,
};
memcpy(addr.sll_addr, client_config.arp, 6);
if (cs->arpFd < 0) {
log_line("%s: arp: Send attempted when no ARP fd is open.\n",
client_config.interface);
return ret;
}
if (!carrier_isup()) {
log_line("%s: (%s) carrier down; sendto would fail\n",
client_config.interface, __func__);
ret = -99;
goto carrier_down;
}
ret = safe_sendto(cs->arpFd, (const char *)arp, sizeof *arp, 0,
(struct sockaddr *)&addr, sizeof addr);
if (ret < 0 || (size_t)ret != sizeof *arp) {
if (ret < 0)
log_line("%s: (%s) sendto failed: %s\n",
client_config.interface, __func__, strerror(errno));
else
log_line("%s: (%s) sendto short write: %d < %zu\n",
client_config.interface, __func__, ret, sizeof *arp);
carrier_down:
return ret;
}
return 0;
}
#define BASE_ARPMSG() struct arpMsg arp = { \
.h_proto = htons(ETH_P_ARP), \
.htype = htons(ARPHRD_ETHER), \
.ptype = htons(ETH_P_IP), \
.hlen = 6, .plen = 4, \
.operation = htons(ARPOP_REQUEST), \
.smac = {0}, \
}; \
memcpy(arp.h_source, client_config.arp, 6); \
memset(arp.h_dest, 0xff, 6); \
memcpy(arp.smac, client_config.arp, 6)
// Returns 0 on success, -1 on failure.
static int arp_ping(struct client_state_t *cs, uint32_t test_ip)
{
BASE_ARPMSG();
memcpy(arp.sip4, &cs->clientAddr, sizeof cs->clientAddr);
memcpy(arp.dip4, &test_ip, sizeof test_ip);
int r = arp_send(cs, &arp);
if (r < 0)
return r;
garp.send_stats[ASEND_GW_PING].count++;
garp.send_stats[ASEND_GW_PING].ts = curms();
return 0;
}
// Returns 0 on success, -1 on failure.
static int arp_ip_anon_ping(struct client_state_t *cs,
uint32_t test_ip)
{
BASE_ARPMSG();
memcpy(arp.dip4, &test_ip, sizeof test_ip);
log_line("%s: arp: Probing for hosts that may conflict with our lease...\n",
client_config.interface);
int r = arp_send(cs, &arp);
if (r < 0)
return r;
garp.send_stats[ASEND_COLLISION_CHECK].count++;
garp.send_stats[ASEND_COLLISION_CHECK].ts = curms();
return 0;
}
static int arp_announcement(struct client_state_t *cs)
{
BASE_ARPMSG();
memcpy(arp.sip4, &cs->clientAddr, 4);
memcpy(arp.dip4, &cs->clientAddr, 4);
int r = arp_send(cs, &arp);
if (r < 0)
return r;
garp.send_stats[ASEND_ANNOUNCE].count++;
garp.send_stats[ASEND_ANNOUNCE].ts = curms();
return 0;
}
#undef BASE_ARPMSG
// Checks to see if there is another host that has our assigned IP.
int arp_check(struct client_state_t *cs,
struct dhcpmsg *packet)
{
memcpy(&garp.dhcp_packet, packet, sizeof (struct dhcpmsg));
if (arp_open_fd(cs, false) < 0)
return -1;
if (arp_ip_anon_ping(cs, garp.dhcp_packet.yiaddr) < 0)
return -1;
garp.arp_check_start_ts = garp.send_stats[ASEND_COLLISION_CHECK].ts;
garp.probe_wait_time = arp_probe_wait;
garp.wake_ts[AS_COLLISION_CHECK] = garp.arp_check_start_ts
+ garp.probe_wait_time;
return 0;
}
static bool can_query_server(const struct client_state_t *cs)
{
return (cs->srcAddr & cs->clientSubnet)
== (cs->clientAddr & cs->clientSubnet);
}
static bool can_query_router(const struct client_state_t *cs)
{
return !!cs->routerAddr;
}
// Confirms that we're still on the fingerprinted network.
int arp_gw_check(struct client_state_t *cs)
{
if (arp_open_fd(cs, false) < 0)
return -1;
garp.gw_check_initpings = garp.send_stats[ASEND_GW_PING].count;
garp.server_replied = true;
garp.router_replied = true;
cs->check_fingerprint = false;
int r;
if (can_query_server(cs)) {
if ((r = arp_ping(cs, cs->srcAddr)) < 0)
return r;
garp.server_replied = false;
cs->check_fingerprint = true;
}
if (can_query_router(cs)) {
if ((r = arp_ping(cs, cs->routerAddr)) < 0)
return r;
garp.router_replied = false;
cs->check_fingerprint = true;
}
if (!cs->check_fingerprint)
return -1;
garp.wake_ts[AS_GW_CHECK] =
garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250;
return 0;
}
// Gathers the fingerprinting info for the associated network.
static int arp_get_gw_hwaddr(struct client_state_t *cs)
{
if (arp_open_fd(cs, false) < 0)
return -1;
cs->router_arp_state = ARP_FAILED;
cs->server_arp_state = ARP_FAILED;
if (can_query_router(cs)) {
if (arp_ping(cs, cs->routerAddr) >= 0) {
cs->router_arp_state = ARP_QUERY;
++cs->router_arp_sent;
}
}
if (can_query_server(cs)) {
if (arp_ping(cs, cs->srcAddr) >= 0) {
cs->server_arp_state = ARP_QUERY;
++cs->server_arp_sent;
}
}
cs->sent_gw_query = true;
const bool sent_router = cs->router_arp_state == ARP_QUERY;
const bool sent_server = cs->server_arp_state == ARP_QUERY;
if (!sent_router && !sent_server) {
return (!can_query_server(cs) && !can_query_router(cs)) ? 0 : -1;
}
if (sent_router && sent_server) {
log_line("%s: arp: Searching for gw and dhcp agent addresses...\n",
client_config.interface);
} else if (sent_router) {
log_line("%s: arp: Searching for gw address...\n",
client_config.interface);
} else if (sent_server) {
log_line("%s: arp: Searching for dhcp agent address...\n",
client_config.interface);
}
garp.wake_ts[AS_GW_QUERY] =
garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250;
return 0;
}
int arp_set_defense_mode(struct client_state_t *cs)
{
return arp_open_fd(cs, true);
}
static int arp_gw_success(struct client_state_t *cs)
{
log_line("%s: arp: Network seems unchanged. Resuming normal operation.\n",
client_config.interface);
if (arp_open_fd(cs, true) < 0)
return ARPR_FAIL;
garp.wake_ts[AS_GW_CHECK] = -1;
if (arp_announcement(cs) < 0)
return ARPR_FAIL;
return ARPR_FREE;
}
// ARP validation functions that will be performed by the BPF if it is
// installed.
static int arp_validate_bpf(struct arpMsg *am)
{
if (am->h_proto != htons(ETH_P_ARP)) {
log_line("%s: arp: IP header does not indicate ARP protocol\n",
client_config.interface);
return 0;
}
if (am->htype != htons(ARPHRD_ETHER)) {
log_line("%s: arp: ARP hardware type field invalid\n",
client_config.interface);
return 0;
}
if (am->ptype != htons(ETH_P_IP)) {
log_line("%s: arp: ARP protocol type field invalid\n",
client_config.interface);
return 0;
}
if (am->hlen != 6) {
log_line("%s: arp: ARP hardware address length invalid\n",
client_config.interface);
return 0;
}
if (am->plen != 4) {
log_line("%s: arp: ARP protocol address length invalid\n",
client_config.interface);
return 0;
}
return 1;
}
// ARP validation functions that will be performed by the BPF if it is
// installed.
static int arp_validate_bpf_defense(struct client_state_t *cs,
struct arpMsg *am)
{
if (memcmp(am->sip4, &cs->clientAddr, 4))
return 0;
if (!memcmp(am->smac, client_config.arp, 6))
return 0;
return 1;
}
static int arp_is_query_reply(struct arpMsg *am)
{
if (am->operation != htons(ARPOP_REPLY))
return 0;
if (memcmp(am->h_dest, client_config.arp, 6))
return 0;
if (memcmp(am->dmac, client_config.arp, 6))
return 0;
return 1;
}
static unsigned arp_gen_probe_wait(struct client_state_t *cs)
{
unsigned range = arp_probe_max - arp_probe_min;
if (range < 1000) range = 1000;
// This is not a uniform distribution but it doesn't matter here.
return arp_probe_min + nk_random_u32(&cs->rnd_state) % range;
}
int arp_defense_timeout(struct client_state_t *cs, long long nowts)
{
(void)nowts; // Suppress warning; parameter necessary but unused.
int ret = 0;
if (garp.wake_ts[AS_DEFENSE] != -1) {
log_line("%s: arp: Defending our lease IP.\n", client_config.interface);
garp.wake_ts[AS_DEFENSE] = -1;
ret = arp_announcement(cs);
}
return ret;
}
int arp_gw_check_timeout(struct client_state_t *cs, long long nowts)
{
if (garp.send_stats[ASEND_GW_PING].count >= garp.gw_check_initpings + 6) {
if (garp.router_replied && !garp.server_replied)
log_line("%s: arp: DHCP agent didn't reply. Getting new lease.\n",
client_config.interface);
else if (!garp.router_replied && garp.server_replied)
log_line("%s: arp: Gateway didn't reply. Getting new lease.\n",
client_config.interface);
else
log_line("%s: arp: DHCP agent and gateway didn't reply. Getting new lease.\n",
client_config.interface);
garp.wake_ts[AS_GW_CHECK] = -1;
return ARPR_CONFLICT;
}
long long rtts = garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
if (nowts < rtts) {
garp.wake_ts[AS_GW_CHECK] = rtts;
return ARPR_OK;
}
if (!garp.router_replied) {
log_line("%s: arp: Still waiting for gateway to reply to arp ping...\n",
client_config.interface);
if (arp_ping(cs, cs->routerAddr) < 0) {
log_line("%s: arp: Failed to send ARP ping in retransmission.\n",
client_config.interface);
return ARPR_FAIL;
}
}
if (!garp.server_replied) {
log_line("%s: arp: Still waiting for DHCP agent to reply to arp ping...\n",
client_config.interface);
if (arp_ping(cs, cs->srcAddr) < 0) {
log_line("%s: arp: Failed to send ARP ping in retransmission.\n",
client_config.interface);
return ARPR_FAIL;
}
}
garp.wake_ts[AS_GW_CHECK] =
garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
return ARPR_OK;
}
int arp_gw_query_timeout(struct client_state_t *cs, long long nowts)
{
long long rtts = garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
if (nowts < rtts) {
garp.wake_ts[AS_GW_QUERY] = rtts;
return ARPR_OK;
}
if (cs->router_arp_state == ARP_QUERY) {
if (cs->router_arp_sent >= ARP_MAX_TRIES) {
log_line("%s: arp: Gateway is ignoring ARPs.\n",
client_config.interface);
cs->router_arp_state = ARP_FAILED;
return ARPR_OK;
}
log_line("%s: arp: Still looking for gateway hardware address...\n",
client_config.interface);
++cs->router_arp_sent;
if (arp_ping(cs, cs->routerAddr) < 0) {
log_line("%s: arp: Failed to send ARP ping in retransmission.\n",
client_config.interface);
return ARPR_FAIL;
}
}
if (cs->server_arp_state == ARP_QUERY) {
if (cs->server_arp_sent >= ARP_MAX_TRIES) {
log_line("%s: arp: DHCP agent is ignoring ARPs.\n",
client_config.interface);
cs->server_arp_state = ARP_FAILED;
return ARPR_OK;
}
log_line("%s: arp: Still looking for DHCP agent hardware address...\n",
client_config.interface);
++cs->server_arp_sent;
if (arp_ping(cs, cs->srcAddr) < 0) {
log_line("%s: arp: Failed to send ARP ping in retransmission.\n",
client_config.interface);
return ARPR_FAIL;
}
}
garp.wake_ts[AS_GW_QUERY] =
garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
return ARPR_OK;
}
int arp_collision_timeout(struct client_state_t *cs, long long nowts)
{
if (nowts - garp.arp_check_start_ts >= ANNOUNCE_WAIT ||
garp.send_stats[ASEND_COLLISION_CHECK].count >= arp_probe_num)
{
char clibuf[INET_ADDRSTRLEN];
struct in_addr temp_addr = {.s_addr = garp.dhcp_packet.yiaddr};
inet_ntop(AF_INET, &temp_addr, clibuf, sizeof clibuf);
log_line("%s: Lease of %s obtained. Lease time is %u seconds.\n",
client_config.interface, clibuf, cs->lease);
cs->clientAddr = garp.dhcp_packet.yiaddr;
cs->program_init = false;
garp.last_conflict_ts = 0;
garp.wake_ts[AS_COLLISION_CHECK] = -1;
if (ifchange_bind(cs, &garp.dhcp_packet) < 0) {
suicide("%s: Failed to set the interface IP address and properties!\n",
client_config.interface);
}
cs->routerAddr = get_option_router(&garp.dhcp_packet);
stop_dhcp_listen(cs);
write_leasefile(temp_addr);
return ARPR_FREE;
}
long long rtts = garp.send_stats[ASEND_COLLISION_CHECK].ts +
garp.probe_wait_time;
if (nowts < rtts) {
garp.wake_ts[AS_COLLISION_CHECK] = rtts;
return ARPR_OK;
}
if (arp_ip_anon_ping(cs, garp.dhcp_packet.yiaddr) < 0) {
log_line("%s: arp: Failed to send ARP ping in retransmission.\n",
client_config.interface);
return ARPR_FAIL;
}
garp.probe_wait_time = arp_gen_probe_wait(cs);
garp.wake_ts[AS_COLLISION_CHECK] =
garp.send_stats[ASEND_COLLISION_CHECK].ts + garp.probe_wait_time;
return ARPR_OK;
}
int arp_query_gateway(struct client_state_t *cs)
{
if (cs->sent_gw_query) {
garp.wake_ts[AS_QUERY_GW_SEND] = -1;
return ARPR_OK;
}
if (arp_get_gw_hwaddr(cs) < 0) {
log_line("%s: (%s) Failed to send request to get gateway and agent hardware addresses: %s\n",
client_config.interface, __func__, strerror(errno));
garp.wake_ts[AS_QUERY_GW_SEND] = curms() + ARP_RETRANS_DELAY;
return ARPR_FAIL;
}
const bool sent_query = cs->router_arp_state == ARP_QUERY
|| cs->server_arp_state == ARP_QUERY;
if (cs->fp_state == FPRINT_NONE) {
if (sent_query)
cs->fp_state = FPRINT_INPROGRESS;
else return ARPR_FAIL;
}
garp.wake_ts[AS_QUERY_GW_SEND] = -1;
return ARPR_OK;
}
// 1 == not yet time, 0 == timed out, success, -1 == timed out, failure
int arp_query_gateway_timeout(struct client_state_t *cs, long long nowts)
{
long long rtts = garp.wake_ts[AS_QUERY_GW_SEND];
if (rtts == -1) return 0;
if (nowts < rtts) return 1;
return arp_query_gateway(cs) == ARPR_OK ? 0 : -1;
}
int arp_announce(struct client_state_t *cs)
{
if (cs->sent_first_announce && cs->sent_second_announce) {
garp.wake_ts[AS_ANNOUNCE] = -1;
return ARPR_OK;
}
if (arp_announcement(cs) < 0) {
log_line("%s: (%s) Failed to send ARP announcement: %s\n",
client_config.interface, __func__, strerror(errno));
garp.wake_ts[AS_ANNOUNCE] = curms() + ARP_RETRANS_DELAY ;
return ARPR_FAIL;
}
if (!cs->sent_first_announce)
cs->sent_first_announce = true;
else if (!cs->sent_second_announce)
cs->sent_second_announce = true;
if (!cs->sent_first_announce || !cs->sent_second_announce)
garp.wake_ts[AS_ANNOUNCE] = curms() + ARP_RETRANS_DELAY;
else
garp.wake_ts[AS_ANNOUNCE] = -1;
return ARPR_OK;
}
// 1 == not yet time, 0 == timed out, success, -1 == timed out, failure
int arp_announce_timeout(struct client_state_t *cs, long long nowts)
{
long long rtts = garp.wake_ts[AS_ANNOUNCE];
if (rtts == -1) return 0;
if (nowts < rtts) return 1;
return arp_announce(cs) == ARPR_OK ? 0 : -1;
}
int arp_do_defense(struct client_state_t *cs)
{
// Even though the BPF will usually catch this case, sometimes there are
// packets still in the socket buffer that arrived before the defense
// BPF was installed, so it's necessary to check here.
if (!arp_validate_bpf_defense(cs, &garp.reply))
return ARPR_OK;
log_line("%s: arp: Detected a peer attempting to use our IP!\n", client_config.interface);
long long nowts = curms();
garp.wake_ts[AS_DEFENSE] = -1;
if (!garp.last_conflict_ts ||
nowts - garp.last_conflict_ts < DEFEND_INTERVAL) {
log_line("%s: arp: Defending our lease IP.\n", client_config.interface);
if (arp_announcement(cs) < 0)
return ARPR_FAIL;
} else if (!garp.relentless_def) {
log_line("%s: arp: Conflicting peer is persistent. Requesting new lease.\n",
client_config.interface);
send_release(cs);
return ARPR_CONFLICT;
} else {
garp.wake_ts[AS_DEFENSE] =
garp.send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL;
}
garp.total_conflicts++;
garp.last_conflict_ts = nowts;
return ARPR_OK;
}
int arp_do_gw_query(struct client_state_t *cs)
{
if (!arp_is_query_reply(&garp.reply))
return ARPR_OK;
if (cs->router_arp_state != ARP_FOUND && !memcmp(garp.reply.sip4, &cs->routerAddr, 4)) {
memcpy(cs->routerArp, garp.reply.smac, 6);
log_line("%s: arp: Gateway hardware address %02x:%02x:%02x:%02x:%02x:%02x\n",
client_config.interface, cs->routerArp[0], cs->routerArp[1],
cs->routerArp[2], cs->routerArp[3],
cs->routerArp[4], cs->routerArp[5]);
cs->router_arp_state = ARP_FOUND;
if (cs->routerAddr == cs->srcAddr)
goto server_is_router;
if (cs->server_arp_state != ARP_QUERY) {
garp.wake_ts[AS_GW_QUERY] = -1;
if (arp_open_fd(cs, true) < 0)
return ARPR_FAIL;
return ARPR_FREE;
}
return ARPR_OK;
}
if (cs->server_arp_state != ARP_FOUND && !memcmp(garp.reply.sip4, &cs->srcAddr, 4)) {
server_is_router:
memcpy(cs->serverArp, garp.reply.smac, 6);
log_line("%s: arp: DHCP agent hardware address %02x:%02x:%02x:%02x:%02x:%02x\n",
client_config.interface, cs->serverArp[0], cs->serverArp[1],
cs->serverArp[2], cs->serverArp[3],
cs->serverArp[4], cs->serverArp[5]);
cs->server_arp_state = ARP_FOUND;
if (cs->router_arp_state != ARP_QUERY) {
garp.wake_ts[AS_GW_QUERY] = -1;
if (arp_open_fd(cs, true) < 0)
return ARPR_FAIL;
return ARPR_FREE;
}
return ARPR_OK;
}
return ARPR_OK;
}
int arp_do_collision_check(struct client_state_t *cs)
{
if (!arp_is_query_reply(&garp.reply))
return ARPR_OK;
// If this packet was sent from our lease IP, and does not have a
// MAC address matching our own (the latter check guards against stupid
// hubs or repeaters), then it's a conflict and thus a failure.
if (!memcmp(garp.reply.sip4, &garp.dhcp_packet.yiaddr, 4) &&
!memcmp(client_config.arp, garp.reply.smac, 6))
{
garp.total_conflicts++;
garp.wake_ts[AS_COLLISION_CHECK] = -1;
log_line("%s: arp: Offered address is in use. Declining.\n",
client_config.interface);
int r = send_decline(cs, garp.dhcp_packet.yiaddr);
if (r < 0) {
log_line("%s: Failed to send a decline notice packet.\n",
client_config.interface);
return ARPR_FAIL;
}
return ARPR_CONFLICT;
}
return ARPR_OK;
}
int arp_do_gw_check(struct client_state_t *cs)
{
if (!arp_is_query_reply(&garp.reply))
return ARPR_OK;
if (!memcmp(garp.reply.sip4, &cs->routerAddr, 4)) {
// Success only if the router/gw MAC matches stored value
if (!memcmp(cs->routerArp, garp.reply.smac, 6)) {
garp.router_replied = true;
if (cs->routerAddr == cs->srcAddr)
goto server_is_router;
if (garp.server_replied)
return arp_gw_success(cs); // FREE or FAIL
return ARPR_OK;
}
log_line("%s: arp: Gateway is different. Getting a new lease.\n",
client_config.interface);
garp.wake_ts[AS_GW_CHECK] = -1;
return ARPR_CONFLICT;
}
if (!memcmp(garp.reply.sip4, &cs->srcAddr, 4)) {
server_is_router:
// Success only if the server MAC matches stored value
if (!memcmp(cs->serverArp, garp.reply.smac, 6)) {
garp.server_replied = true;
if (garp.router_replied)
return arp_gw_success(cs); // FREE or FAIL
return ARPR_OK;
}
log_line("%s: arp: DHCP agent is different. Getting a new lease.\n",
client_config.interface);
garp.wake_ts[AS_GW_CHECK] = -1;
return ARPR_CONFLICT;
}
return ARPR_OK;
}
bool arp_packet_get(struct client_state_t *cs)
{
if (cs->arpFd < 0)
return false;
struct arpMsg amsg;
ssize_t r = 0;
size_t bytes_read = 0;
if (bytes_read < sizeof amsg) {
r = safe_read(cs->arpFd, (char *)&amsg + bytes_read,
sizeof amsg - bytes_read);
if (r == 0)
return false;
if (r < 0) {
log_line("%s: (%s) ARP response read failed: %s\n",
client_config.interface, __func__, strerror(errno));
// Timeouts will trigger anyway without being forced.
arp_min_close_fd(cs);
if (arp_open_fd(cs, cs->arp_is_defense) < 0)
suicide("%s: (%s) Failed to reopen ARP fd: %s\n",
client_config.interface, __func__, strerror(errno));
return false;
}
bytes_read += (size_t)r;
}
if (bytes_read < ARP_MSG_SIZE)
return false;
// Emulate the BPF filters if they are not in use.
if (!garp.using_bpf &&
(!arp_validate_bpf(&amsg) ||
(cs->arp_is_defense &&
!arp_validate_bpf_defense(cs, &amsg)))) {
return false;
}
memcpy(&garp.reply, &amsg, sizeof garp.reply);
return true;
}
long long arp_get_wake_ts(void)
{
long long mt = -1;
for (int i = 0; i < AS_MAX; ++i) {
if (garp.wake_ts[i] < 0)
continue;
if (mt < 0 || mt > garp.wake_ts[i])
mt = garp.wake_ts[i];
}
return mt;
}