// 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 <limits.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 IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
#endif
static void syntax(
char *argv[])
{
fprintf(stderr,
"%s add|ann|rem|csf|dsf|get|set|del|flush|dump|events|listen|accept []\n", argv[0]);
fprintf(stderr,
"\tadd [flags signal|subflow|backup|fullmesh] [id ] [dev ] \n");
fprintf(stderr,
"\tann id token [port ] [dev ]\n");
fprintf(stderr,
"\trem id token \n");
fprintf(stderr,
"\tcsf lip lid rip rport token \n");
fprintf(stderr,
"\tdsf lip lport rip rport token \n");
fprintf(stderr,
"\tdel []\n");
fprintf(stderr,
"\tget \n");
fprintf(stderr,
"\tset [] [id ] flags [no]backup|[no]fullmesh [port ] [token ] [rip ] [rport ]\n");
fprintf(stderr,
"\tflush\n");
fprintf(stderr,
"\tdump\n");
fprintf(stderr,
"\tlimits [ ]\n");
fprintf(stderr,
"\tevents\n");
fprintf(stderr,
"\tlisten \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 int 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));
return -1;
}
if (err->error) {
/* check messages from kernel */
struct rtattr *attrs = (
struct rtattr *)NLMSG_DATA(nh);
fprintf(stderr,
"netlink error %d (%s)\n",
err->error, strerror(-err->error));
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);
}
return -1;
}
return 0;
}
static int capture_events(
int fd,
int event_group)
{
u_int8_t buffer[NLMSG_ALIGN(
sizeof(
struct nlmsghdr)) +
NLMSG_ALIGN(
sizeof(
struct genlmsghdr)) + 1024];
struct genlmsghdr *ghdr;
struct rtattr *attrs;
struct nlmsghdr *nh;
int ret = 0;
int res_len;
int msg_len;
fd_set rfds;
if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
&event_group,
sizeof(event_group)) < 0)
error(1, errno,
"could not join the " MPTCP_PM_EV_GRP_NAME
" mcast group");
do {
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
res_len = NLMSG_ALIGN(
sizeof(
struct nlmsghdr)) +
NLMSG_ALIGN(
sizeof(
struct genlmsghdr)) + 1024;
ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
if (ret < 0)
error(1, ret,
"error in select() on NL socket");
res_len = recv(fd, buffer, res_len, 0);
if (res_len < 0)
error(1, res_len,
"error on recv() from NL socket");
nh = (
struct nlmsghdr *)buffer;
for (; NLMSG_OK(nh, res_len); nh = NLMSG_NEXT(nh, res_len)) {
if (nh->nlmsg_type == NLMSG_ERROR)
error(1, NLMSG_ERROR,
"received invalid NL message");
ghdr = (
struct genlmsghdr *)NLMSG_DATA(nh);
if (ghdr->cmd == 0)
continue;
fprintf(stderr,
"type:%d", ghdr->cmd);
msg_len = nh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
attrs = (
struct rtattr *) ((
char *) ghdr + GENL_HDRLEN);
while (RTA_OK(attrs, msg_len)) {
if (attrs->rta_type == MPTCP_ATTR_TOKEN)
fprintf(stderr,
",token:%u", *(__u32 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_FAMILY)
fprintf(stderr,
",family:%u", *(__u16 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_LOC_ID)
fprintf(stderr,
",loc_id:%u", *(__u8 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_REM_ID)
fprintf(stderr,
",rem_id:%u", *(__u8 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_SADDR4) {
u_int32_t saddr4 = ntohl(*(__u32 *)RTA_DATA(attrs));
fprintf(stderr,
",saddr4:%u.%u.%u.%u", saddr4 >> 24,
(saddr4 >> 16) & 0xFF, (saddr4 >> 8) & 0xFF,
(saddr4 & 0xFF));
}
else if (attrs->rta_type == MPTCP_ATTR_SADDR6) {
char buf[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf,
sizeof(buf)) != NULL)
fprintf(stderr,
",saddr6:%s", buf);
}
else if (attrs->rta_type == MPTCP_ATTR_DADDR4) {
u_int32_t daddr4 = ntohl(*(__u32 *)RTA_DATA(attrs));
fprintf(stderr,
",daddr4:%u.%u.%u.%u", daddr4 >> 24,
(daddr4 >> 16) & 0xFF, (daddr4 >> 8) & 0xFF,
(daddr4 & 0xFF));
}
else if (attrs->rta_type == MPTCP_ATTR_DADDR6) {
char buf[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf,
sizeof(buf)) != NULL)
fprintf(stderr,
",daddr6:%s", buf);
}
else if (attrs->rta_type == MPTCP_ATTR_SPORT)
fprintf(stderr,
",sport:%u",
ntohs(*(__u16 *)RTA_DATA(attrs)));
else if (attrs->rta_type == MPTCP_ATTR_DPORT)
fprintf(stderr,
",dport:%u",
ntohs(*(__u16 *)RTA_DATA(attrs)));
else if (attrs->rta_type == MPTCP_ATTR_BACKUP)
fprintf(stderr,
",backup:%u", *(__u8 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_ERROR)
fprintf(stderr,
",error:%u", *(__u8 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_SERVER_SIDE)
fprintf(stderr,
",server_side:%u", *(__u8 *)RTA_DATA(attrs));
else if (attrs->rta_type == MPTCP_ATTR_FLAGS) {
__u16 flags = *(__u16 *)RTA_DATA(attrs);
/* only print when present, easier */
if (flags & MPTCP_PM_EV_FLAG_DENY_JOIN_ID0)
fprintf(stderr,
",deny_join_id0:1");
}
attrs = RTA_NEXT(attrs, msg_len);
}
}
fprintf(stderr,
"\n");
}
while (1);
return 0;
}
/* do a netlink command and, if max > 0, fetch the reply ; nh's size >1024B */
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;
/* If no expected answer, ask for an ACK to look for errors if any */
if (max == 0) {
nh->nlmsg_flags |= NLM_F_ACK;
max = 1024;
}
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);
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_DONE)
break;
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,
int *pm_family,
int *events_mcast_grp)
{
struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
int len = nlh->nlmsg_len;
struct rtattr *attrs;
struct rtattr *grps;
struct rtattr *grp;
int got_events_grp;
int got_family;
int grps_len;
int grp_len;
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);
got_family = 0;
got_events_grp = 0;
while (RTA_OK(attrs, len)) {
if (attrs->rta_type == CTRL_ATTR_FAMILY_ID) {
*pm_family = *(__u16 *)RTA_DATA(attrs);
got_family = 1;
}
else if (attrs->rta_type == CTRL_ATTR_MCAST_GROUPS) {
grps = RTA_DATA(attrs);
grps_len = RTA_PAYLOAD(attrs);
while (RTA_OK(grps, grps_len)) {
grp = RTA_DATA(grps);
grp_len = RTA_PAYLOAD(grps);
got_events_grp = 0;
while (RTA_OK(grp, grp_len)) {
if (grp->rta_type == CTRL_ATTR_MCAST_GRP_ID)
*events_mcast_grp = *(__u32 *)RTA_DATA(grp);
else if (grp->rta_type == CTRL_ATTR_MCAST_GRP_NAME &&
!strcmp(RTA_DATA(grp), MPTCP_PM_EV_GRP_NAME))
got_events_grp = 1;
grp = RTA_NEXT(grp, grp_len);
}
if (got_events_grp)
break;
grps = RTA_NEXT(grps, grps_len);
}
}
if (got_family && got_events_grp)
return 0;
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,
int *pm_family,
int *events_mcast_grp)
{
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, pm_family, events_mcast_grp);
}
int dsf(
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, *addr;
u_int16_t family, port;
struct nlmsghdr *nh;
u_int32_t token;
int addr_start;
int off = 0;
int arg;
const char *params[5];
memset(params, 0, 5 *
sizeof(
const char *));
memset(data, 0,
sizeof(data));
nh = (
void *)data;
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_DESTROY,
MPTCP_PM_VER);
if (argc < 12)
syntax(argv);
/* Params recorded in this order:
* <local-ip>, <local-port>, <remote-ip>, <remote-port>, <token>
*/
for (arg = 2; arg < argc; arg++) {
if (!strcmp(argv[arg],
"lip")) {
if (++arg >= argc)
error(1, 0,
" missing local IP");
params[0] = argv[arg];
}
else if (!strcmp(argv[arg],
"lport")) {
if (++arg >= argc)
error(1, 0,
" missing local port");
params[1] = argv[arg];
}
else if (!strcmp(argv[arg],
"rip")) {
if (++arg >= argc)
error(1, 0,
" missing remote IP");
params[2] = argv[arg];
}
else if (!strcmp(argv[arg],
"rport")) {
if (++arg >= argc)
error(1, 0,
" missing remote port");
params[3] = argv[arg];
}
else if (!strcmp(argv[arg],
"token")) {
if (++arg >= argc)
error(1, 0,
" missing token");
params[4] = argv[arg];
}
else
error(1, 0,
"unknown keyword %s", argv[arg]);
}
for (arg = 0; arg < 4; arg = arg + 2) {
/* addr header */
addr_start = off;
addr = (
void *)(data + off);
addr->rta_type = NLA_F_NESTED |
((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE);
addr->rta_len = RTA_LENGTH(0);
off += NLMSG_ALIGN(addr->rta_len);
/* addr data */
rta = (
void *)(data + off);
if (inet_pton(AF_INET, params[arg], 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, params[arg], 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", params[arg]);
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);
/* port */
port = atoi(params[arg + 1]);
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);
addr->rta_len = off - addr_start;
}
/* token */
token = strtoul(params[4], NULL, 10);
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
do_nl_req(fd, nh, off, 0);
return 0;
}
int csf(
int fd,
int pm_family,
int argc,
char *argv[])
{
char data[NLMSG_ALIGN(
sizeof(
struct nlmsghdr)) +
NLMSG_ALIGN(
sizeof(
struct genlmsghdr)) +
1024];
u_int32_t flags = MPTCP_PM_ADDR_FLAG_SUBFLOW;
const char *params[5];
struct nlmsghdr *nh;
struct rtattr *addr;
struct rtattr *rta;
u_int16_t family;
u_int32_t token;
u_int16_t port;
int addr_start;
u_int8_t id;
int off = 0;
int arg;
memset(params, 0, 5 *
sizeof(
const char *));
memset(data, 0,
sizeof(data));
nh = (
void *)data;
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_CREATE,
MPTCP_PM_VER);
if (argc < 12)
syntax(argv);
/* Params recorded in this order:
* <local-ip>, <local-id>, <remote-ip>, <remote-port>, <token>
*/
for (arg = 2; arg < argc; arg++) {
if (!strcmp(argv[arg],
"lip")) {
if (++arg >= argc)
error(1, 0,
" missing local IP");
params[0] = argv[arg];
}
else if (!strcmp(argv[arg],
"lid")) {
if (++arg >= argc)
error(1, 0,
" missing local id");
params[1] = argv[arg];
}
else if (!strcmp(argv[arg],
"rip")) {
if (++arg >= argc)
error(1, 0,
" missing remote ip");
params[2] = argv[arg];
}
else if (!strcmp(argv[arg],
"rport")) {
if (++arg >= argc)
error(1, 0,
" missing remote port");
params[3] = argv[arg];
}
else if (!strcmp(argv[arg],
"token")) {
if (++arg >= argc)
error(1, 0,
" missing token");
params[4] = argv[arg];
}
else
error(1, 0,
"unknown param %s", argv[arg]);
}
for (arg = 0; arg < 4; arg = arg + 2) {
/* addr header */
addr_start = off;
addr = (
void *)(data + off);
addr->rta_type = NLA_F_NESTED |
((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE);
addr->rta_len = RTA_LENGTH(0);
off += NLMSG_ALIGN(addr->rta_len);
/* addr data */
rta = (
void *)(data + off);
if (inet_pton(AF_INET, params[arg], 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, params[arg], 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", params[arg]);
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);
if (arg == 2) {
/* port */
port = atoi(params[arg + 1]);
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);
}
if (arg == 0) {
/* id */
id = atoi(params[arg + 1]);
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);
}
/* addr flags */
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);
addr->rta_len = off - addr_start;
}
/* token */
token = strtoul(params[4], NULL, 10);
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
do_nl_req(fd, nh, off, 0);
return 0;
}
int remove_addr(
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;
struct rtattr *rta;
u_int32_t token;
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_REMOVE,
MPTCP_PM_VER);
if (argc < 6)
syntax(argv);
for (arg = 2; arg < argc; arg++) {
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_ATTR_LOC_ID;
rta->rta_len = RTA_LENGTH(1);
memcpy(RTA_DATA(rta), &id, 1);
off += NLMSG_ALIGN(rta->rta_len);
}
else if (!strcmp(argv[arg],
"token")) {
if (++arg >= argc)
error(1, 0,
" missing token value");
token = strtoul(argv[arg], NULL, 10);
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
}
else
error(1, 0,
"unknown keyword %s", argv[arg]);
}
do_nl_req(fd, nh, off, 0);
return 0;
}
int announce_addr(
int fd,
int pm_family,
int argc,
char *argv[])
{
char data[NLMSG_ALIGN(
sizeof(
struct nlmsghdr)) +
NLMSG_ALIGN(
sizeof(
struct genlmsghdr)) +
1024];
u_int32_t flags = MPTCP_PM_ADDR_FLAG_SIGNAL;
u_int32_t token = UINT_MAX;
struct rtattr *rta, *addr;
u_int32_t id = UINT_MAX;
struct nlmsghdr *nh;
u_int16_t family;
int addr_start;
int off = 0;
int arg;
memset(data, 0,
sizeof(data));
nh = (
void *)data;
off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ANNOUNCE,
MPTCP_PM_VER);
if (argc < 7)
syntax(argv);
/* local-ip header */
addr_start = off;
addr = (
void *)(data + off);
addr->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
addr->rta_len = RTA_LENGTH(0);
off += NLMSG_ALIGN(addr->rta_len);
/* local-ip data */
/* record addr type */
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);
/* addr 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],
"id")) {
/* local-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")) {
/* for the if_index */
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")) {
/* local-port (optional) */
u_int16_t port;
if (++arg >= argc)
error(1, 0,
" missing port value");
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 if (!strcmp(argv[arg],
"token")) {
/* MPTCP connection token */
if (++arg >= argc)
error(1, 0,
" missing token value");
token = strtoul(argv[arg], NULL, 10);
}
else
error(1, 0,
"unknown keyword %s", argv[arg]);
}
/* addr flags */
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);
addr->rta_len = off - addr_start;
if (id == UINT_MAX || token == UINT_MAX)
error(1, 0,
" missing mandatory inputs");
/* token */
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
do_nl_req(fd, nh, off, 0);
return 0;
}
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(
",");
}
if (flags & MPTCP_PM_ADDR_FLAG_IMPLICIT) {
printf(
"implicit");
flags &= ~MPTCP_PM_ADDR_FLAG_IMPLICIT;
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;
u_int32_t token = 0;
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 && argc != 5)
syntax(argv);
id = atoi(argv[2]);
if (argc == 5 && !strcmp(argv[3],
"token"))
token = strtoul(argv[4], NULL, 10);
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;
/* token */
if (token) {
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
}
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;
u_int32_t token = 0;
struct rtattr *rta;
int off = 0;
if (argc != 2 && argc != 4)
syntax(argv);
if (argc == 4 && !strcmp(argv[2],
"token"))
token = strtoul(argv[3], NULL, 10);
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;
/* token */
if (token) {
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
}
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 add_listener(
int argc,
char *argv[])
{
struct sockaddr_storage addr;
struct sockaddr_in6 *a6;
struct sockaddr_in *a4;
u_int16_t family = AF_UNSPEC;
int enable = 1;
int sock;
int err;
if (argc < 4)
syntax(argv);
memset(&addr, 0,
sizeof(
struct sockaddr_storage));
a4 = (
struct sockaddr_in *)&addr;
a6 = (
struct sockaddr_in6 *)&addr;
if (inet_pton(AF_INET, argv[2], &a4->sin_addr)) {
family = AF_INET;
a4->sin_family = family;
a4->sin_port = htons(atoi(argv[3]));
}
else if (inet_pton(AF_INET6, argv[2], &a6->sin6_addr)) {
family = AF_INET6;
a6->sin6_family = family;
a6->sin6_port = htons(atoi(argv[3]));
}
else
error(1, errno,
"can't parse ip %s", argv[2]);
sock = socket(family, SOCK_STREAM, IPPROTO_MPTCP);
if (sock < 0)
error(1, errno,
"can't create listener sock\n");
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(enable))) {
close(sock);
error(1, errno,
"can't set SO_REUSEADDR on listener sock\n");
}
err = bind(sock, (
struct sockaddr *)&addr,
((family == AF_INET) ?
sizeof(
struct sockaddr_in) :
sizeof(
struct sockaddr_in6)));
if (err == 0 && listen(sock, 30) == 0)
pause();
close(sock);
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_int32_t token = 0;
u_int16_t rport = 0;
u_int16_t family;
void *rip = NULL;
int nest_start;
int use_id = 0;
u_int8_t id;
int off = 0;
int arg = 2;
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);
if (!strcmp(argv[arg],
"id")) {
if (++arg >= argc)
error(1, 0,
" missing id value");
use_id = 1;
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 {
/* addr data */
rta = (
void *)(data + off);
if (inet_pton(AF_INET, argv[arg], 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[arg], 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[arg]);
}
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);
}
if (++arg >= argc)
error(1, 0,
" missing flags keyword");
for (; arg < argc; arg++) {
if (!strcmp(argv[arg],
"token")) {
if (++arg >= argc)
error(1, 0,
" missing token value");
/* token */
token = strtoul(argv[arg], NULL, 10);
}
else if (!strcmp(argv[arg],
"flags")) {
char *tok, *str;
/* flags */
if (++arg >= argc)
error(1, 0,
" missing flags value");
for (str = argv[arg]; (tok = strtok(str,
","));
str = NULL) {
if (!strcmp(tok,
"backup"))
flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
else if (!strcmp(tok,
"fullmesh"))
flags |= MPTCP_PM_ADDR_FLAG_FULLMESH;
else if (strcmp(tok,
"nobackup") &&
strcmp(tok,
"nofullmesh"))
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],
"port")) {
u_int16_t port;
if (use_id)
error(1, 0,
" port can't be used with id");
if (++arg >= argc)
error(1, 0,
" missing port value");
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 if (!strcmp(argv[arg],
"rport")) {
if (++arg >= argc)
error(1, 0,
" missing remote port");
rport = atoi(argv[arg]);
}
else if (!strcmp(argv[arg],
"rip")) {
if (++arg >= argc)
error(1, 0,
" missing remote ip");
rip = argv[arg];
}
else {
error(1, 0,
"unknown keyword %s", argv[arg]);
}
}
nest->rta_len = off - nest_start;
/* token */
if (token) {
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ATTR_TOKEN;
rta->rta_len = RTA_LENGTH(4);
memcpy(RTA_DATA(rta), &token, 4);
off += NLMSG_ALIGN(rta->rta_len);
}
/* remote addr/port */
if (rip) {
nest_start = off;
nest = (
void *)(data + off);
nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR_REMOTE;
nest->rta_len = RTA_LENGTH(0);
off += NLMSG_ALIGN(nest->rta_len);
/* addr data */
rta = (
void *)(data + off);
if (inet_pton(AF_INET, rip, 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, rip, 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", (
char *)rip);
}
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);
if (rport) {
rta = (
void *)(data + off);
rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT;
rta->rta_len = RTA_LENGTH(2);
memcpy(RTA_DATA(rta), &rport, 2);
off += NLMSG_ALIGN(rta->rta_len);
}
nest->rta_len = off - nest_start;
}
do_nl_req(fd, nh, off, 0);
return 0;
}
int main(
int argc,
char *argv[])
{
int events_mcast_grp;
int pm_family;
int fd;
if (argc < 2)
syntax(argv);
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd == -1)
error(1, errno,
"socket netlink");
resolve_mptcp_pm_netlink(fd, &pm_family, &events_mcast_grp);
if (!strcmp(argv[1],
"add"))
return add_addr(fd, pm_family, argc, argv);
else if (!strcmp(argv[1],
"ann"))
return announce_addr(fd, pm_family, argc, argv);
else if (!strcmp(argv[1],
"rem"))
return remove_addr(fd, pm_family, argc, argv);
else if (!strcmp(argv[1],
"csf"))
return csf(fd, pm_family, argc, argv);
else if (!strcmp(argv[1],
"dsf"))
return dsf(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);
else if (!strcmp(argv[1],
"events"))
return capture_events(fd, events_mcast_grp);
else if (!strcmp(argv[1],
"listen"))
return add_listener(argc, argv);
fprintf(stderr,
"unknown sub-command: %s", argv[1]);
syntax(argv);
return 0;
}