forked from vanrein/6bed4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
peer.c
1936 lines (1829 loc) · 63.5 KB
/
peer.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
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* 6bed4/peer.c -- Peer-to-Peer IPv6-anywhere with 6bed4 -- peer.c
*
* This is an implementation of neighbour and router discovery over a
* tunnel that packs IPv6 inside UDP/IPv4. This tunnel mechanism is
* so efficient that the server administrators need not mind if it is
* distributed widely.
*
* From: Rick van Rein <[email protected]>
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <syslog.h>
#ifndef LOG_PERROR
#define LOG_PERROR LOG_CONS /* LOG_PERROR is non-POSIX, LOG_CONS is */
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <asm/types.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
/* The following will initially fail, due to an IANA obligation to avoid
* default builds with non-standard options.
*/
#include "nonstd.h"
#define MTU 1280
typedef enum {
METRIC_LOW,
METRIC_MEDIUM,
METRIC_HIGH
} metric_t;
/*
* The HAVE_SETUP_TUNNEL variable is used to determine whether absense of
* the -t option leads to an error, or to an attempt to setup the tunnel.
* The setup_tunnel() function used for that is defined per platform, such
* as for LINUX. Remember to maintain the manpage's optionality for -t.
*/
#undef HAVE_SETUP_TUNNEL
/* Global variables */
char *program;
int rtsox = -1;
int v4sox = -1;
int v6sox = -1;
int v4mcast = -1;
uint8_t v4qos = 0; /* Current QoS setting on UDP/IPv4 socket */
uint32_t v6tc = 0; /* Current QoS used by the IPv6 socket */
uint8_t v4ttl = 64; /* Default TTL setting on UDP/IPv4 socket */
int v4ttl_mcast = -1; /* Multicast TTL for LAN explorations */
char *v4server = NULL;
char *v6server = NULL;
char v6prefix [INET6_ADDRSTRLEN];
uint8_t v6lladdr [6];
const uint8_t v6listen_linklocal [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t v6listen_linklocal_complete [16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
struct sockaddr_nl rtname;
struct sockaddr_nl rtkernel;
struct sockaddr_in v4name;
struct sockaddr_in v4peer;
struct sockaddr_in6 v6name;
struct sockaddr_in v4bind;
struct sockaddr_in v4allnodes;
struct in6_addr v6listen;
//TODO:NEEDNOT// struct in6_addr v6listen_complete;
struct in_addr v4listen;
struct {
struct ethhdr eth;
union {
struct {
struct ip6_hdr v6hdr;
uint8_t data [MTU - sizeof (struct ip6_hdr)];
} idata;
struct {
struct ip6_hdr v6hdr;
struct icmp6_hdr v6icmphdr;
} ndata;
} udata;
} __attribute__((packed)) v4data6;
#define v4ether (v4data6.eth)
#define v4data ((uint8_t *) &v4data6.udata)
#define v4hdr6 (&v4data6.udata.idata.v6hdr)
#define v4src6 (&v4data6.udata.idata.v6hdr.ip6_src)
#define v4dst6 (&v4data6.udata.idata.v6hdr.ip6_dst)
#define v4v6plen (v4data6.udata.ndata.v6hdr.ip6_plen)
#define v4v6nexthdr (v4data6.udata.ndata.v6hdr.ip6_nxt)
#define v4v6hoplimit (v4data6.udata.ndata.v6hdr.ip6_hops)
#define v4icmp6 (&v4data6.udata.ndata.v6icmphdr)
#define v4v6icmpdata (v4data6.udata.ndata.v6icmphdr.icmp6_data8)
#define v4v6icmptype (v4data6.udata.ndata.v6icmphdr.icmp6_type)
#define v4v6icmpcode (v4data6.udata.ndata.v6icmphdr.icmp6_code)
#define v4v6icmpcksum (v4data6.udata.ndata.v6icmphdr.icmp6_cksum)
#define v4v6ndtarget (&v4data6.udata.ndata.v6icmphdr.icmp6_data8 [4])
struct {
struct ethhdr eth;
union {
uint8_t data [MTU];
struct {
struct ip6_hdr v6hdr;
struct icmp6_hdr v6icmp;
} __attribute__((packed)) ndata;
} udata;
} __attribute__((packed)) v6data6;
#define v6ether (v6data6.eth)
#define v6data (v6data6.udata.data)
#define v6hdr6 (&v6data6.udata.ndata.v6hdr)
#define v6hops (v6data6.udata.ndata.v6hdr.ip6_hops)
#define v6type (v6data6.udata.ndata.v6hdr.ip6_nxt)
#define v6plen (v6data6.udata.ndata.v6hdr.ip6_plen)
#define v6src6 (&v6data6.udata.ndata.v6hdr.ip6_src)
#define v6dst6 (&v6data6.udata.ndata.v6hdr.ip6_dst)
#define v6icmp6type (v6data6.udata.ndata.v6icmp.icmp6_type)
#define v6icmp6code (v6data6.udata.ndata.v6icmp.icmp6_code)
#define v6icmp6data (v6data6.udata.ndata.v6icmp.icmp6_data8)
#define v6icmp6csum (v6data6.udata.ndata.v6icmp.icmp6_cksum)
#define v6ndtarget (&v6data6.udata.ndata.v6icmp.icmp6_data16[2])
/* Structure for tasks in neighbor discovery queues
*/
struct ndqueue {
struct ndqueue *next;
struct timeval tv;
struct in6_addr source;
struct in6_addr target;
uint8_t source_lladdr [6];
uint8_t todo_lancast, todo_direct;
};
/* Round-robin queue for regular tasks, starting at previous value */
struct ndqueue *ndqueue = NULL;
struct ndqueue *freequeue = NULL;
uint32_t freequeue_items = 100;
/* The time for the next scheduled maintenance: routersol or keepalive.
* The milliseconds are always 0 for maintenance tasks.
*/
time_t maintenance_time_sec;
time_t maintenance_time_cycle = 0;
time_t maintenance_time_cycle_max = 30;
bool got_lladdr = false;
time_t keepalive_period = 30;
time_t keepalive_ttl = -1;
/* The network packet structure of a 6bed4 Router Solicitation */
uint8_t ipv6_router_solicitation [] = {
// IPv6 header
0x60, 0x00, 0x00, 0x00,
16 / 256, 16 % 256, IPPROTO_ICMPV6, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // unspecd src
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
// ICMPv6 header: router solicitation
ND_ROUTER_SOLICIT, 0, 0x7a, 0xae, // Checksum courtesy of WireShark :)
// ICMPv6 body: reserved
0, 0, 0, 0,
// ICMPv6 option: source link layer address 0x0001 (end-aligned)
0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
};
uint8_t ipv6_defaultrouter_neighbor_advertisement [] = {
// IPv6 header
0x60, 0x00, 0x00, 0x00,
32 / 256, 32 % 256, IPPROTO_ICMPV6, 255,
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // src is default router
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,// dst is all-nodes multicast, portable?
// ICMPv6 header: neighbor solicitation
ND_NEIGHBOR_ADVERT, 0, 0x36, 0xf2, // Checksum courtesy of WireShark :)
// ICMPv6 Neighbor Advertisement: flags
0x40, 0, 0, 0,
// Target: fe80::
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // the targeted neighbor
// ICMPv6 option: target link layer address
2, 1,
UDP_PORT_6BED4 % 256, UDP_PORT_6BED4 / 256,
SERVER_6BED4_IPV4_INT0, SERVER_6BED4_IPV4_INT1, SERVER_6BED4_IPV4_INT2, SERVER_6BED4_IPV4_INT3
};
uint8_t router_linklocal_address [] = {
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
};
//TODO// Complete with the if-id of the 6bed4 Router:
uint8_t router_linklocal_address_complete [] = {
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00,
};
uint8_t democlient_linklocal_address [] = {
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
};
uint8_t allnodes_linklocal_address [] = {
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01,
};
uint8_t allrouters_linklocal_address [] = {
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
};
uint8_t solicitednodes_linklocal_prefix [13] = {
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff
};
bool default_route = false;
bool foreground = false;
bool log_to_stderr = false;
bool multicast = true;
/*
*
* Driver routines
*
*/
#ifdef LINUX
#define HAVE_SETUP_TUNNEL
static struct ifreq ifreq;
static bool have_tunnel = false;
/* Implement the setup_tunnel() command for Linux.
* Return true on success, false on failure.
*/
bool setup_tunnel (void) {
if (v6sox == -1) {
v6sox = open ("/dev/net/tun", O_RDWR);
}
if (v6sox == -1) {
syslog (LOG_ERR, "%s: Failed to access tunnel driver on /dev/net/tun: %s\n", program, strerror (errno));
return 0;
}
bool ok = true;
int flags = fcntl (v6sox, F_GETFL, 0);
if (flags == -1) {
syslog (LOG_CRIT, "Failed to retrieve flags for the tunnel file descriptor: %s\n", strerror (errno));
ok = false;
}
if (!have_tunnel) {
memset (&ifreq, 0, sizeof (ifreq));
strncpy (ifreq.ifr_name, "6bed4", IFNAMSIZ);
ifreq.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ok && (ioctl (v6sox, TUNSETIFF, (void *) &ifreq) == -1)) {
syslog (LOG_CRIT, "Failed to set interface name: %s\n", strerror (errno));
ok = false;
} else {
have_tunnel = ok;
}
ifreq.ifr_name [IFNAMSIZ] = 0;
ifreq.ifr_ifindex = if_nametoindex (ifreq.ifr_name);
syslog (LOG_DEBUG, "Found Interface Index %d for name %s\n", ifreq.ifr_ifindex, ifreq.ifr_name);
ok = ok & (ifreq.ifr_ifindex != 0);
}
char cmd [512+1];
#if 0
snprintf (cmd, 512, "/sbin/ip addr add fe80::1 dev %s scope link", ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
#endif
snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.forwarding=0", ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
snprintf (cmd, 512, "/sbin/sysctl -q -w net.ipv6.conf.%s.accept_dad=0", ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
if (!ok) {
close (v6sox); /* This removes the tunnel interface */
v6sox = -1;
}
return ok;
}
bool setup_tunnel_address (void) {
bool ok = have_tunnel;
char cmd [512+1];
// snprintf (cmd, 512, "/sbin/ifconfig %s hw ether 98:1e:53:a4:cf:6e", ifreq.ifr_name, MTU);
snprintf (cmd, 512, "/sbin/ip link set %s address %02x:%02x:%02x:%02x:%02x:%02x", ifreq.ifr_name, v6lladdr [0], v6lladdr [1], v6lladdr [2], v6lladdr [3], v6lladdr [4], v6lladdr [5]);
if (ok && system (cmd) != 0) {
syslog (LOG_CRIT, "Bad news!\n");
ok = false;
}
snprintf (cmd, 512, "/sbin/ip link set %s up mtu %d", ifreq.ifr_name, MTU);
if (ok && system (cmd) != 0) {
ok = false;
}
#ifdef TODO_BELIEVE_THAT_ROUTES_SHOULD_BE_ADDED_STATICALLY
snprintf (cmd, 512, "/sbin/ip -6 route add 2001:610:188:2001::/64 mtu 1280 dev %s", ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
#endif
#ifdef TODO_COMPENSATE_FOR_AUTOCONFIG
snprintf (cmd, 512, "/sbin/ip -6 addr add %s/64 dev %s", v6prefix, ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
#endif
#if 0
if (default_route) {
snprintf (cmd, 512, "/sbin/ip -6 route add default via fe80:: dev %s", ifreq.ifr_name);
if (ok && system (cmd) != 0) {
ok = false;
}
}
#endif
return ok;
}
#endif /* LINUX */
/*
*
* Utility functions
*
*/
/* Look for an entry in the 50ms-cycled Neighbor Discovery queue.
* Match the target address. Return the entry found, or NULL.
*/
struct ndqueue *findqueue (struct in6_addr *target) {
struct ndqueue *ndq = ndqueue;
if (ndq) {
do {
if (memcmp (target, &ndq->target, 16) == 0) {
return ndq;
}
ndq = ndq->next;
} while (ndq != ndqueue);
}
return NULL;
}
/* Enter an item in the 50ms-cycled Neighbor Discovery queue.
* Retrieve its storage space from the free queue.
* TODO: Avoid double entries by looking up entries first -> "requeue?"
*/
static int TODO_qlen;
void enqueue (struct in6_addr *target, struct in6_addr *v6src, uint8_t *source_lladdr) {
//
// Refuse to create double entries
if (findqueue (target)) {
return;
}
//
// Allocate a free item to enqueue
struct ndqueue *new = freequeue;
if (!new) {
// Temporarily overflown with ND -> drop the request
return;
}
char tgt [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, target, tgt, sizeof (tgt));
syslog (LOG_DEBUG, "Queue++ => %d, looking for %s\n", ++TODO_qlen, tgt);
freequeue = freequeue->next;
//
// Setup the new entry with target details
memcpy (&new->target, target, sizeof (new->target));
memcpy (&new->source, v6src, sizeof (new->source));
memcpy (&new->source_lladdr, source_lladdr, sizeof (new->source_lladdr));
new->todo_lancast = (v4mcast == -1)? 0: 2;
new->todo_direct = 3;
//
// Time the new item to run instantly
new->tv.tv_sec = 0;
//
// Enqueue the new item in front of the queue
if (ndqueue) {
new->next = ndqueue->next;
ndqueue->next = new;
} else {
new->next = new;
ndqueue = new;
}
}
/* Remove an item from the 50ms-cycled Neighbor Discovery queue.
* Enter its storage space in the free queue.
*/
void dequeue (struct ndqueue *togo) {
struct ndqueue *prev = ndqueue;
do {
if (prev->next == togo) {
if (togo->next != togo) {
prev->next = togo->next;
if (ndqueue == togo) {
ndqueue = togo->next;
}
} else {
// Must be the only queued item
ndqueue = NULL;
}
togo->next = freequeue;
freequeue = togo;
syslog (LOG_DEBUG, "Queue-- => %d\n", --TODO_qlen);
return;
}
prev = prev->next;
} while (prev != ndqueue);
}
/*
* Calculate the ICMPv6 checksum field
*/
uint16_t icmp6_checksum (uint8_t *ipv6hdr, size_t payloadlen) {
uint16_t plenword = htons (payloadlen);
uint16_t nxthword = htons (IPPROTO_ICMPV6);
uint16_t *areaptr [] = { (uint16_t *) &ipv6hdr [8], (uint16_t *) &ipv6hdr [24], &plenword, &nxthword, (uint16_t *) &ipv6hdr [40], (uint16_t *) &ipv6hdr [40 + 4] };
uint8_t areawords [] = { 8, 8, 1, 1, 1, payloadlen/2 - 2 };
uint32_t csum = 0;
u_int8_t i, j;
for (i=0; i < 6; i++) {
uint16_t *area = areaptr [i];
for (j=0; j<areawords [i]; j++) {
csum += ntohs (area [j]);
}
}
csum = (csum & 0xffff) + (csum >> 16);
csum = (csum & 0xffff) + (csum >> 16);
csum = htons (~csum);
return csum;
}
/*
* Send a Redirect reply. This is in response to a v4v6data message,
* and is directed straight at the origin's address but sent with a
* lower metric.
*
* Note: Although the packet arrived in v4data6, the reply is built
* in v6data6 and sent from there as though it had come from
* the IPv6 stack.
*/
void redirect_reply (uint8_t *ngbc_llremote, metric_t ngbc_metric) {
void handle_6to4_plain_unicast (const ssize_t pktlen, const uint8_t *lladdr);
v6icmp6type = ND_REDIRECT;
v6icmp6code = 0;
v6icmp6data [0] =
v6icmp6data [1] =
v6icmp6data [2] =
v6icmp6data [3] = 0; // Reserved
memcpy (v6icmp6data + 4, &v6listen, 16);
// Target IPv6 address
switch (ngbc_metric) {
// Destination Address suggestion
case METRIC_LOW:
//
// Redirect to the local-subnet IPv4 address
memcpy (v6icmp6data + 4 + 16, v6listen_linklocal, 8);
v6icmp6data [4 + 16 + 8 ] = v4peer.sin_port & 0x00ff;
v6icmp6data [4 + 16 + 9 ] = v4peer.sin_port >> 8;
memcpy (v6icmp6data + 4 + 16 + 12, &v4peer.sin_addr, 4);
v6icmp6data [4 + 16 + 10] = v4v6icmpdata [4 + 16 + 12];
v6icmp6data [4 + 16 + 11] = 0xff;
v6icmp6data [4 + 16 + 12] = 0xfe;
break;
case METRIC_MEDIUM:
memcpy (v6icmp6data + 4 + 16, v6listen_linklocal_complete, 16);
break;
case METRIC_HIGH:
default:
return; /* no cause for Redirect, drop */
}
v6type = IPPROTO_ICMPV6;
v6plen = htons (8 + 16 + 16);
memcpy (v6src6, &v6listen, 16);
memcpy (v6dst6, v4src6, 16);
v6icmp6csum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 16);
handle_6to4_plain_unicast (sizeof (struct ethhdr) + 40 + 8 + 16 + 16, ngbc_llremote);
}
/* Append the current prefix to an ICMPv6 message. Incoming optidx
* and return values signify original and new offset for ICMPv6 options.
* The endlife parameter must be set to obtain zero lifetimes, thus
* instructing the tunnel client to stop using an invalid prefix.
*/
size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
v6icmp6data [optidx++] = 3; // Type
v6icmp6data [optidx++] = 4; // Length
v6icmp6data [optidx++] = 64; // This is a /64 prefix
#ifndef COMPENSATE_FOR_AUTOCONF
v6icmp6data [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
#else
//TODO// Temporary fix: "ip -6 addr add .../64 dev 6bed4"
v6icmp6data [optidx++] = 0x80; // L=1, A=0, Reserved1=0
#endif
memset (v6icmp6data + optidx, endlife? 0x00: 0xff, 8);
optidx += 8;
// Valid Lifetime: Zero / Infinite
// Preferred Lifetime: Zero / Infinite
memset (v6icmp6data + optidx, 0, 4);
optidx += 4;
// Reserved2=0
memcpy (v6icmp6data + optidx + 0, &v6listen, 8);
memset (v6icmp6data + optidx + 8, 0, 8);
// Set IPv6 prefix
optidx += 16;
return optidx;
}
/*
* Construct a Neighbor Advertisement message, providing the
* Public 6bed4 Service as the link-local address.
*
* This is done immediately when the IPv6 stack requests the link-local
* address for fe80:: through Router Solicition. In addition, it is the
* fallback response used when attempts to contact the remote peer at its
* direct IPv4 address and UDP port (its 6bed4 address) fails repeatedly.
*
* This routine is called with info==NULL to respond to an fe80::
* Neighbor Solicitation, otherwise with an info pointer containing
* a target IPv6 address to service.
*/
void advertise_6bed4_public_service (struct ndqueue *info) {
if (info) {
memcpy (v6ether.h_dest, info->source_lladdr, 6);
} else {
memcpy (v6ether.h_dest, v6ether.h_source, 6);
}
memcpy (v6ether.h_source, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
memcpy (v6data, ipv6_defaultrouter_neighbor_advertisement, 8);
if (info) {
memcpy (v6dst6, &info->source, 16);
} else {
memcpy (v6dst6, v6src6, 16);
}
if (info) {
memcpy (v6src6, &info->target, 16);
} else {
memcpy (v6src6, router_linklocal_address_complete, 16);
}
//TODO:OVERWROTE// memcpy (v6data + 8, ipv6_defaultrouter_neighbor_advertisement + 8, 16);
memcpy (v6data + 8 + 16 + 16, ipv6_defaultrouter_neighbor_advertisement + 8 + 16 + 16, sizeof (ipv6_defaultrouter_neighbor_advertisement) - 8 - 16 - 16);
if (info) {
// Overwrite target only for queued requests
memcpy (&v6icmp6data [4], &info->target, 16);
}
v6icmp6csum = icmp6_checksum ((uint8_t *) v6hdr6, 32);
int sent = write (v6sox, &v6data6, sizeof (struct ethhdr) + sizeof (ipv6_defaultrouter_neighbor_advertisement));
if (info) {
syslog (LOG_DEBUG, "TODO: Neighbor Discovery failed to contact directly -- standard response provided\n");
} else {
syslog (LOG_DEBUG, "TODO: Neighbor Discovery for Public 6bed4 Service -- standard response provided\n");
}
}
/*
* Test if the provided IPv6 address matches the prefix used for 6bed4.
*/
static inline bool prefix_6bed4 (struct in6_addr *ip6) {
return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
}
/*
* Validate the originator's IPv6 address. It should match the
* UDP/IPv4 coordinates of the receiving 6bed4 socket. Also,
* the /64 prefix must match that of v6listen.
*/
bool validate_originator (struct sockaddr_in *sin, struct in6_addr *ip6) {
if ((sin->sin_addr.s_addr == v4peer.sin_addr.s_addr) && (sin->sin_port == v4peer.sin_port)) {
return true;
}
uint16_t port = ntohs (sin->sin_port);
uint32_t addr = ntohl (sin->sin_addr.s_addr);
if (memcmp (ip6, v6listen_linklocal, 8) != 0)
if (!prefix_6bed4 (ip6)) {
return false;
}
if ((port % 256) != (ip6->s6_addr [8] ^ 0x02)) {
return false;
}
if ((port / 256) != ip6->s6_addr [9]) {
return false;
}
if ((addr >> 24) != ip6->s6_addr [10]) {
return false;
}
if ((addr & 0x00ffffff) != (htonl (ip6->s6_addr32 [3]) & 0x00ffffff)) {
return false;
}
return true;
}
/*
* Translate a Link-Local Address to its metric. The metrics are
* numbered so that a higher number indicates a more costly path
* over which to connect. The values of the metric should not be
* published, but be treated as an opaque value with a complete
* ordering (that is: <, <=, >=, > relations) defined on it.
*/
metric_t lladdr_metric (uint8_t *lladdr) {
uint32_t ipv4 = * (uint32_t *) (lladdr + 2);
//
// Metric 2: The 6bed4 Router address
if (ipv4 == v4peer.sin_addr.s_addr) {
return METRIC_HIGH;
}
//
// Metric 0: Private Addresses, as per RFC 1918
if ((ipv4 & 0xff000000) == 0x0a000000) {
return METRIC_LOW; /* 10.0.0./8 */
}
if ((ipv4 & 0xffff0000) == 0xc0a80000) {
return METRIC_LOW; /* 192.168.0.0/16 */
}
if ((ipv4 & 0xfff00000) == 0xac100000) {
return METRIC_LOW; /* 172.16.0.0/12 */
}
//
// Metric 1: Direct IPv4 contact is any other address
// Correctness should be checked elsewhere
return METRIC_MEDIUM;
}
/*
* Retrieve the Link-Local Address, if any, for a given 6bed4 Peer.
* Return true on success, false on failure to find it. The lladdr
* parameter is only overwritten in case of success.
*
* Obviously, there is a point where it is more efficient to not
* lookup the cache for every request, but to cache it locally
* and limit the lookup frequency. This low-traffic optimal version
* is used here for initial simplicity, and because this is a peer
* daemon and a reference implementation. But who knows what people
* will submit as patches...
*
* Note: This code is specific to Linux, but note that BSD also has a
* NetLink concept, so it may port without needing to resort to
* shell commands running slowly in separate processes.
* Note: The interface for Linux is under-documented. Work may be
* needed to handle exception situations, such as going over
* invisible boundaries on the number of neighbours. Similarly,
* the use of alignment macros is rather unclear. This is not
* how I prefer to write code, but it's the best I can do now.
*/
bool lookup_neighbor (uint8_t *ipv6, uint8_t *lladdr) {
struct mymsg {
struct nlmsghdr hd;
struct ndmsg nd;
uint8_t arg [16384];
} msg;
memset (&msg, 0, sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
msg.hd.nlmsg_len = NLMSG_LENGTH (sizeof (msg.nd));
msg.hd.nlmsg_type = RTM_GETNEIGH;
msg.hd.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT /* | NLM_F_MATCH */;
msg.hd.nlmsg_pid = rtname.nl_pid;
msg.nd.ndm_family = AF_INET6;
msg.nd.ndm_state = NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE; // Ignored by the kernel?
msg.nd.ndm_ifindex = ifreq.ifr_ifindex; // Ignored by the kernel?
// How to select an IPv6 address? Ignored by the kernel?
#if 0
struct rtattr *ra1 = (struct rtattr *) (((char *) &msg) + sizeof (struct nlmsghdr) + sizeof (struct ndmsg));
ra1->rta_type = NDA_DST; // lookup IPv6 address
ra1->rta_len = RTA_LENGTH(16);
msg.hd.nlmsg_len = NLMSG_ALIGN (msg.hd.nlmsg_len) + RTA_LENGTH (16);
memcpy (RTA_DATA (ra1), ipv6, 16);
#endif
if (send (rtsox, &msg, msg.hd.nlmsg_len, MSG_DONTWAIT) == -1) {
return false;
}
ssize_t recvlen;
uint16_t pos = 0;
{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, ipv6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Looking up v6addr %s\n", buf); }
while (recvlen = recv (rtsox, ((char *) &msg) + pos, sizeof (msg) - pos, MSG_DONTWAIT), recvlen > 0) {
syslog (LOG_DEBUG, "Message of %d bytes from neighbor cache, total is now %d\n", recvlen, pos + recvlen);
recvlen += pos;
pos = 0;
struct mymsg *resp;
while (resp = (struct mymsg *) (((char *) &msg) + pos),
(pos + sizeof (struct nlmsghdr) <= recvlen) &&
(pos + resp->hd.nlmsg_len <= recvlen)) {
bool ok = true, match = false;
uint8_t *result = NULL;
if (resp->hd.nlmsg_type == NLMSG_DONE) {
return false;
} else if (resp->hd.nlmsg_type != RTM_NEWNEIGH) {
syslog (LOG_ERR, "Kernel sent an unexpected nlmsg_type 0x%02x, ending neighbor interpretation\n", resp->hd.nlmsg_type);
ok = false;
} else if (resp->nd.ndm_ifindex != ifreq.ifr_ifindex) {
ok = false;
} else if (resp->nd.ndm_family != AF_INET6) {
syslog (LOG_ERR, "Kernel reported unknown neighbor family %d\n", resp->nd.ndm_family);
ok = false;
} else
if (!(resp->nd.ndm_state & (NUD_REACHABLE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT | NUD_STALE))) {
ok = false;
}
struct rtattr *ra = (struct rtattr *) ((char *) &resp + pos + sizeof (struct nlmsghdr) + sizeof (struct ndmsg) + 8);
ssize_t rapos = 0;
while (ok && (rapos + ra->rta_len <= resp->hd.nlmsg_len)) {
switch (ra->rta_type) {
case NDA_DST:
{ char buf [INET6_ADDRSTRLEN]; inet_ntop (AF_INET6, RTA_DATA (ra), buf, sizeof (buf)); syslog (LOG_DEBUG, "Comparing against %s\n", buf); }
if (memcmp (ipv6, RTA_DATA (ra), 16) == 0) {
match = true;
}
break;
case NDA_LLADDR:
result = RTA_DATA (ra);
break;
case NDA_PROBES:
case NDA_CACHEINFO:
default:
break; /* not of interest, skip */
}
rapos += ((ra->rta_len - 1) | 0x00000003) + 1;
ra = (struct rtattr *) (((char *) ra) + (((ra->rta_len - 1) | 0x0000003) + 1));
}
if (ok && match && result) {
memcpy (lladdr, result, 6);
return true; /* Yippy! Erfolg! */
}
pos += resp->hd.nlmsg_len;
}
// Copy remaining partial message to the beginning, continue from there
memcpy (&msg, ((char *) &msg) + pos, recvlen - pos);
pos = recvlen - pos;
}
return false;
}
/*
* Major packet processing functions
*/
/* Handle the IPv4 message pointed at by msg, checking if (TODO:HUH?) the IPv4:port
* data matches the lower half of the IPv6 sender address. Drop silently
* if this is not the case. TODO: or send ICMP?
*/
void handle_4to6_plain (ssize_t v4datalen, struct sockaddr_in *sin) {
//
// Send the unwrapped IPv6 message out over v6sox
v4ether.h_proto = htons (ETH_P_IPV6);
memcpy (v4ether.h_dest, v6lladdr, 6);
v4ether.h_source [0] = ntohs (sin->sin_port) % 256;
v4ether.h_source [1] = ntohs (sin->sin_port) / 256;
memcpy (v4ether.h_source + 2, &sin->sin_addr, 4);
syslog (LOG_INFO, "Writing IPv6, result = %d\n",
write (v6sox, &v4data6, sizeof (struct ethhdr) + v4datalen)
)
;
}
/* Handle the IPv4 message pointed at by msg as a neighbouring command.
*
* Type Code ICMPv6 meaning Handling
* ---- ---- ----------------------------- ----------------------------
* 133 0 Router Solicitation Ignore
* 134 0 Router Advertisement Setup Tunnel with Prefix
* 135 0 Neighbour Solicitation Send Neighbour Advertisement
* 136 0 Neighbour Advertisement Ignore
* 137 0 Redirect Ignore
*/
void handle_4to6_nd (struct sockaddr_in *sin, ssize_t v4ngbcmdlen) {
uint16_t srclinklayer;
uint8_t *destprefix = NULL;
#ifdef TODO_DEPRECATED
uint8_t *destlladdr = NULL;
#endif
struct ndqueue *ndq;
if (v4ngbcmdlen < sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)) {
return;
}
//
if (v4v6icmpcode != 0) {
return;
}
if (icmp6_checksum (v4data, v4ngbcmdlen - sizeof (struct ip6_hdr)) != v4v6icmpcksum) {
return;
}
//
// Approved. Perform neighbourly courtesy.
switch (v4v6icmptype) {
case ND_ROUTER_SOLICIT:
return; /* this is not a router, drop */
case ND_ROUTER_ADVERT:
//
// Validate Router Advertisement
if (ntohs (v4v6plen) < sizeof (struct icmp6_hdr) + 16) {
return; /* strange length, drop */
}
if (v4v6icmpdata [1] & 0x80 != 0x00) {
return; /* indecent proposal to use DHCPv6, drop */
}
if (memcmp (v4src6, router_linklocal_address, 16) != 0) {
return; /* not from router, drop */
}
if (memcmp (v4dst6, democlient_linklocal_address, 8) != 0) {
return; /* no address setup for me, drop */
}
if ((v4dst6->s6_addr [11] != 0xff) || (v4dst6->s6_addr [12] != 0xfe)) {
return; /* funny interface identifier, drop */
}
if (v4dst6->s6_addr [8] & 0x01) {
syslog (LOG_WARNING, "TODO: Ignoring (by accepting) an odd public UDP port revealed in a Router Advertisement -- this could cause confusion with multicast traffic\n");
}
size_t rdofs = 12;
//TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
while (rdofs + 4 < ntohs (v4v6plen)) {
if (v4v6icmpdata [rdofs + 1] == 0) {
return; /* zero length option */
}
#ifdef TODO_DEPRACATED
if ((v4v6icmpdata [rdofs + 0] == ND_OPT_DESTINATION_LINKADDR) && (v4v6icmpdata [rdofs + 1] == 1)) {
if (v4v6icmpdata [rdofs + 2] & 0x01) {
syslog (LOG_WARNING, "TODO: Ignoring an odd UDP port offered in a Router Advertisement over 6bed4\n");
}
syslog (LOG_INFO, "TODO: Set tunnel link-local address to %02x:%02x:%02x:%02x:%02x:%02x\n", v4v6icmpdata [rdofs + 2], v4v6icmpdata [rdofs + 3], v4v6icmpdata [rdofs + 4], v4v6icmpdata [rdofs + 5], v4v6icmpdata [rdofs + 6], v4v6icmpdata [rdofs + 7]);
destlladdr = &v4v6icmpdata [rdofs + 2];
/* continue with next option */
} else
#endif
if (v4v6icmpdata [rdofs + 0] != ND_OPT_PREFIX_INFORMATION) {
/* skip to next option */
} else if (v4v6icmpdata [rdofs + 1] != 4) {
return; /* bad length field */
} else if (rdofs + (v4v6icmpdata [rdofs + 1] << 3) > ntohs (v4v6plen) + 4) {
return; /* out of packet length */
} else if (v4v6icmpdata [rdofs + 3] & 0xc0 != 0xc0) {
/* no on-link autoconfig prefix */
} else if (v4v6icmpdata [rdofs + 2] != 64) {
return;
} else {
destprefix = &v4v6icmpdata [rdofs + 16];
}
rdofs += (v4v6icmpdata [rdofs + 1] << 3);
}
#ifdef TODO_DEPRECATED
if (destprefix && destlladdr) {
memcpy (v6lladdr, destlladdr, 6);
memcpy (&v6listen.s6_addr [0], destprefix, 8);
v6listen.s6_addr [8] = destlladdr [0] ^ 0x02;
v6listen.s6_addr [9] = destlladdr [1];
v6listen.s6_addr [10] = destlladdr [2];
v6listen.s6_addr [11] = 0xff;
v6listen.s6_addr [12] = 0xfe;
v6listen.s6_addr [13] = destlladdr [3];
v6listen.s6_addr [14] = destlladdr [4];
v6listen.s6_addr [15] = destlladdr [5];
inet_ntop (AF_INET6,
&v6listen,
v6prefix,
sizeof (v6prefix));
syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
if (!setup_tunnel_address ()) {
syslog (LOG_CRIT, "Failed to setup tunnel address\n");
exit (1);
}
got_lladdr = true;
maintenance_time_cycle = maintenance_time_cycle_max;
maintenance_time_sec = time (NULL) + maintenance_time_cycle;
}
#else
if (destprefix) {
memcpy (v6listen.s6_addr + 0, destprefix, 8);
memcpy (v6listen.s6_addr + 8, v4dst6->s6_addr + 8, 8);
memcpy (v6listen_linklocal_complete, v4dst6, 16);
v6lladdr [0] = v6listen_linklocal_complete [8] ^ 0x02;
v6lladdr [1] = v6listen_linklocal_complete [9];
v6lladdr [2] = v6listen_linklocal_complete [10];
v6lladdr [3] = v6listen_linklocal_complete [13];
v6lladdr [4] = v6listen_linklocal_complete [14];
v6lladdr [5] = v6listen_linklocal_complete [15];
inet_ntop (AF_INET6,
&v6listen,
v6prefix,
sizeof (v6prefix));
syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
setup_tunnel_address (); //TODO// parameters?
got_lladdr = true;
maintenance_time_cycle = maintenance_time_cycle_max;
maintenance_time_sec = time (NULL) + maintenance_time_cycle;
}
#endif
return;
case ND_NEIGHBOR_SOLICIT:
//
// Validate Neigbour Solicitation (trivial)
//
// Replicate the message over the IPv6 Link (like plain IPv6)
if (v4ngbcmdlen < 24) {
return; /* too short, drop */
}
syslog (LOG_DEBUG, "%s: Replicating Neighbor Solicatation from 6bed4 to the IPv6 Link\n", program);
char buf [INET6_ADDRSTRLEN]; uint8_t ll [6]; if ((memcmp (v4src6, v6listen_linklocal, 8) != 0) && (memcmp (v4src6, &v6listen, 8) != 0)) { inet_ntop (AF_INET6, v4src6, buf, sizeof (buf)); syslog (LOG_DEBUG, "Source IPv6 address %s from wrong origin\n"); } else { uint8_t pfaddr [16]; memcpy (pfaddr, v6listen.s6_addr, 8); memcpy (pfaddr + 8, v4src6->s6_addr + 8, 8); inet_ntop (AF_INET6, pfaddr, buf, sizeof (buf)); if (lookup_neighbor (pfaddr, ll)) { syslog (LOG_DEBUG, "Source IPv6 %s has Link-Local Address %02x:%02x:%02x:%02x:%02x:%02x with metric %d\n", buf, ll [0], ll [1], ll [2], ll [3], ll [4], ll [5], lladdr_metric (ll)); } else { syslog (LOG_DEBUG, "Source IPv6 %s is unknown to me\n", buf); } }
uint8_t optofs = 4 + 16;
#if 0
uint8_t *srcll = NULL; /* TODO -- use 6bed4 Network sender instead! */
while ((40 + 4 + optofs + 2 < v4ngbcmdlen) && (40 + 4 + optofs + 8 * v4v6icmpdata [optofs + 1] <= v4ngbcmdlen)) {
if (v4v6icmpdata [optofs] == 1) {
srcll = v4v6icmpdata + optofs + 2;
}
optofs += 8 * v4v6icmpdata [optofs + 1];
}
if (srcll) { syslog (LOG_DEBUG, "ND-contained Source Link-Layer Address %02x:%02x:%02x:%02x:%02x:%02x has metric %d\n", srcll [0], srcll [1], srcll [2], srcll [3], srcll [4], srcll [5], lladdr_metric (srcll)); }
#endif
//
// We should attach a Source Link-Layer Address, but
// we cannot automatically trust the one provided remotely.
// Also, we want to detect if routes differ, and handle it.
//
// 0. if no entry in the ngb.cache
// then use 6bed4 server in ND, initiate ngb.sol to src.ll
// impl: use 6bed4-server lladdr, set highest metric
// 1. if metric (ngb.cache) < metric (src.ll)
// then retain ngb.cache, send Redirect to source
// 2. if metric (ngb.cache) > metric (src.ll)
// then retain ngb.cache, initiate ngb.sol to src.ll
// 3. if metric (ngb.cache) == metric (src.ll)
// then retain ngb.cache
//
uint8_t src_lladdr [6];
src_lladdr [0] = ntohs (v4name.sin_port) & 0x00ff;
src_lladdr [1] = ntohs (v4name.sin_port) >> 8;
memcpy (src_lladdr + 2, &v4name.sin_addr, 4);
metric_t src_metric = lladdr_metric (src_lladdr);
v4v6icmpdata [4+16+0] = 1; /* Option: Source LLaddr */
v4v6icmpdata [4+16+1] = 1; /* Length: 1x 8 bytes */
uint8_t *ngbc_lladdr = v4v6icmpdata + 4+16+2;
uint8_t ngbc_ipv6 [16];
if (memcmp (v4src6, v6listen_linklocal, 8)) {
memcpy (ngbc_ipv6 + 0, &v6listen, 8);
memcpy (ngbc_ipv6 + 8, v4src6 + 8, 8);
} else {
memcpy (ngbc_ipv6, v4src6, 16);
}
bool ngbc_cached = lookup_neighbor (ngbc_ipv6, ngbc_lladdr);
metric_t ngbc_metric;
if (ngbc_cached) {
ngbc_metric = lladdr_metric (ngbc_lladdr);
} else {
ngbc_metric = METRIC_HIGH; /* trigger local ngbsol */
memcpy (ngbc_lladdr, SERVER_6BED4_PORT_IPV4_MACSTR, 6);
syslog (LOG_DEBUG, "Failed to find neighbor in cache, initialising it with the high metric\n");
}
syslog (LOG_DEBUG, "Metric analysis: source lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d, neighbor cache lladdr %02x:%02x:%02x:%02x:%02x:%02x metric %d\n", src_lladdr [0], src_lladdr [1], src_lladdr [2], src_lladdr [3], src_lladdr [4], src_lladdr [5], src_metric, ngbc_lladdr [0], ngbc_lladdr [1], ngbc_lladdr [2], ngbc_lladdr [3], ngbc_lladdr [4], ngbc_lladdr [5], ngbc_metric);
//
// Replicate the ngb.sol with the selected ngbc-lladdr
v4v6icmpcksum = icmp6_checksum ((uint8_t *) v4hdr6, 8 + 16 + 8);
handle_4to6_plain (40 + 24 + 8, &v4name);
//
// If needed, initiate Neigbor Solicitation to the source