Skip to content

Commit

Permalink
ping: support ipv6 ping socket flow labels
Browse files Browse the repository at this point in the history
Ping sockets don't appear to make any attempt to preserve flow labels
created and set by userspace using IPV6_FLOWINFO_SEND. Instead they are
clobbered by autolabels (if enabled) or zero.

Grab the flowlabel out of the msghdr similar to how rawv6_sendmsg does
it and move the memset up so it doesn't get zeroed after.

Signed-off-by: Alan Brady <alan.brady@intel.com>
Tested-by: Gurucharan <gurucharanx.g@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Alan Brady authored and David S. Miller committed Jul 22, 2022
1 parent c497885 commit 16576a0
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 16 deletions.
6 changes: 5 additions & 1 deletion net/ipv6/ping.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
if (err)
return err;

memset(&fl6, 0, sizeof(fl6));

if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_in6 *, u, msg->msg_name);
if (msg->msg_namelen < sizeof(*u))
Expand All @@ -72,12 +74,15 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
return -EAFNOSUPPORT;
}
daddr = &(u->sin6_addr);
if (np->sndflow)
fl6.flowlabel = u->sin6_flowinfo & IPV6_FLOWINFO_MASK;
if (__ipv6_addr_needs_scope_id(ipv6_addr_type(daddr)))
oif = u->sin6_scope_id;
} else {
if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ;
daddr = &sk->sk_v6_daddr;
fl6.flowlabel = np->flow_label;
}

if (!oif)
Expand All @@ -101,7 +106,6 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
ipc6.sockc.tsflags = sk->sk_tsflags;
ipc6.sockc.mark = sk->sk_mark;

memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_oif = oif;

if (msg->msg_controllen) {
Expand Down
75 changes: 60 additions & 15 deletions tools/testing/selftests/net/ipv6_flowlabel.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/icmpv6.h>
#include <linux/in6.h>
#include <stdbool.h>
#include <stdio.h>
Expand All @@ -29,26 +30,48 @@
#ifndef IPV6_FLOWLABEL_MGR
#define IPV6_FLOWLABEL_MGR 32
#endif
#ifndef IPV6_FLOWINFO_SEND
#define IPV6_FLOWINFO_SEND 33
#endif

#define FLOWLABEL_WILDCARD ((uint32_t) -1)

static const char cfg_data[] = "a";
static uint32_t cfg_label = 1;
static bool use_ping;
static bool use_flowinfo_send;

static struct icmp6hdr icmp6 = {
.icmp6_type = ICMPV6_ECHO_REQUEST
};

static struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
};

static void do_send(int fd, bool with_flowlabel, uint32_t flowlabel)
{
char control[CMSG_SPACE(sizeof(flowlabel))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
struct iovec iov = {
.iov_base = (char *)cfg_data,
.iov_len = sizeof(cfg_data)
};
int ret;

iov.iov_base = (char *)cfg_data;
iov.iov_len = sizeof(cfg_data);
if (use_ping) {
iov.iov_base = &icmp6;
iov.iov_len = sizeof(icmp6);
}

msg.msg_iov = &iov;
msg.msg_iovlen = 1;

if (with_flowlabel) {
if (use_flowinfo_send) {
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
} else if (with_flowlabel) {
struct cmsghdr *cm;

cm = (void *)control;
Expand Down Expand Up @@ -94,13 +117,16 @@ static void do_recv(int fd, bool with_flowlabel, uint32_t expect)
ret = recvmsg(fd, &msg, 0);
if (ret == -1)
error(1, errno, "recv");
if (use_ping)
goto parse_cmsg;
if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
error(1, 0, "recv: truncated");
if (ret != sizeof(cfg_data))
error(1, 0, "recv: length mismatch");
if (memcmp(data, cfg_data, sizeof(data)))
error(1, 0, "recv: data mismatch");

parse_cmsg:
cm = CMSG_FIRSTHDR(&msg);
if (with_flowlabel) {
if (!cm)
Expand All @@ -114,9 +140,11 @@ static void do_recv(int fd, bool with_flowlabel, uint32_t expect)
flowlabel = ntohl(*(uint32_t *)CMSG_DATA(cm));
fprintf(stderr, "recv with label %u\n", flowlabel);

if (expect != FLOWLABEL_WILDCARD && expect != flowlabel)
if (expect != FLOWLABEL_WILDCARD && expect != flowlabel) {
fprintf(stderr, "recv: incorrect flowlabel %u != %u\n",
flowlabel, expect);
error(1, 0, "recv: flowlabel is wrong");
}

} else {
fprintf(stderr, "recv without label\n");
Expand Down Expand Up @@ -165,11 +193,17 @@ static void parse_opts(int argc, char **argv)
{
int c;

while ((c = getopt(argc, argv, "l:")) != -1) {
while ((c = getopt(argc, argv, "l:ps")) != -1) {
switch (c) {
case 'l':
cfg_label = strtoul(optarg, NULL, 0);
break;
case 'p':
use_ping = true;
break;
case 's':
use_flowinfo_send = true;
break;
default:
error(1, 0, "%s: parse error", argv[0]);
}
Expand All @@ -178,27 +212,30 @@ static void parse_opts(int argc, char **argv)

int main(int argc, char **argv)
{
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_port = htons(8000),
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
};
const int one = 1;
int fdt, fdr;
int prot = 0;

addr.sin6_port = htons(8000);

parse_opts(argc, argv);

fdt = socket(PF_INET6, SOCK_DGRAM, 0);
if (use_ping) {
fprintf(stderr, "attempting to use ping sockets\n");
prot = IPPROTO_ICMPV6;
}

fdt = socket(PF_INET6, SOCK_DGRAM, prot);
if (fdt == -1)
error(1, errno, "socket t");

fdr = socket(PF_INET6, SOCK_DGRAM, 0);
fdr = use_ping ? fdt : socket(PF_INET6, SOCK_DGRAM, 0);
if (fdr == -1)
error(1, errno, "socket r");

if (connect(fdt, (void *)&addr, sizeof(addr)))
error(1, errno, "connect");
if (bind(fdr, (void *)&addr, sizeof(addr)))
if (!use_ping && bind(fdr, (void *)&addr, sizeof(addr)))
error(1, errno, "bind");

flowlabel_get(fdt, cfg_label, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE);
Expand All @@ -216,13 +253,21 @@ int main(int argc, char **argv)
do_recv(fdr, false, 0);
}

if (use_flowinfo_send) {
fprintf(stderr, "using IPV6_FLOWINFO_SEND to send label\n");
addr.sin6_flowinfo = htonl(cfg_label);
if (setsockopt(fdt, SOL_IPV6, IPV6_FLOWINFO_SEND, &one,
sizeof(one)) == -1)
error(1, errno, "setsockopt flowinfo_send");
}

fprintf(stderr, "send label\n");
do_send(fdt, true, cfg_label);
do_recv(fdr, true, cfg_label);

if (close(fdr))
error(1, errno, "close r");
if (close(fdt))
if (!use_ping && close(fdt))
error(1, errno, "close t");

return 0;
Expand Down
16 changes: 16 additions & 0 deletions tools/testing/selftests/net/ipv6_flowlabel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@ echo "TEST datapath (with auto-flowlabels)"
./in_netns.sh \
sh -c 'sysctl -q -w net.ipv6.auto_flowlabels=1 && ./ipv6_flowlabel -l 1'

echo "TEST datapath (with ping-sockets)"
./in_netns.sh \
sh -c 'sysctl -q -w net.ipv6.flowlabel_reflect=4 && \
sysctl -q -w net.ipv4.ping_group_range="0 2147483647" && \
./ipv6_flowlabel -l 1 -p'

echo "TEST datapath (with flowinfo-send)"
./in_netns.sh \
sh -c './ipv6_flowlabel -l 1 -s'

echo "TEST datapath (with ping-sockets flowinfo-send)"
./in_netns.sh \
sh -c 'sysctl -q -w net.ipv6.flowlabel_reflect=4 && \
sysctl -q -w net.ipv4.ping_group_range="0 2147483647" && \
./ipv6_flowlabel -l 1 -p -s'

echo OK. All tests passed

0 comments on commit 16576a0

Please sign in to comment.