aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/net/cmsg_sender.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/net/cmsg_sender.c')
-rw-r--r--tools/testing/selftests/net/cmsg_sender.c172
1 files changed, 148 insertions, 24 deletions
diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c
index 24444dc72543..aed7845c08a8 100644
--- a/tools/testing/selftests/net/cmsg_sender.c
+++ b/tools/testing/selftests/net/cmsg_sender.c
@@ -33,22 +33,28 @@ enum {
ERN_CMSG_RCV,
};
+struct option_cmsg_u32 {
+ bool ena;
+ unsigned int val;
+};
+
struct options {
bool silent_send;
const char *host;
const char *service;
+ unsigned int size;
struct {
unsigned int mark;
+ unsigned int dontfrag;
+ unsigned int tclass;
+ unsigned int hlimit;
} sockopt;
struct {
unsigned int family;
unsigned int type;
unsigned int proto;
} sock;
- struct {
- bool ena;
- unsigned int val;
- } mark;
+ struct option_cmsg_u32 mark;
struct {
bool ena;
unsigned int delay;
@@ -56,7 +62,14 @@ struct options {
struct {
bool ena;
} ts;
+ struct {
+ struct option_cmsg_u32 dontfrag;
+ struct option_cmsg_u32 tclass;
+ struct option_cmsg_u32 hlimit;
+ struct option_cmsg_u32 exthdr;
+ } v6;
} opt = {
+ .size = 13,
.sock = {
.family = AF_UNSPEC,
.type = SOCK_DGRAM,
@@ -72,6 +85,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin);
printf("Options:\n"
"\t\t-s Silent send() failures\n"
+ "\t\t-S send() size\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"
@@ -80,6 +94,14 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
"\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"
+ "\t\t-f val Set don't fragment via cmsg\n"
+ "\t\t-F val Set don't fragment via setsockopt\n"
+ "\t\t-c val Set TCLASS via cmsg\n"
+ "\t\t-C val Set TCLASS via setsockopt\n"
+ "\t\t-l val Set HOPLIMIT via cmsg\n"
+ "\t\t-L val Set HOPLIMIT via setsockopt\n"
+ "\t\t-H type Add an IPv6 header option\n"
+ "\t\t (h = HOP; d = DST; r = RTDST)"
"");
exit(ERN_HELP);
}
@@ -88,11 +110,14 @@ static void cs_parse_args(int argc, char *argv[])
{
char o;
- while ((o = getopt(argc, argv, "46sp:m:M:d:t")) != -1) {
+ while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:c:C:l:L:H:")) != -1) {
switch (o) {
case 's':
opt.silent_send = true;
break;
+ case 'S':
+ opt.size = atoi(optarg);
+ break;
case '4':
opt.sock.family = AF_INET;
break;
@@ -126,6 +151,44 @@ static void cs_parse_args(int argc, char *argv[])
case 't':
opt.ts.ena = true;
break;
+ case 'f':
+ opt.v6.dontfrag.ena = true;
+ opt.v6.dontfrag.val = atoi(optarg);
+ break;
+ case 'F':
+ opt.sockopt.dontfrag = atoi(optarg);
+ break;
+ case 'c':
+ opt.v6.tclass.ena = true;
+ opt.v6.tclass.val = atoi(optarg);
+ break;
+ case 'C':
+ opt.sockopt.tclass = atoi(optarg);
+ break;
+ case 'l':
+ opt.v6.hlimit.ena = true;
+ opt.v6.hlimit.val = atoi(optarg);
+ break;
+ case 'L':
+ opt.sockopt.hlimit = atoi(optarg);
+ break;
+ case 'H':
+ opt.v6.exthdr.ena = true;
+ switch (optarg[0]) {
+ case 'h':
+ opt.v6.exthdr.val = IPV6_HOPOPTS;
+ break;
+ case 'd':
+ opt.v6.exthdr.val = IPV6_DSTOPTS;
+ break;
+ case 'r':
+ opt.v6.exthdr.val = IPV6_RTHDRDSTOPTS;
+ break;
+ default:
+ printf("Error: hdr type: %s\n", optarg);
+ break;
+ }
+ break;
}
}
@@ -136,6 +199,38 @@ static void cs_parse_args(int argc, char *argv[])
opt.service = argv[optind + 1];
}
+static void memrnd(void *s, size_t n)
+{
+ int *dword = s;
+ char *byte;
+
+ for (; n >= 4; n -= 4)
+ *dword++ = rand();
+ byte = (void *)dword;
+ while (n--)
+ *byte++ = rand();
+}
+
+static void
+ca_write_cmsg_u32(char *cbuf, size_t cbuf_sz, size_t *cmsg_len,
+ int level, int optname, struct option_cmsg_u32 *uopt)
+{
+ struct cmsghdr *cmsg;
+
+ if (!uopt->ena)
+ return;
+
+ 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 = level;
+ cmsg->cmsg_type = optname;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
+ *(__u32 *)CMSG_DATA(cmsg) = uopt->val;
+}
+
static void
cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
{
@@ -145,17 +240,15 @@ cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
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");
+ ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+ SOL_SOCKET, SO_MARK, &opt.mark);
+ ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+ SOL_IPV6, IPV6_DONTFRAG, &opt.v6.dontfrag);
+ ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+ SOL_IPV6, IPV6_TCLASS, &opt.v6.tclass);
+ ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+ SOL_IPV6, IPV6_HOPLIMIT, &opt.v6.hlimit);
- 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 (opt.txtime.ena) {
struct sock_txtime so_txtime = {
.clockid = CLOCK_MONOTONIC,
@@ -199,6 +292,17 @@ cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
*(__u32 *)CMSG_DATA(cmsg) = SOF_TIMESTAMPING_TX_SCHED |
SOF_TIMESTAMPING_TX_SOFTWARE;
}
+ if (opt.v6.exthdr.ena) {
+ cmsg = (struct cmsghdr *)(cbuf + cmsg_len);
+ cmsg_len += CMSG_SPACE(8);
+ if (cbuf_sz < cmsg_len)
+ error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
+
+ cmsg->cmsg_level = SOL_IPV6;
+ cmsg->cmsg_type = opt.v6.exthdr.val;
+ cmsg->cmsg_len = CMSG_LEN(8);
+ *(__u64 *)CMSG_DATA(cmsg) = 0;
+ }
if (cmsg_len)
msg->msg_controllen = cmsg_len;
@@ -286,18 +390,41 @@ cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
}
}
+static void ca_set_sockopts(int fd)
+{
+ if (opt.sockopt.mark &&
+ setsockopt(fd, SOL_SOCKET, SO_MARK,
+ &opt.sockopt.mark, sizeof(opt.sockopt.mark)))
+ error(ERN_SOCKOPT, errno, "setsockopt SO_MARK");
+ if (opt.sockopt.dontfrag &&
+ setsockopt(fd, SOL_IPV6, IPV6_DONTFRAG,
+ &opt.sockopt.dontfrag, sizeof(opt.sockopt.dontfrag)))
+ error(ERN_SOCKOPT, errno, "setsockopt IPV6_DONTFRAG");
+ if (opt.sockopt.tclass &&
+ setsockopt(fd, SOL_IPV6, IPV6_TCLASS,
+ &opt.sockopt.tclass, sizeof(opt.sockopt.tclass)))
+ error(ERN_SOCKOPT, errno, "setsockopt IPV6_TCLASS");
+ if (opt.sockopt.hlimit &&
+ setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS,
+ &opt.sockopt.hlimit, sizeof(opt.sockopt.hlimit)))
+ error(ERN_SOCKOPT, errno, "setsockopt IPV6_HOPLIMIT");
+}
+
int main(int argc, char *argv[])
{
- char buf[] = "blablablabla";
struct addrinfo hints, *ai;
struct iovec iov[1];
struct msghdr msg;
char cbuf[1024];
+ char *buf;
int err;
int fd;
cs_parse_args(argc, argv);
+ buf = malloc(opt.size);
+ memrnd(buf, opt.size);
+
memset(&hints, 0, sizeof(hints));
hints.ai_family = opt.sock.family;
@@ -326,25 +453,22 @@ int main(int argc, char *argv[])
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 udphdr hdr = { 1, 2, htons(opt.size), 0 };
struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;;
memcpy(buf, &hdr, sizeof(hdr));
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");
+ ca_set_sockopts(fd);
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");
+ error(ERN_GETTIME, errno, "gettime MONOTONIC");
iov[0].iov_base = buf;
- iov[0].iov_len = sizeof(buf);
+ iov[0].iov_len = opt.size;
memset(&msg, 0, sizeof(msg));
msg.msg_name = ai->ai_addr;
@@ -360,7 +484,7 @@ int main(int argc, char *argv[])
fprintf(stderr, "send failed: %s\n", strerror(errno));
err = ERN_SEND;
goto err_out;
- } else if (err != sizeof(buf)) {
+ } else if (err != (int)opt.size) {
fprintf(stderr, "short send\n");
err = ERN_SEND_SHORT;
goto err_out;