'pm_nl_ctl' was adding a trailing whitespace after having printed the IP. But at the end, the IP element is currently always the last one. The bash script launching 'pm_nl_ctl' had trailing whitespaces in the expected result on purpose. But these whitespaces have been removed when the patch has been applied upstream. To avoid trailing whitespaces in the bash code, 'pm_nl_ctl' and expected results have now been adapted. The MPTCP PM selftest can now pass again. Fixes: eedbc685321b (selftests: add PM netlink functional tests) Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net> Signed-off-by: David S. Miller <davem@davemloft.net>
617 lines
15 KiB
C
617 lines
15 KiB
C
// 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;
|
|
}
|