39
39
#include <inttypes.h>
40
40
#include <linux/errqueue.h>
41
41
#include <linux/if_ether.h>
42
+ #include <linux/ipv6.h>
42
43
#include <linux/net_tstamp.h>
43
44
#include <netdb.h>
44
45
#include <net/if.h>
@@ -74,6 +75,7 @@ static bool cfg_do_pktinfo;
74
75
static bool cfg_loop_nodata ;
75
76
static bool cfg_no_delay ;
76
77
static bool cfg_use_cmsg ;
78
+ static bool cfg_use_pf_packet ;
77
79
static uint16_t dest_port = 9000 ;
78
80
79
81
static struct sockaddr_in daddr ;
@@ -195,7 +197,9 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
195
197
} else if ((cm -> cmsg_level == SOL_IP &&
196
198
cm -> cmsg_type == IP_RECVERR ) ||
197
199
(cm -> cmsg_level == SOL_IPV6 &&
198
- cm -> cmsg_type == IPV6_RECVERR )) {
200
+ cm -> cmsg_type == IPV6_RECVERR ) ||
201
+ (cm -> cmsg_level = SOL_PACKET &&
202
+ cm -> cmsg_type == PACKET_TX_TIMESTAMP )) {
199
203
serr = (void * ) CMSG_DATA (cm );
200
204
if (serr -> ee_errno != ENOMSG ||
201
205
serr -> ee_origin != SO_EE_ORIGIN_TIMESTAMPING ) {
@@ -270,34 +274,118 @@ static int recv_errmsg(int fd)
270
274
return ret == -1 ;
271
275
}
272
276
277
+ static uint16_t get_ip_csum (const uint16_t * start , int num_words ,
278
+ unsigned long sum )
279
+ {
280
+ int i ;
281
+
282
+ for (i = 0 ; i < num_words ; i ++ )
283
+ sum += start [i ];
284
+
285
+ while (sum >> 16 )
286
+ sum = (sum & 0xFFFF ) + (sum >> 16 );
287
+
288
+ return ~sum ;
289
+ }
290
+
291
+ static uint16_t get_udp_csum (const struct udphdr * udph , int alen )
292
+ {
293
+ unsigned long pseudo_sum , csum_len ;
294
+ const void * csum_start = udph ;
295
+
296
+ pseudo_sum = htons (IPPROTO_UDP );
297
+ pseudo_sum += udph -> len ;
298
+
299
+ /* checksum ip(v6) addresses + udp header + payload */
300
+ csum_start -= alen * 2 ;
301
+ csum_len = ntohs (udph -> len ) + alen * 2 ;
302
+
303
+ return get_ip_csum (csum_start , csum_len >> 1 , pseudo_sum );
304
+ }
305
+
306
+ static int fill_header_ipv4 (void * p )
307
+ {
308
+ struct iphdr * iph = p ;
309
+
310
+ memset (iph , 0 , sizeof (* iph ));
311
+
312
+ iph -> ihl = 5 ;
313
+ iph -> version = 4 ;
314
+ iph -> ttl = 2 ;
315
+ iph -> saddr = daddr .sin_addr .s_addr ; /* set for udp csum calc */
316
+ iph -> daddr = daddr .sin_addr .s_addr ;
317
+ iph -> protocol = IPPROTO_UDP ;
318
+
319
+ /* kernel writes saddr, csum, len */
320
+
321
+ return sizeof (* iph );
322
+ }
323
+
324
+ static int fill_header_ipv6 (void * p )
325
+ {
326
+ struct ipv6hdr * ip6h = p ;
327
+
328
+ memset (ip6h , 0 , sizeof (* ip6h ));
329
+
330
+ ip6h -> version = 6 ;
331
+ ip6h -> payload_len = htons (sizeof (struct udphdr ) + cfg_payload_len );
332
+ ip6h -> nexthdr = IPPROTO_UDP ;
333
+ ip6h -> hop_limit = 64 ;
334
+
335
+ ip6h -> saddr = daddr6 .sin6_addr ;
336
+ ip6h -> daddr = daddr6 .sin6_addr ;
337
+
338
+ /* kernel does not write saddr in case of ipv6 */
339
+
340
+ return sizeof (* ip6h );
341
+ }
342
+
343
+ static void fill_header_udp (void * p , bool is_ipv4 )
344
+ {
345
+ struct udphdr * udph = p ;
346
+
347
+ udph -> source = ntohs (dest_port + 1 ); /* spoof */
348
+ udph -> dest = ntohs (dest_port );
349
+ udph -> len = ntohs (sizeof (* udph ) + cfg_payload_len );
350
+ udph -> check = 0 ;
351
+
352
+ udph -> check = get_udp_csum (udph , is_ipv4 ? sizeof (struct in_addr ) :
353
+ sizeof (struct in6_addr ));
354
+ }
355
+
273
356
static void do_test (int family , unsigned int report_opt )
274
357
{
275
358
char control [CMSG_SPACE (sizeof (uint32_t ))];
359
+ struct sockaddr_ll laddr ;
276
360
unsigned int sock_opt ;
277
361
struct cmsghdr * cmsg ;
278
362
struct msghdr msg ;
279
363
struct iovec iov ;
280
364
char * buf ;
281
365
int fd , i , val = 1 , total_len ;
282
366
283
- if (family == AF_INET6 && cfg_proto != SOCK_STREAM ) {
284
- /* due to lack of checksum generation code */
285
- fprintf (stderr , "test: skipping datagram over IPv6\n" );
286
- return ;
287
- }
288
-
289
367
total_len = cfg_payload_len ;
290
- if (cfg_proto == SOCK_RAW ) {
368
+ if (cfg_use_pf_packet || cfg_proto == SOCK_RAW ) {
291
369
total_len += sizeof (struct udphdr );
292
- if (cfg_ipproto == IPPROTO_RAW )
293
- total_len += sizeof (struct iphdr );
370
+ if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW )
371
+ if (family == PF_INET )
372
+ total_len += sizeof (struct iphdr );
373
+ else
374
+ total_len += sizeof (struct ipv6hdr );
375
+
376
+ /* special case, only rawv6_sendmsg:
377
+ * pass proto in sin6_port if not connected
378
+ * also see ANK comment in net/ipv4/raw.c
379
+ */
380
+ daddr6 .sin6_port = htons (cfg_ipproto );
294
381
}
295
382
296
383
buf = malloc (total_len );
297
384
if (!buf )
298
385
error (1 , 0 , "malloc" );
299
386
300
- fd = socket (family , cfg_proto , cfg_ipproto );
387
+ fd = socket (cfg_use_pf_packet ? PF_PACKET : family ,
388
+ cfg_proto , cfg_ipproto );
301
389
if (fd < 0 )
302
390
error (1 , errno , "socket" );
303
391
@@ -346,29 +434,17 @@ static void do_test(int family, unsigned int report_opt)
346
434
memset (& ts_prev , 0 , sizeof (ts_prev ));
347
435
memset (buf , 'a' + i , total_len );
348
436
349
- if (cfg_proto == SOCK_RAW ) {
350
- struct udphdr * udph ;
437
+ if (cfg_use_pf_packet || cfg_proto == SOCK_RAW ) {
351
438
int off = 0 ;
352
439
353
- if (cfg_ipproto == IPPROTO_RAW ) {
354
- struct iphdr * iph = (void * ) buf ;
355
-
356
- memset (iph , 0 , sizeof (* iph ));
357
- iph -> ihl = 5 ;
358
- iph -> version = 4 ;
359
- iph -> ttl = 2 ;
360
- iph -> daddr = daddr .sin_addr .s_addr ;
361
- iph -> protocol = IPPROTO_UDP ;
362
- /* kernel writes saddr, csum, len */
363
-
364
- off = sizeof (* iph );
440
+ if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW ) {
441
+ if (family == PF_INET )
442
+ off = fill_header_ipv4 (buf );
443
+ else
444
+ off = fill_header_ipv6 (buf );
365
445
}
366
446
367
- udph = (void * ) buf + off ;
368
- udph -> source = ntohs (9000 ); /* random spoof */
369
- udph -> dest = ntohs (dest_port );
370
- udph -> len = ntohs (sizeof (* udph ) + cfg_payload_len );
371
- udph -> check = 0 ; /* not allowed for IPv6 */
447
+ fill_header_udp (buf + off , family == PF_INET );
372
448
}
373
449
374
450
print_timestamp_usr ();
@@ -377,7 +453,17 @@ static void do_test(int family, unsigned int report_opt)
377
453
iov .iov_len = total_len ;
378
454
379
455
if (cfg_proto != SOCK_STREAM ) {
380
- if (family == PF_INET ) {
456
+ if (cfg_use_pf_packet ) {
457
+ memset (& laddr , 0 , sizeof (laddr ));
458
+
459
+ laddr .sll_family = AF_PACKET ;
460
+ laddr .sll_ifindex = 1 ;
461
+ laddr .sll_protocol = htons (family == AF_INET ? ETH_P_IP : ETH_P_IPV6 );
462
+ laddr .sll_halen = ETH_ALEN ;
463
+
464
+ msg .msg_name = (void * )& laddr ;
465
+ msg .msg_namelen = sizeof (laddr );
466
+ } else if (family == PF_INET ) {
381
467
msg .msg_name = (void * )& daddr ;
382
468
msg .msg_namelen = sizeof (daddr );
383
469
} else {
@@ -437,9 +523,10 @@ static void __attribute__((noreturn)) usage(const char *filepath)
437
523
" -I: request PKTINFO\n"
438
524
" -l N: send N bytes at a time\n"
439
525
" -n: set no-payload option\n"
526
+ " -p N: connect to port N\n"
527
+ " -P: use PF_PACKET\n"
440
528
" -r: use raw\n"
441
529
" -R: use raw (IP_HDRINCL)\n"
442
- " -p N: connect to port N\n"
443
530
" -u: use udp\n"
444
531
" -x: show payload (up to 70 bytes)\n" ,
445
532
filepath );
@@ -451,7 +538,7 @@ static void parse_opt(int argc, char **argv)
451
538
int proto_count = 0 ;
452
539
int c ;
453
540
454
- while ((c = getopt (argc , argv , "46c:CDFhIl:np:rRux " )) != -1 ) {
541
+ while ((c = getopt (argc , argv , "46c:CDFhIl:np:PrRux " )) != -1 ) {
455
542
switch (c ) {
456
543
case '4' :
457
544
do_ipv6 = 0 ;
@@ -474,9 +561,21 @@ static void parse_opt(int argc, char **argv)
474
561
case 'I' :
475
562
cfg_do_pktinfo = true;
476
563
break ;
564
+ case 'l' :
565
+ cfg_payload_len = strtoul (optarg , NULL , 10 );
566
+ break ;
477
567
case 'n' :
478
568
cfg_loop_nodata = true;
479
569
break ;
570
+ case 'p' :
571
+ dest_port = strtoul (optarg , NULL , 10 );
572
+ break ;
573
+ case 'P' :
574
+ proto_count ++ ;
575
+ cfg_use_pf_packet = true;
576
+ cfg_proto = SOCK_DGRAM ;
577
+ cfg_ipproto = 0 ;
578
+ break ;
480
579
case 'r' :
481
580
proto_count ++ ;
482
581
cfg_proto = SOCK_RAW ;
@@ -492,12 +591,6 @@ static void parse_opt(int argc, char **argv)
492
591
cfg_proto = SOCK_DGRAM ;
493
592
cfg_ipproto = IPPROTO_UDP ;
494
593
break ;
495
- case 'l' :
496
- cfg_payload_len = strtoul (optarg , NULL , 10 );
497
- break ;
498
- case 'p' :
499
- dest_port = strtoul (optarg , NULL , 10 );
500
- break ;
501
594
case 'x' :
502
595
cfg_show_payload = true;
503
596
break ;
@@ -514,18 +607,22 @@ static void parse_opt(int argc, char **argv)
514
607
if (!do_ipv4 && !do_ipv6 )
515
608
error (1 , 0 , "pass -4 or -6, not both" );
516
609
if (proto_count > 1 )
517
- error (1 , 0 , "pass -r, -R or -u, not multiple" );
610
+ error (1 , 0 , "pass -P, -r, -R or -u, not multiple" );
611
+ if (cfg_do_pktinfo && cfg_use_pf_packet )
612
+ error (1 , 0 , "cannot ask for pktinfo over pf_packet" );
518
613
519
614
if (optind != argc - 1 )
520
615
error (1 , 0 , "missing required hostname argument" );
521
616
}
522
617
523
618
static void resolve_hostname (const char * hostname )
524
619
{
620
+ struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };
525
621
struct addrinfo * addrs , * cur ;
526
622
int have_ipv4 = 0 , have_ipv6 = 0 ;
527
623
528
- if (getaddrinfo (hostname , NULL , NULL , & addrs ))
624
+ retry :
625
+ if (getaddrinfo (hostname , NULL , & hints , & addrs ))
529
626
error (1 , errno , "getaddrinfo" );
530
627
531
628
cur = addrs ;
@@ -545,14 +642,20 @@ static void resolve_hostname(const char *hostname)
545
642
if (addrs )
546
643
freeaddrinfo (addrs );
547
644
645
+ if (do_ipv6 && hints .ai_family != AF_INET6 ) {
646
+ hints .ai_family = AF_INET6 ;
647
+ goto retry ;
648
+ }
649
+
548
650
do_ipv4 &= have_ipv4 ;
549
651
do_ipv6 &= have_ipv6 ;
550
652
}
551
653
552
654
static void do_main (int family )
553
655
{
554
- fprintf (stderr , "family: %s\n" ,
555
- family == PF_INET ? "INET" : "INET6" );
656
+ fprintf (stderr , "family: %s %s\n" ,
657
+ family == PF_INET ? "INET" : "INET6" ,
658
+ cfg_use_pf_packet ? "(PF_PACKET)" : "" );
556
659
557
660
fprintf (stderr , "test SND\n" );
558
661
do_test (family , SOF_TIMESTAMPING_TX_SOFTWARE );
0 commit comments