-
Notifications
You must be signed in to change notification settings - Fork 5
/
chether.c
1516 lines (1404 loc) · 52 KB
/
chether.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
/* Copyright © 2005, 2017-2021 Björn Victor ([email protected]) */
/* Bridge program for various Chaosnet implementations. */
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "cbridge.h"
#if __linux__
// for ether_ntoa
#include <netinet/ether.h>
#endif
// TODO
// rewrite using pcap (replace BPF)
/* **** Chaos-over-Ethernet functions **** */
#if CHAOS_ETHERP
// see below
#define FAKE_CHAOS_ETHER_TRAILER 1
// Look for, and use, a Chaos trailer in Ethernet pkts - don't do this
#ifndef USE_CHAOS_ETHER_TRAILER
#define USE_CHAOS_ETHER_TRAILER 0
#endif
// Slightly less efficent, but useful information:
// - add a fake "hardware trailer" based on sender MAC and ARP info, only used internally (not on the network)
#ifndef FAKE_CHAOS_ETHER_TRAILER
#define FAKE_CHAOS_ETHER_TRAILER USE_CHAOS_ETHER_TRAILER
#endif
#endif
// ARP stuff
#ifndef ETHERTYPE_CHAOS
# define ETHERTYPE_CHAOS 0x0804
#endif
#ifndef ARPHRD_CHAOS /* this is the original Chaosnet hardware, not the ethernet protocol type */
#define ARPHRD_CHAOS 5
#endif
// old names for new, new names for old?
#ifndef ARPOP_RREQUEST
#define ARPOP_RREQUEST ARPOP_REVREQUEST // 3 /* request protocol address given hardware */
#endif
#ifndef ARPOP_RREPLY
#define ARPOP_RREPLY ARPOP_REVREPLY // 4 /* response giving protocol address */
#endif
/* Chaos ARP list */
#define CHARP_MAX 16
#define CHARP_MAX_AGE (60*5) // ARP cache limit
struct charp_ent {
u_short charp_chaddr;
u_char charp_eaddr[ETHER_ADDR_LEN];
time_t charp_age;
};
#define CHETHDEST_MAX 8
struct chethdest {
u_short cheth_addr; /* chaos addr or (more likely) subnet */
u_short cheth_myaddr; /* my chaos address on this interface */
char cheth_ifname[IFNAMSIZ]; /* interface name */
u_char cheth_ea[ETHER_ADDR_LEN]; /* ether address */
int cheth_chfd; /* Chaos pkt fd */
int cheth_arpfd; /* ARP pkt fd */
int cheth_ifix; /* interface index */
};
static u_char eth_brd[ETHER_ADDR_LEN] = {255,255,255,255,255,255};
static int chether_debug = 0;
// Ether interface table
static int nchethdest = 0;
static struct chethdest chethdest[CHETHDEST_MAX];
static pthread_mutex_t charp_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t cheth_lock = PTHREAD_MUTEX_INITIALIZER;
// Chaos ARP table
// @@@@ avoid ARP flooding, back off
static struct charp_ent charp_list[CHARP_MAX];
static int charp_len = 0; /* cf CHARP_MAX */
static void get_my_ea(void);
#if FAKE_CHAOS_ETHER_TRAILER
static u_short find_arp_entry_for_ea(u_char *eaddr);
#endif
void print_arp_table();
void
print_config_ether()
{
int i, j;
if (nchethdest == 0) {
printf("Not using Ethernet\n");
return;
} else
printf("Configured %d ether links:\n", nchethdest);
if (chethdest[0].cheth_ea[0] == 0 && chethdest[0].cheth_ea[1] == 0) {
// Find it if needed
get_my_ea();
}
for (i = 0; i < nchethdest; i++) {
printf("Using Ethernet interface %s",
chethdest[i].cheth_ifname);
if (chether_debug)
printf(" (interface index %d)", chethdest[i].cheth_ifix);
printf(", ether address ");
for (j = 0; j < ETHER_ADDR_LEN-1; j++)
printf("%02X:",chethdest[i].cheth_ea[j]);
printf("%02X\n",chethdest[i].cheth_ea[j]);
printf(" for %s %#o, my Chaos address %#o",
(chethdest[i].cheth_addr & 0xff) == 0 ? "subnet" : "host",
(chethdest[i].cheth_addr & 0xff) == 0 ? chethdest[i].cheth_addr >> 8 : chethdest[i].cheth_addr,
chethdest[i].cheth_myaddr);
if (chether_debug)
printf("\n ARP fd %d, Chaos fd %d", chethdest[i].cheth_arpfd, chethdest[i].cheth_chfd);
printf("\n");
}
}
int
parse_ether_link_config()
{
char *tok = NULL;
PTLOCKN(cheth_lock,"cheth_lock");
// clear the entry we're creating
memset(&chethdest[nchethdest], 0, sizeof(struct chethdest));
tok = strtok(NULL, " \t\r\n");
if (tok != NULL) {
// if (chether_debug) fprintf(stderr,"ether link interface %d: %s\n", nchethdest, tok);
strncpy(chethdest[nchethdest].cheth_ifname, tok, IFNAMSIZ);
} else {
fprintf(stderr,"ether error: no interface name given\n");
PTUNLOCKN(cheth_lock,"cheth_lock");
return -1;
}
nchethdest++;
PTUNLOCKN(cheth_lock,"cheth_lock");
return 0;
}
int
postparse_ether_link_config(struct chroute *rt)
{
PTLOCKN(cheth_lock,"cheth_lock");
struct chethdest *cd = &chethdest[nchethdest-1];
cd->cheth_myaddr = rt->rt_myaddr;
cd->cheth_addr = rt->rt_dest;
PTUNLOCKN(cheth_lock,"cheth_lock");
if (cd->cheth_addr == 0) {
fprintf(stderr,"Config error: link ether ifname %s: no addr given\n", cd->cheth_ifname);
return -1;
} else if ((cd->cheth_addr & 0xff) != 0) {
// host address given
fprintf(stderr,"Config error: Ether links must be to subnets, not hosts.\n"
"Change\n"
" link ether %s host %o ...\n"
"to\n"
" link ether %s subnet %o ...\n",
cd->cheth_ifname, cd->cheth_addr, cd->cheth_ifname, cd->cheth_addr>>8);
return -1;
}
if (cd->cheth_myaddr == 0) {
int i;
extern int nchaddr;
for (i = 0; i < nchaddr; i++)
if ((mychaddr[i] & 0xff00) == cd->cheth_addr) {
cd->cheth_myaddr = mychaddr[i];
if (chether_debug) fprintf(stderr,"defaulting myaddr for ether %s to %#o\n", cd->cheth_ifname, mychaddr[i]);
break;
}
if (cd->cheth_myaddr == 0) {
fprintf(stderr,"Config error: link ether %s: no myaddr given\n", cd->cheth_ifname);
return -1;
}
} else if ((cd->cheth_myaddr & 0xff00) != cd->cheth_addr) {
fprintf(stderr,"Config error: link ether %s: myaddr %o is on subnet %o, which doesn't match subnet %o specified\n",
cd->cheth_ifname, cd->cheth_myaddr, cd->cheth_myaddr>>8, cd->cheth_addr>>8);
return -1;
}
add_mychaddr(cd->cheth_myaddr);
return 0;
}
int
parse_ether_config_line()
{
char *tok = NULL;
while ((tok = strtok(NULL," \t\r\n")) != NULL) {
if (strcasecmp(tok,"debug") == 0) {
tok = strtok(NULL, " \t\r\n");
if ((tok == NULL) || (strcasecmp(tok,"on") == 0) || (strcasecmp(tok,"yes") == 0))
chether_debug = 1;
else if ((strcasecmp(tok,"off") == 0) || (strcasecmp(tok,"no") == 0))
chether_debug = 0;
else {
fprintf(stderr,"ether: bad 'debug' arg %s specified\n", tok);
return -1;
}
} else {
fprintf(stderr,"ether config keyword %s unknown (note: interface names now go in \"link\" config)\n", tok);
return -1;
}
}
return 0;
}
// Find the ethernet address of the configured interfaces
static void get_my_ea() {
struct ifaddrs *ifx, *ifs = NULL;
if (getifaddrs(&ifs) < 0) {
perror("getifaddrs");
return;
}
#if ETHER_BPF // really "if on a mac"?
struct sockaddr_dl *sdl = NULL;
#else
struct sockaddr_ll *sll = NULL;
#endif
int i, ngot = 0;
// int ifn = 0;
PTLOCKN(cheth_lock,"cheth_lock");
for (ifx = ifs; ifx != NULL; ifx = ifx->ifa_next) {
// ifn++;
for (i = 0; i < nchethdest; i++) {
if (strcmp(ifx->ifa_name, chethdest[i].cheth_ifname) == 0) {
if (ifx->ifa_flags & IFF_UP) {
if (ifx->ifa_addr != NULL) {
// if (chether_debug) fprintf(stderr,"got address of interface %s (dest %d, if %d)\n", chethdest[i].cheth_ifname, i, ifn);
ngot++;
#if ETHER_BPF
sdl = (struct sockaddr_dl *)ifx->ifa_addr;
if ((sdl->sdl_alen > 0) && (sdl->sdl_alen == ETHER_ADDR_LEN))
memcpy(&chethdest[i].cheth_ea, LLADDR(sdl), sdl->sdl_alen);
else if (chether_debug)
fprintf(stderr,"DL address len for %s is %d, not copying\n", ifx->ifa_name, sdl->sdl_alen);
#else
sll = (struct sockaddr_ll *)ifx->ifa_addr;
if ((sll->sll_halen > 0) && (sll->sll_halen == ETHER_ADDR_LEN))
memcpy(&chethdest[i].cheth_ea, sll->sll_addr, sll->sll_halen);
else if (chether_debug)
fprintf(stderr,"LL address len for %s is %d, not copying\n", ifx->ifa_name, sll->sll_halen);
#endif
} else {
fprintf(stderr,"ether interface %s has no address\n", ifx->ifa_name);
}
} else if (chether_debug) {
fprintf(stderr,"ether interface %s is not up\n", ifx->ifa_name);
}
}
}
}
PTUNLOCKN(cheth_lock,"cheth_lock");
freeifaddrs(ifs);
if (ngot < nchethdest)
fprintf(stderr,"Failed to get ether addresses for all interfaces! Got %d out of %d\n",
ngot, nchethdest);
}
#if ETHER_BPF
// #define BPF_MTU (BPF_WORDALIGN(CH_PK_MAXLEN) + BPF_WORDALIGN(sizeof(struct bpf_hdr)) + BPF_WORDALIGN(sizeof(struct ether_header)))
#define BPF_MTU (BPF_WORDALIGN(1514) + BPF_WORDALIGN(sizeof(struct bpf_hdr)))
// based on dpimp.c in klh10 by Ken Harrenstein
/* Packet byte offsets for interesting fields (in network order) */
#define PKBOFF_EDEST 0 /* 1st shortword of Ethernet destination */
#define PKBOFF_ESRC 6 /* 1st shortword of Ethernet source */
#define PKBOFF_ETYPE 12 /* Shortwd offset to Ethernet packet type */
#define PKBOFF_ARP_PTYPE (sizeof(struct ether_header)+sizeof(u_short)) /* ARP protocol type */
/* BPF simple Loads */
#define BPFI_LD(a) bpf_stmt(BPF_LD+BPF_W+BPF_ABS,(a)) /* Load word P[a:4] */
#define BPFI_LDH(a) bpf_stmt(BPF_LD+BPF_H+BPF_ABS,(a)) /* Load short P[a:2] */
#define BPFI_LDB(a) bpf_stmt(BPF_LD+BPF_B+BPF_ABS,(a)) /* Load byte P[a:1] */
/* BPF Jumps and skips */
#define BPFI_J(op,k,t,f) bpf_jump(BPF_JMP+(op)+BPF_K,(k),(t),(f))
#define BPFI_JEQ(k,n) BPFI_J(BPF_JEQ,(k),(n),0) /* Jump if A == K */
#define BPFI_JNE(k,n) BPFI_J(BPF_JEQ,(k),0,(n)) /* Jump if A != K */
#define BPFI_JGT(k,n) BPFI_J(BPF_JGT,(k),(n),0) /* Jump if A > K */
#define BPFI_JLE(k,n) BPFI_J(BPF_JGT,(k),0,(n)) /* Jump if A <= K */
#define BPFI_JGE(k,n) BPFI_J(BPF_JGE,(k),(n),0) /* Jump if A >= K */
#define BPFI_JLT(k,n) BPFI_J(BPF_JGE,(k),0,(n)) /* Jump if A < K */
#define BPFI_JDO(k,n) BPFI_J(BPF_JSET,(k),(n),0) /* Jump if A & K */
#define BPFI_JDZ(k,n) BPFI_J(BPF_JSET,(k),0,(n)) /* Jump if !(A & K) */
#define BPFI_CAME(k) BPFI_JEQ((k),1) /* Skip if A == K */
#define BPFI_CAMN(k) BPFI_JNE((k),1) /* Skip if A != K */
#define BPFI_CAMG(k) BPFI_JGT((k),1) /* Skip if A > K */
#define BPFI_CAMLE(k) BPFI_JLE((k),1) /* Skip if A <= K */
#define BPFI_CAMGE(k) BPFI_JGE((k),1) /* Skip if A >= K */
#define BPFI_CAML(k) BPFI_JLT((k),1) /* Skip if A < K */
#define BPFI_TDNN(k) BPFI_JDO((k),1) /* Skip if A & K */
#define BPFI_TDNE(k) BPFI_JDZ((k),1) /* Skip if !(A & K) */
/* BPF Returns */
#define BPFI_RET(n) bpf_stmt(BPF_RET+BPF_K, (n)) /* Return N bytes */
#define BPFI_RETFAIL() BPFI_RET(0) /* Failure return */
#define BPFI_RETWIN() BPFI_RET((u_int)-1) /* Success return */
// My addition
#define BPFI_SKIP(n) BPFI_J(BPF_JA,0,(n),(n)) /* skip n instructions */
struct bpf_insn bpf_stmt(unsigned short code, bpf_u_int32 k)
{
struct bpf_insn ret;
ret.code = code;
ret.jt = 0;
ret.jf = 0;
ret.k = k;
return ret;
}
struct bpf_insn bpf_jump(unsigned short code, bpf_u_int32 k,
unsigned char jt, unsigned char jf)
{
struct bpf_insn ret;
ret.code = code;
ret.jt = jt;
ret.jf = jf;
ret.k = k;
return ret;
}
#endif // ETHER_BPF
/* Get a PACKET/DGRAM socket for the specified ethernet type, on the specified interface */
static int
get_packet_socket(u_short ethtype, struct chethdest *cd)
{
int fd = -1;
#if ETHER_BPF
struct ifreq ifr;
char bpfname[64];
int x;
for (x = 0; x < 16; x++) {
sprintf(bpfname, "/dev/bpf%d", x);
if ((fd = open(bpfname, O_RDWR)) < 0) {
if (errno == EBUSY) {
/* if (debug) perror(bpfname); */
continue;
} else {
perror(bpfname);
return -1;
}
} else
break;
}
if (fd < 0) {
perror("Failed to open BPF device");
fprintf(stderr,"Last tried %s\n", bpfname);
return -1;
} else
if (chether_debug || debug) fprintf(stderr,"Opened BPF device %s successfully, fd %d\n",
bpfname, fd);
// set max packet length (shorter for Chaos?). Must be set before interface is attached!
x = BPF_MTU;
if (ioctl(fd, BIOCSBLEN, (void *)&x) < 0) {
perror("ioctl(BIOCSBLEN)");
close(fd);
return -1;
}
// Become nonblocking
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
flags = 0;
if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
perror("fcntl(F_SETFL)");
close(fd);
return -1;
}
#if 1
// unset header-complete mode (we read and write complete pkts)
// but we still need to create the header
x = 0;
if (ioctl(fd, BIOCSHDRCMPLT, (void *)&x) < 0) {
perror("ioctl(BIOCSHDRCMPLT)");
close(fd);
return -1;
}
#endif
// Do echo my sent pkts back to me, please - this lets other BPF processes see them
#ifdef BIOCSSEESENT
x = 1;
if (ioctl(fd, BIOCSSEESENT, (void *)&x) < 0) {
perror("ioctl(BIOCSSEESENT)");
close(fd);
return -1;
}
#endif
// Operate in Immediate Mode: process pkts as they arrive rather than wait for timeout or buffer full
x = 1;
if (ioctl(fd, BIOCIMMEDIATE, (void *)&x) < 0) {
perror("ioctl(BIOCIMMEDIATE)");
close(fd);
return -1;
}
#if 0
// let Promiscuous mode be, we filter for it
x = 0;
if (ioctl(fd, BIOCPROMISC, (void *)&x) < 0) {
perror("ioctl(BIOCPROMISC)");
close(fd);
return -1;
}
#endif
// Now build the filter
struct bpf_version bv;
if (ioctl(fd, BIOCVERSION, (char *)&bv) < 0) {
perror("ioctl(BIOCVERSION)");
close(fd);
return -1;
} else if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
fprintf(stderr, "requires BPF language %d.%d or higher; kernel is %d.%d",
BPF_MAJOR_VERSION, BPF_MINOR_VERSION, bv.bv_major, bv.bv_minor);
close(fd);
return -1;
}
// Here is the BPF program, simple
#define BPF_PFMAX 50
struct bpf_insn pftab[BPF_PFMAX], *p;
struct bpf_program pfilter = {0, pftab}, *pfp;
// must also check for address since if may be promisc although we didn't ask for it
// tcpdump -i en0 -d 'ether proto 0x0804 && (ether dst 3c:07:54:14:c9:24 || ether dst ff:ff:ff:ff:ff:ff)'
// (000) ldh [12]
// (001) jeq #0x804 jt 2 jf 10
// (002) ld [2]
// (003) jeq #0x5414c924 jt 4 jf 6
// (004) ldh [0]
// (005) jeq #0x3c07 jt 9 jf 10
// (006) jeq #0xffffffff jt 7 jf 10
// (007) ldh [0]
// (008) jeq #0xffff jt 9 jf 10
// (009) ret #262144
// (010) ret #0
u_short ea1 = ((cd->cheth_ea[0])<<8)|(cd->cheth_ea[1]);
u_long ea2 = ((((cd->cheth_ea[2])<<8)|(cd->cheth_ea[3]))<<8|(cd->cheth_ea[4]))<<8 | (cd->cheth_ea[5]);
pfp = &pfilter;
p = pfp->bf_insns; /* 1st instruction of BPF program */
// Check the ethernet type field
*p++ = BPFI_LDH(PKBOFF_ETYPE); /* Load ethernet type field */
*p++ = BPFI_CAME(ethtype); /* Skip if right type */
*p++ = BPFI_RETFAIL(); /* nope, fail */
if (ethtype == ETHERTYPE_ARP) {
// For ARP, check the protocol type
*p++ = BPFI_LDH(PKBOFF_ARP_PTYPE); /* Check the ARP type */
*p++ = BPFI_CAME(ETHERTYPE_CHAOS);
*p++ = BPFI_RETFAIL(); /* Not Chaos, ignore */
// check for pkt sent from myself
*p++ = BPFI_LD(PKBOFF_ESRC+2); /* last word of Ether source */
*p++ = BPFI_CAME(ea2);
*p++ = BPFI_RETWIN(); /* no match, handle it */
*p++ = BPFI_LDH(PKBOFF_ESRC); /* get first part of source addr */
*p++ = BPFI_CAMN(ea1);
*p++ = BPFI_RETFAIL(); /* match both, skip pkt sent from myself */
// Never mind about destination here, if we get other ARP info that's nice?
}
else {
// For Ethernet pkts, also filter for our own address or broadcast,
// in case someone else makes the interface promiscuous
*p++ = BPFI_LD(PKBOFF_EDEST+2); /* last word of Ether dest */
*p++ = BPFI_CAME(ea2);
*p++ = BPFI_SKIP(3); /* no match, skip forward and check for broadcast */
*p++ = BPFI_LDH(PKBOFF_EDEST); /* get first part of dest addr */
*p++ = BPFI_CAMN(ea1);
*p++ = BPFI_RETWIN(); /* match both, win! */
*p++ = BPFI_LD(PKBOFF_EDEST+2); /* 1st word of Ether dest again */
*p++ = BPFI_CAME(0xffffffff); /* last hword is broadcast? */
*p++ = BPFI_RETFAIL();
*p++ = BPFI_LDH(PKBOFF_EDEST); /* get first part of dest addr */
*p++ = BPFI_CAME(0xffff);
*p++ = BPFI_RETFAIL(); /* nope */
// check for pkt sent from myself
*p++ = BPFI_LD(PKBOFF_ESRC+2); /* last work of Ether source */
*p++ = BPFI_CAME(ea2);
*p++ = BPFI_RETWIN(); /* no match, handle it */
*p++ = BPFI_LDH(PKBOFF_ESRC); /* get first part of source addr */
*p++ = BPFI_CAMN(ea1);
*p++ = BPFI_RETFAIL(); /* match both, skip pkt sent from myself */
}
*p++ = BPFI_RETWIN(); /* win */
pfp->bf_len = p - pfp->bf_insns; /* length of program */
if (pfp->bf_len > BPF_PFMAX) {
fprintf(stderr,"BPF: filter program too long, increase BPF_PFMAX!\n");
exit(1);
}
if (ioctl(fd, BIOCSETF, (char *)pfp) < 0) {
perror("ioctl(BIOCSETF)");
close(fd);
return -1;
#if 0 // debug
} else if (chether_debug || debug) {
fprintf(stderr,"BPF filter len %d:\n", pfp->bf_len);
for (x = 0; x < pfp->bf_len; x++)
fprintf(stderr," %d: 0x%04X %2d %2d 0x%0X\n",
x,
pfp->bf_insns[x].code,
pfp->bf_insns[x].jt,
pfp->bf_insns[x].jf,
pfp->bf_insns[x].k);
#endif // 0
}
// Attach to interface
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, cd->cheth_ifname, IFNAMSIZ);
if (ioctl(fd, BIOCSETIF, (void *)&ifr) < 0) {
perror("ioctl(BIOCSETIF)");
close(fd);
return -1;
}
if (cd->cheth_ea[0] == 0 && cd->cheth_ea[1] == 0) {
// Find it if needed
get_my_ea();
}
if (cd->cheth_ea[0] == 0 && cd->cheth_ea[1] == 0) {
fprintf(stderr,"Cannot find MAC addr of interface %s\n", cd->cheth_ifname);
close(fd);
return -1;
}
#if 0
// I don't get a signal? But I don't want one anyway.
if (verbose) {
if (ioctl(fd, BIOCGRSIG, (void *)&x) >= 0)
fprintf(stderr,"Signal on BPF reception: %d\n", x);
}
#endif
#else // not BPF, but direct sockaddr_ll
struct ifreq ifr;
struct sockaddr_ll sll;
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ethtype))) < 0) {
perror("socket(PF_PACKET, SOCK_DGRAM)");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, cd->cheth_ifname, strlen(cd->cheth_ifname));
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(SIOCGIFINDEX)");
return -1;
}
PTLOCKN(cheth_lock,"cheth_lock");
cd->cheth_ifix = ifr.ifr_ifindex;
PTUNLOCKN(cheth_lock,"cheth_lock");
#if 0
if ((chether_debug || debug))
printf("ifname %s ifindex %d\n", cd->cheth_ifname, cd->cheth_ifix);
#endif
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(ethtype);
sll.sll_ifindex = cd->cheth_ifix;
if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
perror("bind");
return -1;
}
// why not try this too
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, cd->cheth_ifname, strlen(cd->cheth_ifname)+1) == -1) {
perror("SO_BINDTODEVICE");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, cd->cheth_ifname, strlen(cd->cheth_ifname));
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0 ) {
perror("ioctl(SIOCGIFHWADDR)");
return -1;
}
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
fprintf(stderr,"wrong ARPHDR %d ", ifr.ifr_hwaddr.sa_family);
perror("ioctl");
return -1;
}
#if 0
if (cd->cheth_ea[0] == 0 && cd->cheth_ea[1] == 0)
memcpy(&cd->cheth_ea, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
#endif
#if 0
if ((chether_debug || debug)) {
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = cd->cheth_ifix;
if (ioctl(fd, SIOCGIFNAME, &ifr) < 0) {
perror("ioctl(SIOCGIFNAME)");
return -1;
}
printf("if index %d ifname %s\n", cd->cheth_ifix, ifr.ifr_name);
}
#endif
#endif // !ETHER_BPF
return fd;
}
/* Send a packet of the specified type to the specified address */
static void
send_packet(struct chethdest *cd, int if_fd, u_short ethtype, u_char *addr, u_char addrlen, u_char *packet, int packetlen)
{
int cc;
#if !ETHER_BPF
static struct sockaddr_ll sa;
memset (&sa, 0, sizeof sa);
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ethtype);
sa.sll_ifindex = cd->cheth_ifix;
sa.sll_hatype = ARPHRD_ETHER;
sa.sll_pkttype = PACKET_HOST;
sa.sll_halen = addrlen;
memcpy(&sa.sll_addr, addr, addrlen);
#endif // !ETHER_BPF
if (chether_debug || verbose) {
struct chaos_header *ch = (struct chaos_header *)packet;
if (ethtype == ETHERTYPE_CHAOS) {
printf("Ether: Sending %s to <%#o,%#x>: %d bytes with protocol 0x%04x (Chaos) to address %s\n",
ch_opcode_name(ch->ch_opcode_x >> 8), // not swapped
htons(ch_destaddr(ch)), htons(ch_destindex(ch)),
packetlen, ethtype,
ether_ntoa((struct ether_addr *)addr));
} else if (ethtype == ETHERTYPE_ARP) {
printf("Ether: Sending ARP: %d bytes with protocol 0x%04x (ARP) to address %s\n",
packetlen, ethtype,
ether_ntoa((struct ether_addr *)addr));
} else {
printf("Ether: sending %d bytes with protocol %#04x to address %s\n",
packetlen, ethtype, ether_ntoa((struct ether_addr *)addr));
}
if (debug && (ethtype == ETHERTYPE_CHAOS)) {
u_char pk[CH_PK_MAXLEN];
ntohs_buf((u_short *)packet, (u_short *)pk, packetlen);
ch_dumpkt(pk, packetlen);
htons_buf((u_short *)packet, (u_short *)pk, packetlen);
}
}
#if ETHER_BPF
// construct the header separately to avoid copying
struct iovec iov[2];
struct ether_header eh;
memcpy(&eh.ether_shost, &cd->cheth_ea, ETHER_ADDR_LEN);
memcpy(&eh.ether_dhost, addr, ETHER_ADDR_LEN);
eh.ether_type = htons(ethtype);
iov[0].iov_base = (char *) &eh;
iov[0].iov_len = ETHER_HDR_LEN;
iov[1].iov_base = packet;
iov[1].iov_len = packetlen;;
if (packetlen+sizeof(struct ether_header) > BPF_MTU) {
fprintf(stderr,"send_packet: buf len %lu > MTU %lu\n",
packetlen+sizeof(struct ether_header), BPF_MTU);
}
// @@@@ skip if_fd param
if (ethtype == ETHERTYPE_ARP) {
if (if_fd != cd->cheth_arpfd)
if (chether_debug) fprintf(stderr,"send_packet: bad if_fd %d given for ARP\n", if_fd);
if_fd = cd->cheth_arpfd;
} else if (ethtype == ETHERTYPE_CHAOS) {
if (if_fd != cd->cheth_chfd)
if (chether_debug) fprintf(stderr,"send_packet: bad if_fd %d given for Chaos\n", if_fd);
if_fd = cd->cheth_chfd;
} else
fprintf(stderr,"send_packet: Bad ether type %#x\n", ethtype);
cc = writev(if_fd, iov, sizeof(iov)/sizeof(*iov));
packetlen += sizeof(struct ether_header); /* avoid complaints below */
#else // not BPF
// @@@@ skip if_fd param
if (ethtype == ETHERTYPE_ARP) {
if (if_fd != cd->cheth_arpfd)
if (chether_debug) fprintf(stderr,"send_packet: bad if_fd %d given for ARP\n", if_fd);
if_fd = cd->cheth_arpfd;
} else if (ethtype == ETHERTYPE_CHAOS) {
if (if_fd != cd->cheth_chfd)
if (chether_debug) fprintf(stderr,"send_packet: bad if_fd %d given for Chaos\n", if_fd);
if_fd = cd->cheth_chfd;
} else
fprintf(stderr,"send_packet: Bad ether type %#x\n", ethtype);
cc = sendto(if_fd, packet, packetlen, 0, (struct sockaddr *)&sa, sizeof(sa));
// if (chether_debug) fprintf(stderr,"send_packet: sent %d bytes of type 0x%04x (packet len %d) on fd %d\n", cc, ethtype, packetlen, if_fd);
#endif // ETHER_BPF
if (cc == packetlen)
return;
else if (cc >= 0) {
if (chether_debug || debug) fprintf(stderr,"send_packet sent only %d bytes\n", cc);
} else if ((errno == EHOSTUNREACH) || (errno == ENETDOWN) || (errno == ENETUNREACH)) {
// Ignore these errors.
if (chether_debug || debug) perror("send_packet");
} else
{
perror("send_packet");
fprintf(stderr, "\n");
exit(1);
}
}
/* Read a packet from the socket. */
#if ETHER_BPF
unsigned int bpf_buf_offset = 0;
unsigned int bpf_buf_length = 0;
uint8_t ether_bpf_buf[BPF_MTU];
#endif // ETHER_BPF
static void
describe_arp_pkt(u_char *buf)
{
int i;
struct arphdr *arp = (struct arphdr *)buf;
printf("ARP message:\n");
printf(" HW format %d (%s)\n",ntohs(arp->ar_hrd),
(arp->ar_hrd == htons(ARPHRD_ETHER) ? "Ether" :
(arp->ar_hrd == htons(ARPHRD_CHAOS) ? "Chaos" : "?")));
printf(" Protocol format 0x%04x (%s)\n",ntohs(arp->ar_pro),
(arp->ar_pro == htons(ETHERTYPE_ARP) ? "ARP" :
(arp->ar_pro == htons(ETHERTYPE_CHAOS) ? "Chaos" :
(arp->ar_pro == htons(ETHERTYPE_IP) ? "IP" : "?"))));
printf(" HW addr len %d\n Proto addr len %d\n ARP command %d (%s)\n",
arp->ar_hln, arp->ar_pln, ntohs(arp->ar_op),
arp->ar_op == htons(ARPOP_REQUEST) ? "Request" :
(arp->ar_op == htons(ARPOP_REPLY) ? "Reply" :
(arp->ar_op == htons(ARPOP_RREQUEST) ? "Reverse request" :
(arp->ar_op == htons(ARPOP_RREPLY) ? "Reverse reply" : "?"))));
printf(" Src HW addr: ");
for (i = 0; i < arp->ar_hln; i++)
printf("%02X ", buf[sizeof(struct arphdr)+i]);
printf("\n Src Protocol addr: ");
if (arp->ar_pro == htons(ETHERTYPE_CHAOS))
printf("%#o ", ntohs(buf[sizeof(struct arphdr)+(arp->ar_hln)]<<8 |
buf[sizeof(struct arphdr)+(arp->ar_hln)+1]));
else
for (i = 0; i < arp->ar_pln; i++)
printf("%#x ", buf[sizeof(struct arphdr)+arp->ar_hln+i]);
printf("\n Dst HW addr: ");
for (i = 0; i < arp->ar_hln; i++)
printf("%02X ", buf[sizeof(struct arphdr)+arp->ar_hln+arp->ar_pln+i]);
printf("\n Dst Protocol addr: ");
if (arp->ar_pro == htons(ETHERTYPE_CHAOS))
printf("%#o ", ntohs(buf[sizeof(struct arphdr)+(2 * (arp->ar_hln))+arp->ar_pln]<<8 |
buf[sizeof(struct arphdr)+(2 * (arp->ar_hln))+arp->ar_pln+1]));
else
for (i = 0; i < arp->ar_pln; i++)
printf("%#x ", buf[sizeof(struct arphdr)+arp->ar_hln+arp->ar_hln+arp->ar_pln+i]);
printf("\n");
}
static int
get_packet(struct chethdest *cd, int if_fd, u_char *buf, int buflen)
{
int i, rlen;
u_short protocol;
u_char src_mac[ETHER_ADDR_LEN];
#if ETHER_BPF
// Based on 3com.c in LambdaDelta by Daniel Seagraves & Barry Silverman
// see https://github.com/dseagrav/ld
int res;
struct bpf_hdr *bpf_header;
// #### gross hack, don't want to mess with the actual headers
if (if_fd == cd->cheth_arpfd)
protocol = ntohs(ETHERTYPE_ARP);
else if (if_fd == cd->cheth_chfd)
protocol = ntohs(ETHERTYPE_CHAOS);
else {
fprintf(stderr,"get_packet: bad FD\n");
exit(1);
}
#if 0 //debug
if (chether_debug || debug) {
int avail;
if (ioctl(if_fd, FIONREAD, (void *)&avail) < 0) {
perror("ioctl(FIONREAD)");
return 0;
} else
fprintf(stderr,"about to read from fd %d: available %d bytes\n", if_fd, avail);
}
#endif
if (bpf_buf_offset == 0) {
// BPF _requires_ you use the same buffer length you configured the filter for
if ((res = read(if_fd, ether_bpf_buf, sizeof(ether_bpf_buf))) < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read BPF ether");
#if 0 //debug
if (chether_debug || debug) {
fprintf(stderr,"failed reading fd %d (chfd %d, arpfd %d) buf %p buflen %d\n", if_fd, chfd, arpfd,
buf, buflen);
fprintf(stderr,"tried using buflen %d, configured for %lu\n",
buflen, BPF_MTU);
}
#endif
exit(1);
}
else if (chether_debug || debug) perror("read BPF ether");
return 0;
}
bpf_buf_length = res;
}
bpf_header = (struct bpf_hdr *)(ether_bpf_buf + bpf_buf_offset);
#if 0 //debug
if (chether_debug || debug) fprintf(stderr,"BPF: read %d bytes from fd (MTU %lu), timeval sec %d\n buflen %d, hdrlen %d, caplen %d, datalen %d, offset %d\n",
bpf_buf_length, BPF_MTU,
bpf_header->bh_tstamp.tv_sec,
buflen, bpf_header->bh_hdrlen, bpf_header->bh_caplen, bpf_header->bh_datalen,
bpf_buf_offset);
#endif
memcpy(buf, (uint8_t *)(ether_bpf_buf + bpf_buf_offset + bpf_header->bh_hdrlen)
+sizeof(struct ether_header), /* skip ether header! */
bpf_header->bh_caplen < buflen ? bpf_header->bh_caplen : buflen);
if (bpf_buf_offset + bpf_header->bh_hdrlen + bpf_header->bh_caplen < bpf_buf_length)
bpf_buf_offset += BPF_WORDALIGN(bpf_header->bh_hdrlen + bpf_header->bh_caplen);
else
bpf_buf_offset = 0;
if (bpf_header->bh_caplen != bpf_header->bh_datalen) {
if (chether_debug || debug) fprintf(stderr,"BPF: LENGTH MISMATCH: Captured %d of %d\n",
bpf_header->bh_caplen, bpf_header->bh_datalen);
return 0; /* throw away packet */
} else {
rlen = bpf_header->bh_caplen - sizeof(struct ether_header);
// if (chether_debug) fprintf(stderr,"BPF: read %d bytes\n", rlen);
}
struct ether_header *eh = (struct ether_header *)(ether_bpf_buf + bpf_buf_offset + bpf_header->bh_hdrlen);
if (nchethdest > 1) {
// check if this was sent by me on another interface
// BPF filter program checks for this interface only (lazy) - in most cases there is only one interface
for (i = 0; i < nchethdest; i++) {
if ((&chethdest[i] != cd) && (memcmp(eh->ether_shost, chethdest[i].cheth_ea, 6) == 0)) {
if (chether_debug)
fprintf(stderr,"%%%% Ether: dropping pkt sent from my ea on interface %d (%s)\n", i, chethdest[i].cheth_ifname);
return 0;
}
}
}
memcpy(src_mac, eh->ether_shost, ETHER_ADDR_LEN);
#if 0
for (i = 0; i < ETHER_ADDR_LEN-1; i++) {
src_mac |= eh->ether_shost[i];
src_mac = src_mac<<8;
}
src_mac |= eh->ether_shost[i];
#endif
#else // not BPF
struct sockaddr_ll sll;
socklen_t sllen = sizeof(sll);
rlen = recvfrom(if_fd, buf, buflen, 0, (struct sockaddr *) &sll, &sllen);
protocol = sll.sll_protocol;
#if 0
if (chether_debug) fprintf(stderr,"Ether: Received %d bytes from fd %d, protocol 0x%04x, halen %d\n",
rlen, if_fd, ntohs(protocol), sll.sll_halen);
#endif
#if 0
// won't happen for non-BPF?
// check if pkt sent from myself
if (sll.sll_halen == 6) {
for (i = 0; i < nchethdest; i++) {
if (memcmp(sll.sll_addr, chethdest[i].cheth_ea, 6) == 0) {
if (chether_debug)
fprintf(stderr,"Ether: dropping pkt sent from my ea on interface %d (%s)\n", i, chethdest[i].cheth_ifname);
return 0;
}
}
}
#endif
memcpy(src_mac, sll.sll_addr, ETHER_ADDR_LEN);
#if 0
for (i = 0; i < sll.sll_halen-1; i++) {
src_mac |= sll.sll_addr[i];
src_mac = src_mac<<8;
}
src_mac |= sll.sll_addr[i];
#endif
#endif // ETHER_BPF
if (rlen < 0)
{
if (errno != EAGAIN) {
perror ("get_packet: Read error");
exit(1);
}
return 0;
}
if (rlen == 0)
return 0;
if (chether_debug || verbose) {
#if !ETHER_BPF
#if 0
if (debug) {
printf("Received:\n");
printf(" Family %d\n",sll.sll_family);
printf(" Protocol %#x (%s)\n",ntohs(sll.sll_protocol),
(sll.sll_protocol == htons(ETHERTYPE_ARP) ? "ARP" :
(sll.sll_protocol == htons(ETHERTYPE_CHAOS) ? "Chaos" : "?")));
printf(" Index %d\n",sll.sll_ifindex);
printf(" Header type %d (%s)\n",sll.sll_hatype,
(sll.sll_hatype == ARPHRD_ETHER ? "Ether" :
(sll.sll_hatype == ARPHRD_CHAOS ? "Chaos" : "?")));
printf(" Pkt type %d (%s)\n",sll.sll_pkttype,
(sll.sll_pkttype == PACKET_HOST ? "Host" :
(sll.sll_pkttype == PACKET_BROADCAST ? "Broadcast" :
(sll.sll_pkttype == PACKET_MULTICAST ? "Multicast" :
(sll.sll_pkttype == PACKET_OTHERHOST ? "Other host" :
(sll.sll_pkttype == PACKET_OUTGOING ? "Outgoing" : "?"))))));
printf(" Addr len %d\n Addr ", sll.sll_halen);
} else
printf("Received %d bytes, protocol %#x, from ",
rlen, ntohs(sll.sll_protocol));
for (i = 0; i < sll.sll_halen; i++)
printf("%02X ", sll.sll_addr[i]);
printf("\n");
#endif
#endif // !ETHER_BPF
#if 0
if (debug && protocol == htons(ETHERTYPE_ARP)) {
struct arphdr *arp = (struct arphdr *)buf;
fprintf(stderr,"ARP message received, protocol 0x%04x (%s)\n",
ntohs(arp->ar_pro), (arp->ar_hrd == htons(ARPHRD_ETHER) ? "Ether" :
(arp->ar_hrd == htons(ARPHRD_CHAOS) ? "Chaos" : "?")));
}
#endif
if (protocol == htons(ETHERTYPE_ARP)
#if 1
&& (((struct arphdr *)buf)->ar_pro == htons(ETHERTYPE_CHAOS))
#endif
) {
struct arphdr *arp = (struct arphdr *)buf;
if (debug) {
describe_arp_pkt(buf);
} else if (stats || chether_debug || verbose) {
printf("ARP %s for protocol 0x%04x",
arp->ar_op == htons(ARPOP_REQUEST) ? "Request" :
(arp->ar_op == htons(ARPOP_REPLY) ? "Reply" :
(arp->ar_op == htons(ARPOP_RREQUEST) ? "Reverse request" :
(arp->ar_op == htons(ARPOP_RREPLY) ? "Reverse reply" : "?"))),
ntohs(arp->ar_pro));
printf(", dest addr ");
if (arp->ar_pln == 2)
printf("%#o ", ntohs(buf[sizeof(struct arphdr)+(2 * (arp->ar_hln))+arp->ar_pln]<<8 |
buf[sizeof(struct arphdr)+(2 * (arp->ar_hln))+arp->ar_pln+1]));
else
for (i = 0; i < arp->ar_pln; i++)
printf("%#x ", buf[sizeof(struct arphdr)+arp->ar_hln+arp->ar_hln+arp->ar_pln+i]);
printf("from src ");
for (i = 0; i < arp->ar_hln; i++)
printf("%02X ", buf[sizeof(struct arphdr)+i]);
printf("\n");
}
}
else if (protocol == htons(ETHERTYPE_CHAOS)) {
// Oh dear, this might slow things down a bit,
// but I really would like the LASTCN stats to know who sent it last
#if FAKE_CHAOS_ETHER_TRAILER
u_short trailer_from = find_arp_entry_for_ea((u_char *)&src_mac);
if ((trailer_from != 0)
// it fits
&& (rlen + CHAOS_HW_TRAILERSIZE < buflen)
// it's not already there
// - but size is sometimes larger than what was actually sent, so don't check this