Skip to content

Commit b52354a

Browse files
wdebruijdavem330
authored andcommitted
selftests: expand txtimestamp with ipv6 dgram + raw and pf_packet
Expand the transmit timestamp regression test with support for missing protocols: ipv6 datagram and raw and pf_packet. Also refine resolve_hostname to independently request AF_INET or AF_INET6 addresses. Else, ipv4 addresses may be returned as AF_INET6. Signed-off-by: Willem de Bruijn <[email protected]> Acked-by: Soheil Hassas Yeganeh <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7085f47 commit b52354a

File tree

1 file changed

+146
-43
lines changed

1 file changed

+146
-43
lines changed

tools/testing/selftests/networking/timestamping/txtimestamp.c

Lines changed: 146 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <inttypes.h>
4040
#include <linux/errqueue.h>
4141
#include <linux/if_ether.h>
42+
#include <linux/ipv6.h>
4243
#include <linux/net_tstamp.h>
4344
#include <netdb.h>
4445
#include <net/if.h>
@@ -74,6 +75,7 @@ static bool cfg_do_pktinfo;
7475
static bool cfg_loop_nodata;
7576
static bool cfg_no_delay;
7677
static bool cfg_use_cmsg;
78+
static bool cfg_use_pf_packet;
7779
static uint16_t dest_port = 9000;
7880

7981
static struct sockaddr_in daddr;
@@ -195,7 +197,9 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
195197
} else if ((cm->cmsg_level == SOL_IP &&
196198
cm->cmsg_type == IP_RECVERR) ||
197199
(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)) {
199203
serr = (void *) CMSG_DATA(cm);
200204
if (serr->ee_errno != ENOMSG ||
201205
serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
@@ -270,34 +274,118 @@ static int recv_errmsg(int fd)
270274
return ret == -1;
271275
}
272276

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+
273356
static void do_test(int family, unsigned int report_opt)
274357
{
275358
char control[CMSG_SPACE(sizeof(uint32_t))];
359+
struct sockaddr_ll laddr;
276360
unsigned int sock_opt;
277361
struct cmsghdr *cmsg;
278362
struct msghdr msg;
279363
struct iovec iov;
280364
char *buf;
281365
int fd, i, val = 1, total_len;
282366

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-
289367
total_len = cfg_payload_len;
290-
if (cfg_proto == SOCK_RAW) {
368+
if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
291369
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);
294381
}
295382

296383
buf = malloc(total_len);
297384
if (!buf)
298385
error(1, 0, "malloc");
299386

300-
fd = socket(family, cfg_proto, cfg_ipproto);
387+
fd = socket(cfg_use_pf_packet ? PF_PACKET : family,
388+
cfg_proto, cfg_ipproto);
301389
if (fd < 0)
302390
error(1, errno, "socket");
303391

@@ -346,29 +434,17 @@ static void do_test(int family, unsigned int report_opt)
346434
memset(&ts_prev, 0, sizeof(ts_prev));
347435
memset(buf, 'a' + i, total_len);
348436

349-
if (cfg_proto == SOCK_RAW) {
350-
struct udphdr *udph;
437+
if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {
351438
int off = 0;
352439

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);
365445
}
366446

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);
372448
}
373449

374450
print_timestamp_usr();
@@ -377,7 +453,17 @@ static void do_test(int family, unsigned int report_opt)
377453
iov.iov_len = total_len;
378454

379455
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) {
381467
msg.msg_name = (void *)&daddr;
382468
msg.msg_namelen = sizeof(daddr);
383469
} else {
@@ -437,9 +523,10 @@ static void __attribute__((noreturn)) usage(const char *filepath)
437523
" -I: request PKTINFO\n"
438524
" -l N: send N bytes at a time\n"
439525
" -n: set no-payload option\n"
526+
" -p N: connect to port N\n"
527+
" -P: use PF_PACKET\n"
440528
" -r: use raw\n"
441529
" -R: use raw (IP_HDRINCL)\n"
442-
" -p N: connect to port N\n"
443530
" -u: use udp\n"
444531
" -x: show payload (up to 70 bytes)\n",
445532
filepath);
@@ -451,7 +538,7 @@ static void parse_opt(int argc, char **argv)
451538
int proto_count = 0;
452539
int c;
453540

454-
while ((c = getopt(argc, argv, "46c:CDFhIl:np:rRux")) != -1) {
541+
while ((c = getopt(argc, argv, "46c:CDFhIl:np:PrRux")) != -1) {
455542
switch (c) {
456543
case '4':
457544
do_ipv6 = 0;
@@ -474,9 +561,21 @@ static void parse_opt(int argc, char **argv)
474561
case 'I':
475562
cfg_do_pktinfo = true;
476563
break;
564+
case 'l':
565+
cfg_payload_len = strtoul(optarg, NULL, 10);
566+
break;
477567
case 'n':
478568
cfg_loop_nodata = true;
479569
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;
480579
case 'r':
481580
proto_count++;
482581
cfg_proto = SOCK_RAW;
@@ -492,12 +591,6 @@ static void parse_opt(int argc, char **argv)
492591
cfg_proto = SOCK_DGRAM;
493592
cfg_ipproto = IPPROTO_UDP;
494593
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;
501594
case 'x':
502595
cfg_show_payload = true;
503596
break;
@@ -514,18 +607,22 @@ static void parse_opt(int argc, char **argv)
514607
if (!do_ipv4 && !do_ipv6)
515608
error(1, 0, "pass -4 or -6, not both");
516609
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");
518613

519614
if (optind != argc - 1)
520615
error(1, 0, "missing required hostname argument");
521616
}
522617

523618
static void resolve_hostname(const char *hostname)
524619
{
620+
struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };
525621
struct addrinfo *addrs, *cur;
526622
int have_ipv4 = 0, have_ipv6 = 0;
527623

528-
if (getaddrinfo(hostname, NULL, NULL, &addrs))
624+
retry:
625+
if (getaddrinfo(hostname, NULL, &hints, &addrs))
529626
error(1, errno, "getaddrinfo");
530627

531628
cur = addrs;
@@ -545,14 +642,20 @@ static void resolve_hostname(const char *hostname)
545642
if (addrs)
546643
freeaddrinfo(addrs);
547644

645+
if (do_ipv6 && hints.ai_family != AF_INET6) {
646+
hints.ai_family = AF_INET6;
647+
goto retry;
648+
}
649+
548650
do_ipv4 &= have_ipv4;
549651
do_ipv6 &= have_ipv6;
550652
}
551653

552654
static void do_main(int family)
553655
{
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)" : "");
556659

557660
fprintf(stderr, "test SND\n");
558661
do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);

0 commit comments

Comments
 (0)