From 426522394649233f00b1da9bc813fdd5ef3a05fe Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:39 -0800 Subject: net: ping6: remove a pr_debug() statement We have ftrace and BPF today, there's no need for printing arguments at the start of a function. Signed-off-by: Jakub Kicinski Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ping.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 9256f6ba87ef..86a72f7a61cf 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -59,8 +59,6 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) struct pingfakehdr pfh; struct ipcm6_cookie ipc6; - pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); - err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, sizeof(user_icmph)); if (err) -- cgit v1.2.3 From e7b060460f29c31a550cb563819c69f2b1cb7b10 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:40 -0800 Subject: net: ping6: support packet timestamping Nothing prevents the user from requesting timestamping on ping6 sockets, yet timestamps are not going to be reported. Plumb the flags through. Signed-off-by: Jakub Kicinski Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ping.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 86a72f7a61cf..3228ccd8abf1 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -113,6 +113,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipcm6_init_sk(&ipc6, np); ipc6.sockc.mark = sk->sk_mark; + ipc6.sockc.tsflags = sk->sk_tsflags; fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); -- cgit v1.2.3 From 3ebb0b1032e55614672af10c491302018bf34fd4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:41 -0800 Subject: net: ping6: support setting socket options via cmsg Minor reordering of the code and a call to sock_cmsg_send() gives us support for setting the common socket options via cmsg (the usual ones - SO_MARK, SO_TIMESTAMPING_OLD, SCM_TXTIME). Signed-off-by: Jakub Kicinski Reviewed-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ping.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 3228ccd8abf1..d5544cf67ffe 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -97,6 +97,14 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if)) return -EINVAL; + ipcm6_init_sk(&ipc6, np); + ipc6.sockc.tsflags = sk->sk_tsflags; + ipc6.sockc.mark = sk->sk_mark; + + err = sock_cmsg_send(sk, msg, &ipc6.sockc); + if (err) + return err; + /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ memset(&fl6, 0, sizeof(fl6)); @@ -105,15 +113,12 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.daddr = *daddr; fl6.flowi6_oif = oif; - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = ipc6.sockc.mark; fl6.flowi6_uid = sk->sk_uid; fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); - ipcm6_init_sk(&ipc6, np); - ipc6.sockc.mark = sk->sk_mark; - ipc6.sockc.tsflags = sk->sk_tsflags; fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, false); -- cgit v1.2.3 From a086ee24cce21994150789fc83f31ee7f5a9cf2d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:42 -0800 Subject: selftests: net: rename cmsg_so_mark Rename the file in prep for generalization. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/.gitignore | 2 +- tools/testing/selftests/net/Makefile | 2 +- tools/testing/selftests/net/cmsg_sender.c | 67 +++++++++++++++++++++++++++++ tools/testing/selftests/net/cmsg_so_mark.c | 67 ----------------------------- tools/testing/selftests/net/cmsg_so_mark.sh | 8 ++-- 5 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 tools/testing/selftests/net/cmsg_sender.c delete mode 100644 tools/testing/selftests/net/cmsg_so_mark.c diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index 7581a7348e1b..21a411b04890 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -35,4 +35,4 @@ test_unix_oob gro ioam6_parser toeplitz -cmsg_so_mark +cmsg_sender diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 9897fa9ab953..8f4c1f16655f 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -52,7 +52,7 @@ TEST_GEN_FILES += gro TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls TEST_GEN_FILES += toeplitz -TEST_GEN_FILES += cmsg_so_mark +TEST_GEN_FILES += cmsg_sender TEST_FILES := settings diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c new file mode 100644 index 000000000000..27f2804892a7 --- /dev/null +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, const char **argv) +{ + char cbuf[CMSG_SPACE(sizeof(__u32))]; + struct addrinfo hints, *ai; + struct cmsghdr *cmsg; + struct iovec iov[1]; + struct msghdr msg; + int mark; + int err; + int fd; + + if (argc != 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + mark = atoi(argv[3]); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + ai = NULL; + err = getaddrinfo(argv[1], argv[2], &hints, &ai); + if (err) { + fprintf(stderr, "Can't resolve address: %s\n", strerror(errno)); + return 1; + } + + fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); + freeaddrinfo(ai); + return 1; + } + + iov[0].iov_base = "bla"; + iov[0].iov_len = 4; + + msg.msg_name = ai->ai_addr; + msg.msg_namelen = ai->ai_addrlen; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_MARK; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); + *(__u32 *)CMSG_DATA(cmsg) = mark; + + err = sendmsg(fd, &msg, 0); + + close(fd); + freeaddrinfo(ai); + return err != 4; +} diff --git a/tools/testing/selftests/net/cmsg_so_mark.c b/tools/testing/selftests/net/cmsg_so_mark.c deleted file mode 100644 index 27f2804892a7..000000000000 --- a/tools/testing/selftests/net/cmsg_so_mark.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, const char **argv) -{ - char cbuf[CMSG_SPACE(sizeof(__u32))]; - struct addrinfo hints, *ai; - struct cmsghdr *cmsg; - struct iovec iov[1]; - struct msghdr msg; - int mark; - int err; - int fd; - - if (argc != 4) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - mark = atoi(argv[3]); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - - ai = NULL; - err = getaddrinfo(argv[1], argv[2], &hints, &ai); - if (err) { - fprintf(stderr, "Can't resolve address: %s\n", strerror(errno)); - return 1; - } - - fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); - freeaddrinfo(ai); - return 1; - } - - iov[0].iov_base = "bla"; - iov[0].iov_len = 4; - - msg.msg_name = ai->ai_addr; - msg.msg_namelen = ai->ai_addrlen; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_MARK; - cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); - *(__u32 *)CMSG_DATA(cmsg) = mark; - - err = sendmsg(fd, &msg, 0); - - close(fd); - freeaddrinfo(ai); - return err != 4; -} diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh index 19c6aab8d0e9..29a623aac74b 100755 --- a/tools/testing/selftests/net/cmsg_so_mark.sh +++ b/tools/testing/selftests/net/cmsg_so_mark.sh @@ -41,14 +41,14 @@ check_result() { fi } -ip netns exec $NS ./cmsg_so_mark $TGT4 1234 $((MARK + 1)) +ip netns exec $NS ./cmsg_sender $TGT4 1234 $((MARK + 1)) check_result $? 0 "IPv4 pass" -ip netns exec $NS ./cmsg_so_mark $TGT6 1234 $((MARK + 1)) +ip netns exec $NS ./cmsg_sender $TGT6 1234 $((MARK + 1)) check_result $? 0 "IPv6 pass" -ip netns exec $NS ./cmsg_so_mark $TGT4 1234 $MARK +ip netns exec $NS ./cmsg_sender $TGT4 1234 $MARK check_result $? 1 "IPv4 rejection" -ip netns exec $NS ./cmsg_so_mark $TGT6 1234 $MARK +ip netns exec $NS ./cmsg_sender $TGT6 1234 $MARK check_result $? 1 "IPv6 rejection" # Summary -- cgit v1.2.3 From 49b78613029642eec9601af6bc6c716e10929106 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:43 -0800 Subject: selftests: net: make cmsg_so_mark ready for more options Parametrize the code so that it can support UDP and ICMP sockets in the future, and more cmsg types. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_sender.c | 135 +++++++++++++++++++++++----- tools/testing/selftests/net/cmsg_so_mark.sh | 8 +- 2 files changed, 117 insertions(+), 26 deletions(-) diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c index 27f2804892a7..4528ae638aea 100644 --- a/tools/testing/selftests/net/cmsg_sender.c +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include +#include #include #include #include @@ -8,60 +10,149 @@ #include #include -int main(int argc, const char **argv) +enum { + ERN_SUCCESS = 0, + /* Well defined errors, callers may depend on these */ + ERN_SEND = 1, + /* Informational, can reorder */ + ERN_HELP, + ERN_SEND_SHORT, + ERN_SOCK_CREATE, + ERN_RESOLVE, + ERN_CMSG_WR, +}; + +struct options { + bool silent_send; + const char *host; + const char *service; + struct { + unsigned int type; + } sock; + struct { + bool ena; + unsigned int val; + } mark; +} opt = { + .sock = { + .type = SOCK_DGRAM, + }, +}; + +static void __attribute__((noreturn)) cs_usage(const char *bin) +{ + printf("Usage: %s [opts] \n", bin); + printf("Options:\n" + "\t\t-s Silent send() failures\n" + "\t\t-m val Set SO_MARK with given value\n" + ""); + exit(ERN_HELP); +} + +static void cs_parse_args(int argc, char *argv[]) +{ + char o; + + while ((o = getopt(argc, argv, "sm:")) != -1) { + switch (o) { + case 's': + opt.silent_send = true; + break; + case 'm': + opt.mark.ena = true; + opt.mark.val = atoi(optarg); + break; + } + } + + if (optind != argc - 2) + cs_usage(argv[0]); + + opt.host = argv[optind]; + opt.service = argv[optind + 1]; +} + +static void +cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz) { - char cbuf[CMSG_SPACE(sizeof(__u32))]; - struct addrinfo hints, *ai; struct cmsghdr *cmsg; + size_t cmsg_len; + + msg->msg_control = cbuf; + cmsg_len = 0; + + if (opt.mark.ena) { + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(sizeof(__u32)); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_MARK; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); + *(__u32 *)CMSG_DATA(cmsg) = opt.mark.val; + } + + if (cmsg_len) + msg->msg_controllen = cmsg_len; + else + msg->msg_control = NULL; +} + +int main(int argc, char *argv[]) +{ + struct addrinfo hints, *ai; struct iovec iov[1]; struct msghdr msg; - int mark; + char cbuf[1024]; int err; int fd; - if (argc != 4) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - mark = atoi(argv[3]); + cs_parse_args(argc, argv); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; + hints.ai_socktype = opt.sock.type; ai = NULL; - err = getaddrinfo(argv[1], argv[2], &hints, &ai); + err = getaddrinfo(opt.host, opt.service, &hints, &ai); if (err) { - fprintf(stderr, "Can't resolve address: %s\n", strerror(errno)); - return 1; + fprintf(stderr, "Can't resolve address [%s]:%s: %s\n", + opt.host, opt.service, strerror(errno)); + return ERN_SOCK_CREATE; } fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); freeaddrinfo(ai); - return 1; + return ERN_RESOLVE; } iov[0].iov_base = "bla"; iov[0].iov_len = 4; + memset(&msg, 0, sizeof(msg)); msg.msg_name = ai->ai_addr; msg.msg_namelen = ai->ai_addrlen; msg.msg_iov = iov; msg.msg_iovlen = 1; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SO_MARK; - cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); - *(__u32 *)CMSG_DATA(cmsg) = mark; + cs_write_cmsg(&msg, cbuf, sizeof(cbuf)); err = sendmsg(fd, &msg, 0); + if (err < 0) { + if (!opt.silent_send) + fprintf(stderr, "send failed: %s\n", strerror(errno)); + err = ERN_SEND; + } else if (err != 4) { + fprintf(stderr, "short send\n"); + err = ERN_SEND_SHORT; + } else { + err = ERN_SUCCESS; + } close(fd); freeaddrinfo(ai); - return err != 4; + return err; } diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh index 29a623aac74b..841d706dc91b 100755 --- a/tools/testing/selftests/net/cmsg_so_mark.sh +++ b/tools/testing/selftests/net/cmsg_so_mark.sh @@ -41,14 +41,14 @@ check_result() { fi } -ip netns exec $NS ./cmsg_sender $TGT4 1234 $((MARK + 1)) +ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT4 1234 check_result $? 0 "IPv4 pass" -ip netns exec $NS ./cmsg_sender $TGT6 1234 $((MARK + 1)) +ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT6 1234 check_result $? 0 "IPv6 pass" -ip netns exec $NS ./cmsg_sender $TGT4 1234 $MARK +ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT4 1234 check_result $? 1 "IPv4 rejection" -ip netns exec $NS ./cmsg_sender $TGT6 1234 $MARK +ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT6 1234 check_result $? 1 "IPv6 rejection" # Summary -- cgit v1.2.3 From de17e305a81012120c23380a94ed22c7e8bf324f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:44 -0800 Subject: selftests: net: cmsg_sender: support icmp and raw sockets Support sending fake ICMP(v6) messages and UDP via RAW sockets. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_sender.c | 64 ++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c index 4528ae638aea..edb8c427c7cb 100644 --- a/tools/testing/selftests/net/cmsg_sender.c +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -7,7 +7,10 @@ #include #include #include +#include +#include #include +#include #include enum { @@ -27,7 +30,9 @@ struct options { const char *host; const char *service; struct { + unsigned int family; unsigned int type; + unsigned int proto; } sock; struct { bool ena; @@ -35,7 +40,9 @@ struct options { } mark; } opt = { .sock = { + .family = AF_UNSPEC, .type = SOCK_DGRAM, + .proto = IPPROTO_UDP, }, }; @@ -44,6 +51,10 @@ static void __attribute__((noreturn)) cs_usage(const char *bin) printf("Usage: %s [opts] \n", bin); printf("Options:\n" "\t\t-s Silent send() failures\n" + "\t\t-4/-6 Force IPv4 / IPv6 only\n" + "\t\t-p prot Socket protocol\n" + "\t\t (u = UDP (default); i = ICMP; r = RAW)\n" + "\n" "\t\t-m val Set SO_MARK with given value\n" ""); exit(ERN_HELP); @@ -53,11 +64,29 @@ static void cs_parse_args(int argc, char *argv[]) { char o; - while ((o = getopt(argc, argv, "sm:")) != -1) { + while ((o = getopt(argc, argv, "46sp:m:")) != -1) { switch (o) { case 's': opt.silent_send = true; break; + case '4': + opt.sock.family = AF_INET; + break; + case '6': + opt.sock.family = AF_INET6; + break; + case 'p': + if (*optarg == 'u' || *optarg == 'U') { + opt.sock.proto = IPPROTO_UDP; + } else if (*optarg == 'i' || *optarg == 'I') { + opt.sock.proto = IPPROTO_ICMP; + } else if (*optarg == 'r') { + opt.sock.type = SOCK_RAW; + } else { + printf("Error: unknown protocol: %s\n", optarg); + cs_usage(argv[0]); + } + break; case 'm': opt.mark.ena = true; opt.mark.val = atoi(optarg); @@ -101,6 +130,7 @@ cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz) int main(int argc, char *argv[]) { + char buf[] = "blablablabla"; struct addrinfo hints, *ai; struct iovec iov[1]; struct msghdr msg; @@ -111,26 +141,42 @@ int main(int argc, char *argv[]) cs_parse_args(argc, argv); memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = opt.sock.type; + hints.ai_family = opt.sock.family; ai = NULL; err = getaddrinfo(opt.host, opt.service, &hints, &ai); if (err) { - fprintf(stderr, "Can't resolve address [%s]:%s: %s\n", - opt.host, opt.service, strerror(errno)); + fprintf(stderr, "Can't resolve address [%s]:%s\n", + opt.host, opt.service); return ERN_SOCK_CREATE; } - fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if (ai->ai_family == AF_INET6 && opt.sock.proto == IPPROTO_ICMP) + opt.sock.proto = IPPROTO_ICMPV6; + + fd = socket(ai->ai_family, opt.sock.type, opt.sock.proto); if (fd < 0) { fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); freeaddrinfo(ai); return ERN_RESOLVE; } - iov[0].iov_base = "bla"; - iov[0].iov_len = 4; + if (opt.sock.proto == IPPROTO_ICMP) { + buf[0] = ICMP_ECHO; + buf[1] = 0; + } else if (opt.sock.proto == IPPROTO_ICMPV6) { + buf[0] = ICMPV6_ECHO_REQUEST; + buf[1] = 0; + } else if (opt.sock.type == SOCK_RAW) { + struct udphdr hdr = { 1, 2, htons(sizeof(buf)), 0 }; + struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;; + + memcpy(buf, &hdr, sizeof(hdr)); + sin6->sin6_port = htons(opt.sock.proto); + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_name = ai->ai_addr; @@ -145,7 +191,7 @@ int main(int argc, char *argv[]) if (!opt.silent_send) fprintf(stderr, "send failed: %s\n", strerror(errno)); err = ERN_SEND; - } else if (err != 4) { + } else if (err != sizeof(buf)) { fprintf(stderr, "short send\n"); err = ERN_SEND_SHORT; } else { -- cgit v1.2.3 From 0344488e11cab982d2b2402a1689ced1815680fc Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:45 -0800 Subject: selftests: net: cmsg_so_mark: test ICMP and RAW sockets Use new capabilities of cmsg_sender to test ICMP and RAW sockets, previously only UDP was tested. Before SO_MARK support was added to ICMPv6: # ./cmsg_so_mark.sh Case ICMP rejection returned 0, expected 1 FAIL - 1/12 cases failed After: # ./cmsg_so_mark.sh OK Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_so_mark.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh index 841d706dc91b..925f6b9deee2 100755 --- a/tools/testing/selftests/net/cmsg_so_mark.sh +++ b/tools/testing/selftests/net/cmsg_so_mark.sh @@ -18,6 +18,8 @@ trap cleanup EXIT # Namespaces ip netns add $NS +ip netns exec $NS sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null + # Connectivity ip -netns $NS link add type dummy ip -netns $NS link set dev dummy0 up @@ -41,15 +43,21 @@ check_result() { fi } -ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT4 1234 -check_result $? 0 "IPv4 pass" -ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT6 1234 -check_result $? 0 "IPv6 pass" +for i in 4 6; do + [ $i == 4 ] && TGT=$TGT4 || TGT=$TGT6 + + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW + + ip netns exec $NS ./cmsg_sender -$i -p $p -m $((MARK + 1)) $TGT 1234 + check_result $? 0 "$prot pass" -ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT4 1234 -check_result $? 1 "IPv4 rejection" -ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT6 1234 -check_result $? 1 "IPv6 rejection" + ip netns exec $NS ./cmsg_sender -$i -p $p -m $MARK -s $TGT 1234 + check_result $? 1 "$prot rejection" + done +done # Summary if [ $BAD -ne 0 ]; then -- cgit v1.2.3 From 9bbfbc92c64a9f4d5ac4205071c5fc02a8201039 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:46 -0800 Subject: selftests: net: cmsg_so_mark: test with SO_MARK set by setsockopt Test if setting SO_MARK with setsockopt works and if cmsg takes precedence over it. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_sender.c | 14 +++++++++++++- tools/testing/selftests/net/cmsg_so_mark.sh | 28 ++++++++++++++++++---------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c index edb8c427c7cb..c7586a4b0361 100644 --- a/tools/testing/selftests/net/cmsg_sender.c +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -29,6 +29,9 @@ struct options { bool silent_send; const char *host; const char *service; + struct { + unsigned int mark; + } sockopt; struct { unsigned int family; unsigned int type; @@ -56,6 +59,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin) "\t\t (u = UDP (default); i = ICMP; r = RAW)\n" "\n" "\t\t-m val Set SO_MARK with given value\n" + "\t\t-M val Set SO_MARK via setsockopt\n" ""); exit(ERN_HELP); } @@ -64,7 +68,7 @@ static void cs_parse_args(int argc, char *argv[]) { char o; - while ((o = getopt(argc, argv, "46sp:m:")) != -1) { + while ((o = getopt(argc, argv, "46sp:m:M:")) != -1) { switch (o) { case 's': opt.silent_send = true; @@ -91,6 +95,9 @@ static void cs_parse_args(int argc, char *argv[]) opt.mark.ena = true; opt.mark.val = atoi(optarg); break; + case 'M': + opt.sockopt.mark = atoi(optarg); + break; } } @@ -175,6 +182,11 @@ int main(int argc, char *argv[]) sin6->sin6_port = htons(opt.sock.proto); } + if (opt.sockopt.mark && + setsockopt(fd, SOL_SOCKET, SO_MARK, + &opt.sockopt.mark, sizeof(opt.sockopt.mark))) + error(ERN_SOCKOPT, errno, "setsockopt SO_MARK"); + iov[0].iov_base = buf; iov[0].iov_len = sizeof(buf); diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh index 925f6b9deee2..1650b8622f2f 100755 --- a/tools/testing/selftests/net/cmsg_so_mark.sh +++ b/tools/testing/selftests/net/cmsg_so_mark.sh @@ -43,19 +43,27 @@ check_result() { fi } -for i in 4 6; do - [ $i == 4 ] && TGT=$TGT4 || TGT=$TGT6 +for ovr in setsock cmsg both; do + for i in 4 6; do + [ $i == 4 ] && TGT=$TGT4 || TGT=$TGT6 - for p in u i r; do - [ $p == "u" ] && prot=UDP - [ $p == "i" ] && prot=ICMP - [ $p == "r" ] && prot=RAW + for p in u i r; do + [ $p == "u" ] && prot=UDP + [ $p == "i" ] && prot=ICMP + [ $p == "r" ] && prot=RAW - ip netns exec $NS ./cmsg_sender -$i -p $p -m $((MARK + 1)) $TGT 1234 - check_result $? 0 "$prot pass" + [ $ovr == "setsock" ] && m="-M" + [ $ovr == "cmsg" ] && m="-m" + [ $ovr == "both" ] && m="-M $MARK -m" - ip netns exec $NS ./cmsg_sender -$i -p $p -m $MARK -s $TGT 1234 - check_result $? 1 "$prot rejection" + ip netns exec $NS ./cmsg_sender -$i -p $p $m $((MARK + 1)) $TGT 1234 + check_result $? 0 "$prot $ovr - pass" + + [ $ovr == "diff" ] && m="-M $((MARK + 1)) -m" + + ip netns exec $NS ./cmsg_sender -$i -p $p $m $MARK -s $TGT 1234 + check_result $? 1 "$prot $ovr - rejection" + done done done -- cgit v1.2.3 From 4d397424a5e0e130d5e8d0023776f0aa2e791f51 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:47 -0800 Subject: selftests: net: cmsg_sender: support setting SO_TXTIME Add ability to send delayed packets. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_sender.c | 49 +++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c index c7586a4b0361..5a722baa108b 100644 --- a/tools/testing/selftests/net/cmsg_sender.c +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -6,9 +6,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -23,6 +25,8 @@ enum { ERN_SOCK_CREATE, ERN_RESOLVE, ERN_CMSG_WR, + ERN_SOCKOPT, + ERN_GETTIME, }; struct options { @@ -41,6 +45,10 @@ struct options { bool ena; unsigned int val; } mark; + struct { + bool ena; + unsigned int delay; + } txtime; } opt = { .sock = { .family = AF_UNSPEC, @@ -49,6 +57,8 @@ struct options { }, }; +static struct timespec time_start_mono; + static void __attribute__((noreturn)) cs_usage(const char *bin) { printf("Usage: %s [opts] \n", bin); @@ -60,6 +70,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin) "\n" "\t\t-m val Set SO_MARK with given value\n" "\t\t-M val Set SO_MARK via setsockopt\n" + "\t\t-d val Set SO_TXTIME with given delay (usec)\n" ""); exit(ERN_HELP); } @@ -68,7 +79,7 @@ static void cs_parse_args(int argc, char *argv[]) { char o; - while ((o = getopt(argc, argv, "46sp:m:M:")) != -1) { + while ((o = getopt(argc, argv, "46sp:m:M:d:")) != -1) { switch (o) { case 's': opt.silent_send = true; @@ -91,6 +102,7 @@ static void cs_parse_args(int argc, char *argv[]) cs_usage(argv[0]); } break; + case 'm': opt.mark.ena = true; opt.mark.val = atoi(optarg); @@ -98,6 +110,10 @@ static void cs_parse_args(int argc, char *argv[]) case 'M': opt.sockopt.mark = atoi(optarg); break; + case 'd': + opt.txtime.ena = true; + opt.txtime.delay = atoi(optarg); + break; } } @@ -109,7 +125,7 @@ static void cs_parse_args(int argc, char *argv[]) } static void -cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz) +cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) { struct cmsghdr *cmsg; size_t cmsg_len; @@ -128,6 +144,30 @@ cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz) cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); *(__u32 *)CMSG_DATA(cmsg) = opt.mark.val; } + if (opt.txtime.ena) { + struct sock_txtime so_txtime = { + .clockid = CLOCK_MONOTONIC, + }; + __u64 txtime; + + if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, + &so_txtime, sizeof(so_txtime))) + error(ERN_SOCKOPT, errno, "setsockopt TXTIME"); + + txtime = time_start_mono.tv_sec * (1000ULL * 1000 * 1000) + + time_start_mono.tv_nsec + + opt.txtime.delay * 1000; + + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(sizeof(txtime)); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_TXTIME; + cmsg->cmsg_len = CMSG_LEN(sizeof(txtime)); + memcpy(CMSG_DATA(cmsg), &txtime, sizeof(txtime)); + } if (cmsg_len) msg->msg_controllen = cmsg_len; @@ -187,6 +227,9 @@ int main(int argc, char *argv[]) &opt.sockopt.mark, sizeof(opt.sockopt.mark))) error(ERN_SOCKOPT, errno, "setsockopt SO_MARK"); + if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono)) + error(ERN_GETTIME, errno, "gettime MONOTINIC"); + iov[0].iov_base = buf; iov[0].iov_len = sizeof(buf); @@ -196,7 +239,7 @@ int main(int argc, char *argv[]) msg.msg_iov = iov; msg.msg_iovlen = 1; - cs_write_cmsg(&msg, cbuf, sizeof(cbuf)); + cs_write_cmsg(fd, &msg, cbuf, sizeof(cbuf)); err = sendmsg(fd, &msg, 0); if (err < 0) { -- cgit v1.2.3 From eb8f3116fb3f51062ed8df9db3c4ca730eec3743 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:48 -0800 Subject: selftests: net: cmsg_sender: support Tx timestamping Support requesting Tx timestamps: $ ./cmsg_sender -p i -t -4 $tgt 123 -d 1000 SCHED ts0 61us SND ts0 1071us Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/cmsg_sender.c | 123 +++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c index 5a722baa108b..24444dc72543 100644 --- a/tools/testing/selftests/net/cmsg_sender.c +++ b/tools/testing/selftests/net/cmsg_sender.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,9 @@ enum { ERN_CMSG_WR, ERN_SOCKOPT, ERN_GETTIME, + ERN_RECVERR, + ERN_CMSG_RD, + ERN_CMSG_RCV, }; struct options { @@ -49,6 +53,9 @@ struct options { bool ena; unsigned int delay; } txtime; + struct { + bool ena; + } ts; } opt = { .sock = { .family = AF_UNSPEC, @@ -57,6 +64,7 @@ struct options { }, }; +static struct timespec time_start_real; static struct timespec time_start_mono; static void __attribute__((noreturn)) cs_usage(const char *bin) @@ -71,6 +79,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin) "\t\t-m val Set SO_MARK with given value\n" "\t\t-M val Set SO_MARK via setsockopt\n" "\t\t-d val Set SO_TXTIME with given delay (usec)\n" + "\t\t-t Enable time stamp reporting\n" ""); exit(ERN_HELP); } @@ -79,7 +88,7 @@ static void cs_parse_args(int argc, char *argv[]) { char o; - while ((o = getopt(argc, argv, "46sp:m:M:d:")) != -1) { + while ((o = getopt(argc, argv, "46sp:m:M:d:t")) != -1) { switch (o) { case 's': opt.silent_send = true; @@ -114,6 +123,9 @@ static void cs_parse_args(int argc, char *argv[]) opt.txtime.ena = true; opt.txtime.delay = atoi(optarg); break; + case 't': + opt.ts.ena = true; + break; } } @@ -168,6 +180,25 @@ cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) cmsg->cmsg_len = CMSG_LEN(sizeof(txtime)); memcpy(CMSG_DATA(cmsg), &txtime, sizeof(txtime)); } + if (opt.ts.ena) { + __u32 val = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_TSONLY; + + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, + &val, sizeof(val))) + error(ERN_SOCKOPT, errno, "setsockopt TIMESTAMPING"); + + cmsg = (struct cmsghdr *)(cbuf + cmsg_len); + cmsg_len += CMSG_SPACE(sizeof(__u32)); + if (cbuf_sz < cmsg_len) + error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_TIMESTAMPING; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); + *(__u32 *)CMSG_DATA(cmsg) = SOF_TIMESTAMPING_TX_SCHED | + SOF_TIMESTAMPING_TX_SOFTWARE; + } if (cmsg_len) msg->msg_controllen = cmsg_len; @@ -175,6 +206,86 @@ cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) msg->msg_control = NULL; } +static const char *cs_ts_info2str(unsigned int info) +{ + static const char *names[] = { + [SCM_TSTAMP_SND] = "SND", + [SCM_TSTAMP_SCHED] = "SCHED", + [SCM_TSTAMP_ACK] = "ACK", + }; + + if (info < sizeof(names) / sizeof(names[0])) + return names[info]; + return "unknown"; +} + +static void +cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) +{ + struct sock_extended_err *see; + struct scm_timestamping *ts; + struct cmsghdr *cmsg; + int i, err; + + if (!opt.ts.ena) + return; + msg->msg_control = cbuf; + msg->msg_controllen = cbuf_sz; + + while (true) { + ts = NULL; + see = NULL; + memset(cbuf, 0, cbuf_sz); + + err = recvmsg(fd, msg, MSG_ERRQUEUE); + if (err < 0) { + if (errno == EAGAIN) + break; + error(ERN_RECVERR, errno, "recvmsg ERRQ"); + } + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMPING_OLD) { + if (cmsg->cmsg_len < sizeof(*ts)) + error(ERN_CMSG_RD, EINVAL, "TS cmsg"); + + ts = (void *)CMSG_DATA(cmsg); + } + if ((cmsg->cmsg_level == SOL_IP && + cmsg->cmsg_type == IP_RECVERR) || + (cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_RECVERR)) { + if (cmsg->cmsg_len < sizeof(*see)) + error(ERN_CMSG_RD, EINVAL, "sock_err cmsg"); + + see = (void *)CMSG_DATA(cmsg); + } + } + + if (!ts) + error(ERN_CMSG_RCV, ENOENT, "TS cmsg not found"); + if (!see) + error(ERN_CMSG_RCV, ENOENT, "sock_err cmsg not found"); + + for (i = 0; i < 3; i++) { + unsigned long long rel_time; + + if (!ts->ts[i].tv_sec && !ts->ts[i].tv_nsec) + continue; + + rel_time = (ts->ts[i].tv_sec - time_start_real.tv_sec) * + (1000ULL * 1000) + + (ts->ts[i].tv_nsec - time_start_real.tv_nsec) / + 1000; + printf(" %5s ts%d %lluus\n", + cs_ts_info2str(see->ee_info), + i, rel_time); + } + } +} + int main(int argc, char *argv[]) { char buf[] = "blablablabla"; @@ -227,6 +338,8 @@ int main(int argc, char *argv[]) &opt.sockopt.mark, sizeof(opt.sockopt.mark))) error(ERN_SOCKOPT, errno, "setsockopt SO_MARK"); + if (clock_gettime(CLOCK_REALTIME, &time_start_real)) + error(ERN_GETTIME, errno, "gettime REALTIME"); if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono)) error(ERN_GETTIME, errno, "gettime MONOTINIC"); @@ -246,13 +359,21 @@ int main(int argc, char *argv[]) if (!opt.silent_send) fprintf(stderr, "send failed: %s\n", strerror(errno)); err = ERN_SEND; + goto err_out; } else if (err != sizeof(buf)) { fprintf(stderr, "short send\n"); err = ERN_SEND_SHORT; + goto err_out; } else { err = ERN_SUCCESS; } + /* Make sure all timestamps have time to loop back */ + usleep(opt.txtime.delay); + + cs_read_cmsg(fd, &msg, cbuf, sizeof(cbuf)); + +err_out: close(fd); freeaddrinfo(ai); return err; -- cgit v1.2.3 From af6ca20591efce42535e1c870b7ae6eae98df1b3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 9 Feb 2022 16:36:49 -0800 Subject: selftests: net: test standard socket cmsgs across UDP and ICMP sockets Test TIMESTAMPING and TXTIME across UDP / ICMP and IP versions. Before ICMPv6 support: # ./tools/testing/selftests/net/cmsg_time.sh Case ICMPv6 - ts cnt returned '0', expected '2' Case ICMPv6 - ts0 SCHED returned '', expected 'OK' Case ICMPv6 - ts0 SND returned '', expected 'OK' Case ICMPv6 - TXTIME abs returned '', expected 'OK' Case ICMPv6 - TXTIME rel returned '', expected 'OK' FAIL - 5/36 cases failed After: # ./tools/testing/selftests/net/cmsg_time.sh OK Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/cmsg_time.sh | 83 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100755 tools/testing/selftests/net/cmsg_time.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 8f4c1f16655f..3bfeaf06b960 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -30,6 +30,7 @@ TEST_PROGS += ioam6.sh TEST_PROGS += gro.sh TEST_PROGS += gre_gso.sh TEST_PROGS += cmsg_so_mark.sh +TEST_PROGS += cmsg_time.sh TEST_PROGS += srv6_end_dt46_l3vpn_test.sh TEST_PROGS += srv6_end_dt4_l3vpn_test.sh TEST_PROGS += srv6_end_dt6_l3vpn_test.sh diff --git a/tools/testing/selftests/net/cmsg_time.sh b/tools/testing/selftests/net/cmsg_time.sh new file mode 100755 index 000000000000..91161e1da734 --- /dev/null +++ b/tools/testing/selftests/net/cmsg_time.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +NS=ns +IP4=172.16.0.1/24 +TGT4=172.16.0.2 +IP6=2001:db8:1::1/64 +TGT6=2001:db8:1::2 + +cleanup() +{ + ip netns del $NS +} + +trap cleanup EXIT + +# Namespaces +ip netns add $NS + +ip netns exec $NS sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null + +# Connectivity +ip -netns $NS link add type dummy +ip -netns $NS link set dev dummy0 up +ip -netns $NS addr add $IP4 dev dummy0 +ip -netns $NS addr add $IP6 dev dummy0 + +# Need FQ for TXTIME +ip netns exec $NS tc qdisc replace dev dummy0 root fq + +# Test +BAD=0 +TOTAL=0 + +check_result() { + ((TOTAL++)) + if [ $1 -ne 0 ]; then + echo " Case $4 returned $1, expected 0" + ((BAD++)) + elif [ "$2" != "$3" ]; then + echo " Case $4 returned '$2', expected '$3'" + ((BAD++)) + fi +} + +for i in "-4 $TGT4" "-6 $TGT6"; do + for p in u i r; do + [ $p == "u" ] && prot=UDPv${i:1:2} + [ $p == "i" ] && prot=ICMPv${i:1:2} + [ $p == "r" ] && prot=RAWv${i:1:2} + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234) + check_result $? "$ts" "" "$prot - no options" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | wc -l) + check_result $? "$ts" "2" "$prot - ts cnt" + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | + sed -n "s/.*SCHED ts0 [0-9].*/OK/p") + check_result $? "$ts" "OK" "$prot - ts0 SCHED" + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t | + sed -n "s/.*SND ts0 [0-9].*/OK/p") + check_result $? "$ts" "OK" "$prot - ts0 SND" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t -d 1000 | + awk '/SND/ { if ($3 > 1000) print "OK"; }') + check_result $? "$ts" "OK" "$prot - TXTIME abs" + + ts=$(ip netns exec $NS ./cmsg_sender -p $p $i 1234 -t -d 1000 | + awk '/SND/ {snd=$3} + /SCHED/ {sch=$3} + END { if (snd - sch > 500) print "OK"; }') + check_result $? "$ts" "OK" "$prot - TXTIME rel" + done +done + +# Summary +if [ $BAD -ne 0 ]; then + echo "FAIL - $BAD/$TOTAL cases failed" + exit 1 +else + echo "OK" + exit 0 +fi -- cgit v1.2.3