forked from Minki/linux
selftests: add PM netlink functional tests
This introduces basic self-tests for the PM netlink, checking the basic APIs and possible exceptional values. Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
01cacb00b3
commit
eedbc68532
1
tools/testing/selftests/net/mptcp/.gitignore
vendored
1
tools/testing/selftests/net/mptcp/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
mptcp_connect
|
||||
pm_nl_ctl
|
||||
*.pcap
|
||||
|
@ -1,12 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
top_srcdir = ../../../../..
|
||||
KSFT_KHDR_INSTALL := 1
|
||||
|
||||
CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
|
||||
CFLAGS = -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include
|
||||
|
||||
TEST_PROGS := mptcp_connect.sh
|
||||
TEST_PROGS := mptcp_connect.sh pm_netlink.sh
|
||||
|
||||
TEST_GEN_FILES = mptcp_connect
|
||||
TEST_GEN_FILES = mptcp_connect pm_nl_ctl
|
||||
|
||||
TEST_FILES := settings
|
||||
|
||||
|
130
tools/testing/selftests/net/mptcp/pm_netlink.sh
Executable file
130
tools/testing/selftests/net/mptcp/pm_netlink.sh
Executable file
@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ksft_skip=4
|
||||
ret=0
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [ -h ]"
|
||||
}
|
||||
|
||||
|
||||
while getopts "$optstring" option;do
|
||||
case "$option" in
|
||||
"h")
|
||||
usage $0
|
||||
exit 0
|
||||
;;
|
||||
"?")
|
||||
usage $0
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
sec=$(date +%s)
|
||||
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
|
||||
ns1="ns1-$rndh"
|
||||
err=$(mktemp)
|
||||
ret=0
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -f $out
|
||||
ip netns del $ns1
|
||||
}
|
||||
|
||||
ip -Version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without ip tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
ip netns add $ns1 || exit $ksft_skip
|
||||
ip -net $ns1 link set lo up
|
||||
ip netns exec $ns1 sysctl -q net.mptcp.enabled=1
|
||||
|
||||
check()
|
||||
{
|
||||
local cmd="$1"
|
||||
local expected="$2"
|
||||
local msg="$3"
|
||||
local out=`$cmd 2>$err`
|
||||
local cmd_ret=$?
|
||||
|
||||
printf "%-50s %s" "$msg"
|
||||
if [ $cmd_ret -ne 0 ]; then
|
||||
echo "[FAIL] command execution '$cmd' stderr "
|
||||
cat $err
|
||||
ret=1
|
||||
elif [ "$out" = "$expected" ]; then
|
||||
echo "[ OK ]"
|
||||
else
|
||||
echo -n "[FAIL] "
|
||||
echo "expected '$expected' got '$out'"
|
||||
ret=1
|
||||
fi
|
||||
}
|
||||
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list"
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
|
||||
subflows 0" "defaults limits"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow dev lo
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3 flags signal,backup
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 1" "id 1 flags 10.0.1.1 " "simple add/get addr"
|
||||
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl dump" \
|
||||
"id 1 flags 10.0.1.1
|
||||
id 2 flags subflow dev lo 10.0.1.2
|
||||
id 3 flags signal,backup 10.0.1.3 " "dump addrs"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl del 2
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 2" "" "simple del addr"
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl dump" \
|
||||
"id 1 flags 10.0.1.1
|
||||
id 3 flags signal,backup 10.0.1.3 " "dump addrs after del"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 4" "" "duplicate addr"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 id 10 flags signal
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 4" "id 4 flags signal 10.0.1.4 " "id addr increment"
|
||||
|
||||
for i in `seq 5 9`; do
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.$i flags signal >/dev/null 2>&1
|
||||
done
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 9" "id 9 flags signal 10.0.1.9 " "hard addr limit"
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl get 10" "" "above hard addr limit"
|
||||
|
||||
for i in `seq 9 256`; do
|
||||
ip netns exec $ns1 ./pm_nl_ctl del $i
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9
|
||||
done
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags 10.0.1.1
|
||||
id 3 flags signal,backup 10.0.1.3
|
||||
id 4 flags signal 10.0.1.4
|
||||
id 5 flags signal 10.0.1.5
|
||||
id 6 flags signal 10.0.1.6
|
||||
id 7 flags signal 10.0.1.7
|
||||
id 8 flags signal 10.0.1.8 " "id limit"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl flush
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 9 1
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
|
||||
subflows 0" "rcv addrs above hard limit"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 1 9
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
|
||||
subflows 0" "subflows above hard limit"
|
||||
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 8 8
|
||||
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8
|
||||
subflows 8" "set limits"
|
||||
|
||||
exit $ret
|
616
tools/testing/selftests/net/mptcp/pm_nl_ctl.c
Normal file
616
tools/testing/selftests/net/mptcp/pm_nl_ctl.c
Normal file
@ -0,0 +1,616 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#include "linux/mptcp.h"
|
||||
|
||||
#ifndef MPTCP_PM_NAME
|
||||
#define MPTCP_PM_NAME "mptcp_pm"
|
||||
#endif
|
||||
|
||||
static void syntax(char *argv[])
|
||||
{
|
||||
fprintf(stderr, "%s add|get|del|flush|dump|accept [<args>]\n", argv[0]);
|
||||
fprintf(stderr, "\tadd [flags signal|subflow|backup] [id <nr>] [dev <name>] <ip>\n");
|
||||
fprintf(stderr, "\tdel <id>\n");
|
||||
fprintf(stderr, "\tget <id>\n");
|
||||
fprintf(stderr, "\tflush\n");
|
||||
fprintf(stderr, "\tdump\n");
|
||||
fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int init_genl_req(char *data, int family, int cmd, int version)
|
||||
{
|
||||
struct nlmsghdr *nh = (void *)data;
|
||||
struct genlmsghdr *gh;
|
||||
int off = 0;
|
||||
|
||||
nh->nlmsg_type = family;
|
||||
nh->nlmsg_flags = NLM_F_REQUEST;
|
||||
nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
|
||||
off += NLMSG_ALIGN(sizeof(*nh));
|
||||
|
||||
gh = (void *)(data + off);
|
||||
gh->cmd = cmd;
|
||||
gh->version = version;
|
||||
off += NLMSG_ALIGN(sizeof(*gh));
|
||||
return off;
|
||||
}
|
||||
|
||||
static void nl_error(struct nlmsghdr *nh)
|
||||
{
|
||||
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh);
|
||||
int len = nh->nlmsg_len - sizeof(*nh);
|
||||
uint32_t off;
|
||||
|
||||
if (len < sizeof(struct nlmsgerr))
|
||||
error(1, 0, "netlink error message truncated %d min %ld", len,
|
||||
sizeof(struct nlmsgerr));
|
||||
|
||||
if (!err->error) {
|
||||
/* check messages from kernel */
|
||||
struct rtattr *attrs = (struct rtattr *)NLMSG_DATA(nh);
|
||||
|
||||
while (RTA_OK(attrs, len)) {
|
||||
if (attrs->rta_type == NLMSGERR_ATTR_MSG)
|
||||
fprintf(stderr, "netlink ext ack msg: %s\n",
|
||||
(char *)RTA_DATA(attrs));
|
||||
if (attrs->rta_type == NLMSGERR_ATTR_OFFS) {
|
||||
memcpy(&off, RTA_DATA(attrs), 4);
|
||||
fprintf(stderr, "netlink err off %d\n",
|
||||
(int)off);
|
||||
}
|
||||
attrs = RTA_NEXT(attrs, len);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "netlink error %d", err->error);
|
||||
}
|
||||
}
|
||||
|
||||
/* do a netlink command and, if max > 0, fetch the reply */
|
||||
static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max)
|
||||
{
|
||||
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
|
||||
socklen_t addr_len;
|
||||
void *data = nh;
|
||||
int rem, ret;
|
||||
int err = 0;
|
||||
|
||||
nh->nlmsg_len = len;
|
||||
ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr));
|
||||
if (ret != len)
|
||||
error(1, errno, "send netlink: %uB != %uB\n", ret, len);
|
||||
if (max == 0)
|
||||
return 0;
|
||||
|
||||
addr_len = sizeof(nladdr);
|
||||
rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len);
|
||||
if (ret < 0)
|
||||
error(1, errno, "recv netlink: %uB\n", ret);
|
||||
|
||||
/* Beware: the NLMSG_NEXT macro updates the 'rem' argument */
|
||||
for (; NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) {
|
||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
||||
nl_error(nh);
|
||||
err = 1;
|
||||
}
|
||||
}
|
||||
if (err)
|
||||
error(1, 0, "bailing out due to netlink error[s]");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int genl_parse_getfamily(struct nlmsghdr *nlh)
|
||||
{
|
||||
struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
|
||||
int len = nlh->nlmsg_len;
|
||||
struct rtattr *attrs;
|
||||
|
||||
if (nlh->nlmsg_type != GENL_ID_CTRL)
|
||||
error(1, errno, "Not a controller message, len=%d type=0x%x\n",
|
||||
nlh->nlmsg_len, nlh->nlmsg_type);
|
||||
|
||||
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||
|
||||
if (len < 0)
|
||||
error(1, errno, "wrong controller message len %d\n", len);
|
||||
|
||||
if (ghdr->cmd != CTRL_CMD_NEWFAMILY)
|
||||
error(1, errno, "Unknown controller command %d\n", ghdr->cmd);
|
||||
|
||||
attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
|
||||
while (RTA_OK(attrs, len)) {
|
||||
if (attrs->rta_type == CTRL_ATTR_FAMILY_ID)
|
||||
return *(__u16 *)RTA_DATA(attrs);
|
||||
attrs = RTA_NEXT(attrs, len);
|
||||
}
|
||||
|
||||
error(1, errno, "can't find CTRL_ATTR_FAMILY_ID attr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int resolve_mptcp_pm_netlink(int fd)
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
struct nlmsghdr *nh;
|
||||
struct rtattr *rta;
|
||||
int namelen;
|
||||
int off = 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0);
|
||||
|
||||
rta = (void *)(data + off);
|
||||
namelen = strlen(MPTCP_PM_NAME) + 1;
|
||||
rta->rta_type = CTRL_ATTR_FAMILY_NAME;
|
||||
rta->rta_len = RTA_LENGTH(namelen);
|
||||
memcpy(RTA_DATA(rta), MPTCP_PM_NAME, namelen);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
|
||||
do_nl_req(fd, nh, off, sizeof(data));
|
||||
return genl_parse_getfamily((void *)data);
|
||||
}
|
||||
|
||||
int add_addr(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
struct rtattr *rta, *nest;
|
||||
struct nlmsghdr *nh;
|
||||
u_int16_t family;
|
||||
u_int32_t flags;
|
||||
int nest_start;
|
||||
u_int8_t id;
|
||||
int off = 0;
|
||||
int arg;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ADD_ADDR,
|
||||
MPTCP_PM_VER);
|
||||
|
||||
if (argc < 3)
|
||||
syntax(argv);
|
||||
|
||||
nest_start = off;
|
||||
nest = (void *)(data + off);
|
||||
nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
|
||||
nest->rta_len = RTA_LENGTH(0);
|
||||
off += NLMSG_ALIGN(nest->rta_len);
|
||||
|
||||
/* addr data */
|
||||
rta = (void *)(data + off);
|
||||
if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) {
|
||||
family = AF_INET;
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
|
||||
rta->rta_len = RTA_LENGTH(4);
|
||||
} else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) {
|
||||
family = AF_INET6;
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
|
||||
rta->rta_len = RTA_LENGTH(16);
|
||||
} else
|
||||
error(1, errno, "can't parse ip %s", argv[2]);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
|
||||
/* family */
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
|
||||
rta->rta_len = RTA_LENGTH(2);
|
||||
memcpy(RTA_DATA(rta), &family, 2);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
|
||||
for (arg = 3; arg < argc; arg++) {
|
||||
if (!strcmp(argv[arg], "flags")) {
|
||||
char *tok, *str;
|
||||
|
||||
/* flags */
|
||||
flags = 0;
|
||||
if (++arg >= argc)
|
||||
error(1, 0, " missing flags value");
|
||||
|
||||
/* do not support flag list yet */
|
||||
for (str = argv[arg]; (tok = strtok(str, ","));
|
||||
str = NULL) {
|
||||
if (!strcmp(tok, "subflow"))
|
||||
flags |= MPTCP_PM_ADDR_FLAG_SUBFLOW;
|
||||
else if (!strcmp(tok, "signal"))
|
||||
flags |= MPTCP_PM_ADDR_FLAG_SIGNAL;
|
||||
else if (!strcmp(tok, "backup"))
|
||||
flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
|
||||
else
|
||||
error(1, errno,
|
||||
"unknown flag %s", argv[arg]);
|
||||
}
|
||||
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS;
|
||||
rta->rta_len = RTA_LENGTH(4);
|
||||
memcpy(RTA_DATA(rta), &flags, 4);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
} else if (!strcmp(argv[arg], "id")) {
|
||||
if (++arg >= argc)
|
||||
error(1, 0, " missing id value");
|
||||
|
||||
id = atoi(argv[arg]);
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
|
||||
rta->rta_len = RTA_LENGTH(1);
|
||||
memcpy(RTA_DATA(rta), &id, 1);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
} else if (!strcmp(argv[arg], "dev")) {
|
||||
int32_t ifindex;
|
||||
|
||||
if (++arg >= argc)
|
||||
error(1, 0, " missing dev name");
|
||||
|
||||
ifindex = if_nametoindex(argv[arg]);
|
||||
if (!ifindex)
|
||||
error(1, errno, "unknown device %s", argv[arg]);
|
||||
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX;
|
||||
rta->rta_len = RTA_LENGTH(4);
|
||||
memcpy(RTA_DATA(rta), &ifindex, 4);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
} else
|
||||
error(1, 0, "unknown keyword %s", argv[arg]);
|
||||
}
|
||||
nest->rta_len = off - nest_start;
|
||||
|
||||
do_nl_req(fd, nh, off, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int del_addr(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
struct rtattr *rta, *nest;
|
||||
struct nlmsghdr *nh;
|
||||
int nest_start;
|
||||
u_int8_t id;
|
||||
int off = 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_DEL_ADDR,
|
||||
MPTCP_PM_VER);
|
||||
|
||||
/* the only argument is the address id */
|
||||
if (argc != 3)
|
||||
syntax(argv);
|
||||
|
||||
id = atoi(argv[2]);
|
||||
|
||||
nest_start = off;
|
||||
nest = (void *)(data + off);
|
||||
nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
|
||||
nest->rta_len = RTA_LENGTH(0);
|
||||
off += NLMSG_ALIGN(nest->rta_len);
|
||||
|
||||
/* build a dummy addr with only the ID set */
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
|
||||
rta->rta_len = RTA_LENGTH(1);
|
||||
memcpy(RTA_DATA(rta), &id, 1);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
nest->rta_len = off - nest_start;
|
||||
|
||||
do_nl_req(fd, nh, off, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_addr(struct rtattr *attrs, int len)
|
||||
{
|
||||
uint16_t family = 0;
|
||||
char str[1024];
|
||||
uint32_t flags;
|
||||
uint8_t id;
|
||||
|
||||
while (RTA_OK(attrs, len)) {
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FAMILY)
|
||||
memcpy(&family, RTA_DATA(attrs), 2);
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR4) {
|
||||
if (family != AF_INET)
|
||||
error(1, errno, "wrong IP (v4) for family %d",
|
||||
family);
|
||||
inet_ntop(AF_INET, RTA_DATA(attrs), str, sizeof(str));
|
||||
printf("%s ", str);
|
||||
}
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR6) {
|
||||
if (family != AF_INET6)
|
||||
error(1, errno, "wrong IP (v6) for family %d",
|
||||
family);
|
||||
inet_ntop(AF_INET6, RTA_DATA(attrs), str, sizeof(str));
|
||||
printf("%s ", str);
|
||||
}
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ID) {
|
||||
memcpy(&id, RTA_DATA(attrs), 1);
|
||||
printf("id %d ", id);
|
||||
}
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FLAGS) {
|
||||
memcpy(&flags, RTA_DATA(attrs), 4);
|
||||
|
||||
printf("flags ");
|
||||
if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) {
|
||||
printf("signal");
|
||||
flags &= ~MPTCP_PM_ADDR_FLAG_SIGNAL;
|
||||
if (flags)
|
||||
printf(",");
|
||||
}
|
||||
|
||||
if (flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) {
|
||||
printf("subflow");
|
||||
flags &= ~MPTCP_PM_ADDR_FLAG_SUBFLOW;
|
||||
if (flags)
|
||||
printf(",");
|
||||
}
|
||||
|
||||
if (flags & MPTCP_PM_ADDR_FLAG_BACKUP) {
|
||||
printf("backup");
|
||||
flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP;
|
||||
if (flags)
|
||||
printf(",");
|
||||
}
|
||||
|
||||
/* bump unknown flags, if any */
|
||||
if (flags)
|
||||
printf("0x%x", flags);
|
||||
printf(" ");
|
||||
}
|
||||
if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_IF_IDX) {
|
||||
char name[IF_NAMESIZE], *ret;
|
||||
int32_t ifindex;
|
||||
|
||||
memcpy(&ifindex, RTA_DATA(attrs), 4);
|
||||
ret = if_indextoname(ifindex, name);
|
||||
if (ret)
|
||||
printf("dev %s ", ret);
|
||||
else
|
||||
printf("dev unknown/%d", ifindex);
|
||||
}
|
||||
|
||||
attrs = RTA_NEXT(attrs, len);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_addrs(struct nlmsghdr *nh, int pm_family, int total_len)
|
||||
{
|
||||
struct rtattr *attrs;
|
||||
|
||||
for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) {
|
||||
int len = nh->nlmsg_len;
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
if (nh->nlmsg_type == NLMSG_ERROR)
|
||||
nl_error(nh);
|
||||
if (nh->nlmsg_type != pm_family)
|
||||
continue;
|
||||
|
||||
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||
attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) +
|
||||
GENL_HDRLEN);
|
||||
while (RTA_OK(attrs, len)) {
|
||||
if (attrs->rta_type ==
|
||||
(MPTCP_PM_ATTR_ADDR | NLA_F_NESTED))
|
||||
print_addr((void *)RTA_DATA(attrs),
|
||||
attrs->rta_len);
|
||||
attrs = RTA_NEXT(attrs, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_addr(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
struct rtattr *rta, *nest;
|
||||
struct nlmsghdr *nh;
|
||||
int nest_start;
|
||||
u_int8_t id;
|
||||
int off = 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR,
|
||||
MPTCP_PM_VER);
|
||||
|
||||
/* the only argument is the address id */
|
||||
if (argc != 3)
|
||||
syntax(argv);
|
||||
|
||||
id = atoi(argv[2]);
|
||||
|
||||
nest_start = off;
|
||||
nest = (void *)(data + off);
|
||||
nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
|
||||
nest->rta_len = RTA_LENGTH(0);
|
||||
off += NLMSG_ALIGN(nest->rta_len);
|
||||
|
||||
/* build a dummy addr with only the ID set */
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ADDR_ATTR_ID;
|
||||
rta->rta_len = RTA_LENGTH(1);
|
||||
memcpy(RTA_DATA(rta), &id, 1);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
nest->rta_len = off - nest_start;
|
||||
|
||||
print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dump_addrs(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
pid_t pid = getpid();
|
||||
struct nlmsghdr *nh;
|
||||
int off = 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR,
|
||||
MPTCP_PM_VER);
|
||||
nh->nlmsg_flags |= NLM_F_DUMP;
|
||||
nh->nlmsg_seq = 1;
|
||||
nh->nlmsg_pid = pid;
|
||||
nh->nlmsg_len = off;
|
||||
|
||||
print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int flush_addrs(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
struct nlmsghdr *nh;
|
||||
int off = 0;
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_FLUSH_ADDRS,
|
||||
MPTCP_PM_VER);
|
||||
|
||||
do_nl_req(fd, nh, off, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_limits(struct nlmsghdr *nh, int pm_family, int total_len)
|
||||
{
|
||||
struct rtattr *attrs;
|
||||
uint32_t max;
|
||||
|
||||
for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) {
|
||||
int len = nh->nlmsg_len;
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
if (nh->nlmsg_type == NLMSG_ERROR)
|
||||
nl_error(nh);
|
||||
if (nh->nlmsg_type != pm_family)
|
||||
continue;
|
||||
|
||||
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||
attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) +
|
||||
GENL_HDRLEN);
|
||||
while (RTA_OK(attrs, len)) {
|
||||
int type = attrs->rta_type;
|
||||
|
||||
if (type != MPTCP_PM_ATTR_RCV_ADD_ADDRS &&
|
||||
type != MPTCP_PM_ATTR_SUBFLOWS)
|
||||
goto next;
|
||||
|
||||
memcpy(&max, RTA_DATA(attrs), 4);
|
||||
printf("%s %u\n", type == MPTCP_PM_ATTR_SUBFLOWS ?
|
||||
"subflows" : "accept", max);
|
||||
|
||||
next:
|
||||
attrs = RTA_NEXT(attrs, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_set_limits(int fd, int pm_family, int argc, char *argv[])
|
||||
{
|
||||
char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
|
||||
NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
|
||||
1024];
|
||||
uint32_t rcv_addr = 0, subflows = 0;
|
||||
int cmd, len = sizeof(data);
|
||||
struct nlmsghdr *nh;
|
||||
int off = 0;
|
||||
|
||||
/* limit */
|
||||
if (argc == 4) {
|
||||
rcv_addr = atoi(argv[2]);
|
||||
subflows = atoi(argv[3]);
|
||||
cmd = MPTCP_PM_CMD_SET_LIMITS;
|
||||
} else {
|
||||
cmd = MPTCP_PM_CMD_GET_LIMITS;
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
nh = (void *)data;
|
||||
off = init_genl_req(data, pm_family, cmd, MPTCP_PM_VER);
|
||||
|
||||
/* limit */
|
||||
if (cmd == MPTCP_PM_CMD_SET_LIMITS) {
|
||||
struct rtattr *rta = (void *)(data + off);
|
||||
|
||||
rta->rta_type = MPTCP_PM_ATTR_RCV_ADD_ADDRS;
|
||||
rta->rta_len = RTA_LENGTH(4);
|
||||
memcpy(RTA_DATA(rta), &rcv_addr, 4);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
|
||||
rta = (void *)(data + off);
|
||||
rta->rta_type = MPTCP_PM_ATTR_SUBFLOWS;
|
||||
rta->rta_len = RTA_LENGTH(4);
|
||||
memcpy(RTA_DATA(rta), &subflows, 4);
|
||||
off += NLMSG_ALIGN(rta->rta_len);
|
||||
|
||||
/* do not expect a reply */
|
||||
len = 0;
|
||||
}
|
||||
|
||||
len = do_nl_req(fd, nh, off, len);
|
||||
if (cmd == MPTCP_PM_CMD_GET_LIMITS)
|
||||
print_limits(nh, pm_family, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int fd, pm_family;
|
||||
|
||||
if (argc < 2)
|
||||
syntax(argv);
|
||||
|
||||
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
|
||||
if (fd == -1)
|
||||
error(1, errno, "socket netlink");
|
||||
|
||||
pm_family = resolve_mptcp_pm_netlink(fd);
|
||||
|
||||
if (!strcmp(argv[1], "add"))
|
||||
return add_addr(fd, pm_family, argc, argv);
|
||||
else if (!strcmp(argv[1], "del"))
|
||||
return del_addr(fd, pm_family, argc, argv);
|
||||
else if (!strcmp(argv[1], "flush"))
|
||||
return flush_addrs(fd, pm_family, argc, argv);
|
||||
else if (!strcmp(argv[1], "get"))
|
||||
return get_addr(fd, pm_family, argc, argv);
|
||||
else if (!strcmp(argv[1], "dump"))
|
||||
return dump_addrs(fd, pm_family, argc, argv);
|
||||
else if (!strcmp(argv[1], "limits"))
|
||||
return get_set_limits(fd, pm_family, argc, argv);
|
||||
|
||||
fprintf(stderr, "unknown sub-command: %s", argv[1]);
|
||||
syntax(argv);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user