mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
19 KiB
763 lines
19 KiB
// 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|set|del|flush|dump|accept [<args>]\n", argv[0]); |
|
fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id <nr>] [dev <name>] <ip>\n"); |
|
fprintf(stderr, "\tdel <id> [<ip>]\n"); |
|
fprintf(stderr, "\tget <id>\n"); |
|
fprintf(stderr, "\tset <ip> [flags backup|nobackup]\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_int32_t flags = 0; |
|
u_int16_t family; |
|
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 */ |
|
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 if (!strcmp(tok, "fullmesh")) |
|
flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; |
|
else |
|
error(1, errno, |
|
"unknown flag %s", argv[arg]); |
|
} |
|
|
|
if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL && |
|
flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
|
error(1, errno, "error flag fullmesh"); |
|
} |
|
|
|
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 if (!strcmp(argv[arg], "port")) { |
|
u_int16_t port; |
|
|
|
if (++arg >= argc) |
|
error(1, 0, " missing port value"); |
|
if (!(flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) |
|
error(1, 0, " flags must be signal when using port"); |
|
|
|
port = atoi(argv[arg]); |
|
rta = (void *)(data + off); |
|
rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
|
rta->rta_len = RTA_LENGTH(2); |
|
memcpy(RTA_DATA(rta), &port, 2); |
|
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; |
|
u_int16_t family; |
|
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 (nonzero) */ |
|
if (argc != 3 && argc != 4) |
|
syntax(argv); |
|
|
|
id = atoi(argv[2]); |
|
/* zero id with the IP address */ |
|
if (!id && argc != 4) |
|
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); |
|
|
|
/* 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); |
|
|
|
if (!id) { |
|
/* addr data */ |
|
rta = (void *)(data + off); |
|
if (inet_pton(AF_INET, argv[3], 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[3], 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[3]); |
|
} |
|
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); |
|
} |
|
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; |
|
uint16_t port = 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_PORT) |
|
memcpy(&port, 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 (port) |
|
printf(" %d", port); |
|
} |
|
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 (port) |
|
printf(" %d", port); |
|
} |
|
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(","); |
|
} |
|
|
|
if (flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
|
printf("fullmesh"); |
|
flags &= ~MPTCP_PM_ADDR_FLAG_FULLMESH; |
|
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 set_flags(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_int32_t flags = 0; |
|
u_int16_t family; |
|
int nest_start; |
|
int off = 0; |
|
int arg; |
|
|
|
memset(data, 0, sizeof(data)); |
|
nh = (void *)data; |
|
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SET_FLAGS, |
|
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 */ |
|
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, "backup")) |
|
flags |= MPTCP_PM_ADDR_FLAG_BACKUP; |
|
else if (strcmp(tok, "nobackup")) |
|
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 { |
|
error(1, 0, "unknown keyword %s", argv[arg]); |
|
} |
|
} |
|
nest->rta_len = off - nest_start; |
|
|
|
do_nl_req(fd, nh, off, 0); |
|
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); |
|
else if (!strcmp(argv[1], "set")) |
|
return set_flags(fd, pm_family, argc, argv); |
|
|
|
fprintf(stderr, "unknown sub-command: %s", argv[1]); |
|
syntax(argv); |
|
return 0; |
|
}
|
|
|