selftests: udp gso with connected sockets
Connected sockets use path mtu instead of device mtu. Test this path by inserting a route mtu that is lower than the device mtu. Verify that the path mtu for the connection matches this lower number, then run the same test as in the connectionless case. Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
a160725780
commit
e5b2d91c2d
|
@ -47,6 +47,7 @@
|
||||||
|
|
||||||
static bool cfg_do_ipv4;
|
static bool cfg_do_ipv4;
|
||||||
static bool cfg_do_ipv6;
|
static bool cfg_do_ipv6;
|
||||||
|
static bool cfg_do_connected;
|
||||||
static bool cfg_do_connectionless;
|
static bool cfg_do_connectionless;
|
||||||
static bool cfg_do_setsockopt;
|
static bool cfg_do_setsockopt;
|
||||||
static int cfg_specific_test_id = -1;
|
static int cfg_specific_test_id = -1;
|
||||||
|
@ -273,6 +274,101 @@ static void set_pmtu_discover(int fd, bool is_ipv4)
|
||||||
error(1, errno, "setsockopt path mtu");
|
error(1, errno, "setsockopt path mtu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int get_path_mtu(int fd, bool is_ipv4)
|
||||||
|
{
|
||||||
|
socklen_t vallen;
|
||||||
|
unsigned int mtu;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
vallen = sizeof(mtu);
|
||||||
|
if (is_ipv4)
|
||||||
|
ret = getsockopt(fd, SOL_IP, IP_MTU, &mtu, &vallen);
|
||||||
|
else
|
||||||
|
ret = getsockopt(fd, SOL_IPV6, IPV6_MTU, &mtu, &vallen);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
error(1, errno, "getsockopt mtu");
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "path mtu (read): %u\n", mtu);
|
||||||
|
return mtu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* very wordy version of system("ip route add dev lo mtu 1500 127.0.0.3/32") */
|
||||||
|
static void set_route_mtu(int mtu, bool is_ipv4)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
|
||||||
|
struct nlmsghdr *nh;
|
||||||
|
struct rtattr *rta;
|
||||||
|
struct rtmsg *rt;
|
||||||
|
char data[NLMSG_ALIGN(sizeof(*nh)) +
|
||||||
|
NLMSG_ALIGN(sizeof(*rt)) +
|
||||||
|
NLMSG_ALIGN(RTA_LENGTH(sizeof(addr6))) +
|
||||||
|
NLMSG_ALIGN(RTA_LENGTH(sizeof(int))) +
|
||||||
|
NLMSG_ALIGN(RTA_LENGTH(0) + RTA_LENGTH(sizeof(int)))];
|
||||||
|
int fd, ret, alen, off = 0;
|
||||||
|
|
||||||
|
alen = is_ipv4 ? sizeof(addr4) : sizeof(addr6);
|
||||||
|
|
||||||
|
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||||
|
if (fd == -1)
|
||||||
|
error(1, errno, "socket netlink");
|
||||||
|
|
||||||
|
memset(data, 0, sizeof(data));
|
||||||
|
|
||||||
|
nh = (void *)data;
|
||||||
|
nh->nlmsg_type = RTM_NEWROUTE;
|
||||||
|
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||||
|
off += NLMSG_ALIGN(sizeof(*nh));
|
||||||
|
|
||||||
|
rt = (void *)(data + off);
|
||||||
|
rt->rtm_family = is_ipv4 ? AF_INET : AF_INET6;
|
||||||
|
rt->rtm_table = RT_TABLE_MAIN;
|
||||||
|
rt->rtm_dst_len = alen << 3;
|
||||||
|
rt->rtm_protocol = RTPROT_BOOT;
|
||||||
|
rt->rtm_scope = RT_SCOPE_UNIVERSE;
|
||||||
|
rt->rtm_type = RTN_UNICAST;
|
||||||
|
off += NLMSG_ALIGN(sizeof(*rt));
|
||||||
|
|
||||||
|
rta = (void *)(data + off);
|
||||||
|
rta->rta_type = RTA_DST;
|
||||||
|
rta->rta_len = RTA_LENGTH(alen);
|
||||||
|
if (is_ipv4)
|
||||||
|
memcpy(RTA_DATA(rta), &addr4, alen);
|
||||||
|
else
|
||||||
|
memcpy(RTA_DATA(rta), &addr6, alen);
|
||||||
|
off += NLMSG_ALIGN(rta->rta_len);
|
||||||
|
|
||||||
|
rta = (void *)(data + off);
|
||||||
|
rta->rta_type = RTA_OIF;
|
||||||
|
rta->rta_len = RTA_LENGTH(sizeof(int));
|
||||||
|
*((int *)(RTA_DATA(rta))) = 1; //if_nametoindex("lo");
|
||||||
|
off += NLMSG_ALIGN(rta->rta_len);
|
||||||
|
|
||||||
|
/* MTU is a subtype in a metrics type */
|
||||||
|
rta = (void *)(data + off);
|
||||||
|
rta->rta_type = RTA_METRICS;
|
||||||
|
rta->rta_len = RTA_LENGTH(0) + RTA_LENGTH(sizeof(int));
|
||||||
|
off += NLMSG_ALIGN(rta->rta_len);
|
||||||
|
|
||||||
|
/* now fill MTU subtype. Note that it fits within above rta_len */
|
||||||
|
rta = (void *)(((char *) rta) + RTA_LENGTH(0));
|
||||||
|
rta->rta_type = RTAX_MTU;
|
||||||
|
rta->rta_len = RTA_LENGTH(sizeof(int));
|
||||||
|
*((int *)(RTA_DATA(rta))) = mtu;
|
||||||
|
|
||||||
|
nh->nlmsg_len = off;
|
||||||
|
|
||||||
|
ret = sendto(fd, data, off, 0, (void *)&nladdr, sizeof(nladdr));
|
||||||
|
if (ret != off)
|
||||||
|
error(1, errno, "send netlink: %uB != %uB\n", ret, off);
|
||||||
|
|
||||||
|
if (close(fd))
|
||||||
|
error(1, errno, "close netlink");
|
||||||
|
|
||||||
|
fprintf(stderr, "route mtu (test): %u\n", mtu);
|
||||||
|
}
|
||||||
|
|
||||||
static bool send_one(int fd, int len, int gso_len,
|
static bool send_one(int fd, int len, int gso_len,
|
||||||
struct sockaddr *addr, socklen_t alen)
|
struct sockaddr *addr, socklen_t alen)
|
||||||
{
|
{
|
||||||
|
@ -391,7 +487,7 @@ static void run_all(int fdt, int fdr, struct sockaddr *addr, socklen_t alen)
|
||||||
static void run_test(struct sockaddr *addr, socklen_t alen)
|
static void run_test(struct sockaddr *addr, socklen_t alen)
|
||||||
{
|
{
|
||||||
struct timeval tv = { .tv_usec = 100 * 1000 };
|
struct timeval tv = { .tv_usec = 100 * 1000 };
|
||||||
int fdr, fdt;
|
int fdr, fdt, val;
|
||||||
|
|
||||||
fdr = socket(addr->sa_family, SOCK_DGRAM, 0);
|
fdr = socket(addr->sa_family, SOCK_DGRAM, 0);
|
||||||
if (fdr == -1)
|
if (fdr == -1)
|
||||||
|
@ -416,6 +512,20 @@ static void run_test(struct sockaddr *addr, socklen_t alen)
|
||||||
run_all(fdt, fdr, addr, alen);
|
run_all(fdt, fdr, addr, alen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cfg_do_connected) {
|
||||||
|
set_device_mtu(fdt, CONST_MTU_TEST + 100);
|
||||||
|
set_route_mtu(CONST_MTU_TEST, addr->sa_family == AF_INET);
|
||||||
|
|
||||||
|
if (connect(fdt, addr, alen))
|
||||||
|
error(1, errno, "connect");
|
||||||
|
|
||||||
|
val = get_path_mtu(fdt, addr->sa_family == AF_INET);
|
||||||
|
if (val != CONST_MTU_TEST)
|
||||||
|
error(1, 0, "bad path mtu %u\n", val);
|
||||||
|
|
||||||
|
run_all(fdt, fdr, addr, 0 /* use connected addr */);
|
||||||
|
}
|
||||||
|
|
||||||
if (close(fdt))
|
if (close(fdt))
|
||||||
error(1, errno, "close t");
|
error(1, errno, "close t");
|
||||||
if (close(fdr))
|
if (close(fdr))
|
||||||
|
@ -448,7 +558,7 @@ static void parse_opts(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "46Cst:")) != -1) {
|
while ((c = getopt(argc, argv, "46cCst:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '4':
|
case '4':
|
||||||
cfg_do_ipv4 = true;
|
cfg_do_ipv4 = true;
|
||||||
|
@ -456,6 +566,9 @@ static void parse_opts(int argc, char **argv)
|
||||||
case '6':
|
case '6':
|
||||||
cfg_do_ipv6 = true;
|
cfg_do_ipv6 = true;
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
cfg_do_connected = true;
|
||||||
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
cfg_do_connectionless = true;
|
cfg_do_connectionless = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -14,3 +14,10 @@ echo "ipv6 cmsg"
|
||||||
|
|
||||||
echo "ipv6 setsockopt"
|
echo "ipv6 setsockopt"
|
||||||
./in_netns.sh ./udpgso -6 -C -s
|
./in_netns.sh ./udpgso -6 -C -s
|
||||||
|
|
||||||
|
echo "ipv4 connected"
|
||||||
|
./in_netns.sh ./udpgso -4 -c
|
||||||
|
|
||||||
|
# blocked on 2nd loopback address
|
||||||
|
# echo "ipv6 connected"
|
||||||
|
# ./in_netns.sh ./udpgso -6 -c
|
||||||
|
|
Loading…
Reference in New Issue