Blame ip/ipmptcp.c

Packit Service 3880ab
// SPDX-License-Identifier: GPL-2.0
Packit Service 3880ab
Packit Service 3880ab
#include <stdio.h>
Packit Service 3880ab
#include <string.h>
Packit Service 3880ab
#include <rt_names.h>
Packit Service 3880ab
#include <errno.h>
Packit Service 3880ab
Packit Service 3880ab
#include <linux/genetlink.h>
Packit Service 3880ab
#include <linux/mptcp.h>
Packit Service 3880ab
Packit Service 3880ab
#include "utils.h"
Packit Service 3880ab
#include "ip_common.h"
Packit Service 3880ab
#include "libgenl.h"
Packit Service 3880ab
#include "json_print.h"
Packit Service 3880ab
Packit Service 3880ab
static void usage(void)
Packit Service 3880ab
{
Packit Service 3880ab
	fprintf(stderr,
Packit Service 3880ab
		"Usage:	ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
Packit Service 3880ab
		"				      [ FLAG-LIST ]\n"
Packit Service 3880ab
		"	ip mptcp endpoint delete id ID\n"
Packit Service 3880ab
		"	ip mptcp endpoint show [ id ID ]\n"
Packit Service 3880ab
		"	ip mptcp endpoint flush\n"
Packit Service 3880ab
		"	ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
Packit Service 3880ab
		"	ip mptcp limits show\n"
Packit Service 3880ab
		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
Packit Service 3880ab
		"FLAG  := [ signal | subflow | backup ]\n");
Packit Service 3880ab
Packit Service 3880ab
	exit(-1);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
/* netlink socket */
Packit Service 3880ab
static struct rtnl_handle genl_rth = { .fd = -1 };
Packit Service 3880ab
static int genl_family = -1;
Packit Service 3880ab
Packit Service 3880ab
#define MPTCP_BUFLEN	4096
Packit Service 3880ab
#define MPTCP_REQUEST(_req,  _cmd, _flags)	\
Packit Service 3880ab
	GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0,	\
Packit Service 3880ab
		     MPTCP_PM_VER, _cmd, _flags)
Packit Service 3880ab
Packit Service 3880ab
/* Mapping from argument to address flag mask */
Packit Service 3880ab
static const struct {
Packit Service 3880ab
	const char *name;
Packit Service 3880ab
	unsigned long value;
Packit Service 3880ab
} mptcp_addr_flag_names[] = {
Packit Service 3880ab
	{ "signal",		MPTCP_PM_ADDR_FLAG_SIGNAL },
Packit Service 3880ab
	{ "subflow",		MPTCP_PM_ADDR_FLAG_SUBFLOW },
Packit Service 3880ab
	{ "backup",		MPTCP_PM_ADDR_FLAG_BACKUP },
Packit Service 3880ab
};
Packit Service 3880ab
Packit Service 3880ab
static void print_mptcp_addr_flags(unsigned int flags)
Packit Service 3880ab
{
Packit Service 3880ab
	unsigned int i;
Packit Service 3880ab
Packit Service 3880ab
	for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
Packit Service 3880ab
		unsigned long mask = mptcp_addr_flag_names[i].value;
Packit Service 3880ab
Packit Service 3880ab
		if (flags & mask) {
Packit Service 3880ab
			print_string(PRINT_FP, NULL, "%s ",
Packit Service 3880ab
				     mptcp_addr_flag_names[i].name);
Packit Service 3880ab
			print_bool(PRINT_JSON,
Packit Service 3880ab
				   mptcp_addr_flag_names[i].name, NULL, true);
Packit Service 3880ab
		}
Packit Service 3880ab
Packit Service 3880ab
		flags &= ~mask;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (flags) {
Packit Service 3880ab
		/* unknown flags */
Packit Service 3880ab
		SPRINT_BUF(b1);
Packit Service 3880ab
Packit Service 3880ab
		snprintf(b1, sizeof(b1), "%02x", flags);
Packit Service 3880ab
		print_string(PRINT_ANY, "rawflags", "rawflags %s ", b1);
Packit Service 3880ab
	}
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int get_flags(const char *arg, __u32 *flags)
Packit Service 3880ab
{
Packit Service 3880ab
	unsigned int i;
Packit Service 3880ab
Packit Service 3880ab
	for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
Packit Service 3880ab
		if (strcmp(arg, mptcp_addr_flag_names[i].name))
Packit Service 3880ab
			continue;
Packit Service 3880ab
Packit Service 3880ab
		*flags |= mptcp_addr_flag_names[i].value;
Packit Service 3880ab
		return 0;
Packit Service 3880ab
	}
Packit Service 3880ab
	return -1;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
Packit Service 3880ab
			 bool adding)
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *attr_addr;
Packit Service 3880ab
	bool addr_set = false;
Packit Service 3880ab
	inet_prefix address;
Packit Service 3880ab
	bool id_set = false;
Packit Service 3880ab
	__u32 index = 0;
Packit Service 3880ab
	__u32 flags = 0;
Packit Service 3880ab
	__u8 id = 0;
Packit Service 3880ab
Packit Service 3880ab
	ll_init_map(&rth);
Packit Service 3880ab
	while (argc > 0) {
Packit Service 3880ab
		if (get_flags(*argv, &flags) == 0) {
Packit Service 3880ab
		} else if (matches(*argv, "id") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
Packit Service 3880ab
			if (get_u8(&id, *argv, 0))
Packit Service 3880ab
				invarg("invalid ID\n", *argv);
Packit Service 3880ab
			id_set = true;
Packit Service 3880ab
		} else if (matches(*argv, "dev") == 0) {
Packit Service 3880ab
			const char *ifname;
Packit Service 3880ab
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
Packit Service 3880ab
			ifname = *argv;
Packit Service 3880ab
Packit Service 3880ab
			if (check_ifname(ifname))
Packit Service 3880ab
				invarg("invalid interface name\n", ifname);
Packit Service 3880ab
Packit Service 3880ab
			index = ll_name_to_index(ifname);
Packit Service 3880ab
Packit Service 3880ab
			if (!index)
Packit Service 3880ab
				invarg("device does not exist\n", ifname);
Packit Service 3880ab
Packit Service 3880ab
		} else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
Packit Service 3880ab
			addr_set = true;
Packit Service 3880ab
		} else {
Packit Service 3880ab
			invarg("unknown argument", *argv);
Packit Service 3880ab
		}
Packit Service 3880ab
		NEXT_ARG_FWD();
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (!addr_set && adding)
Packit Service 3880ab
		missarg("ADDRESS");
Packit Service 3880ab
Packit Service 3880ab
	if (!id_set && !adding)
Packit Service 3880ab
		missarg("ID");
Packit Service 3880ab
Packit Service 3880ab
	attr_addr = addattr_nest(n, MPTCP_BUFLEN,
Packit Service 3880ab
				 MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
Packit Service 3880ab
	if (id_set)
Packit Service 3880ab
		addattr8(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_ID, id);
Packit Service 3880ab
	if (flags)
Packit Service 3880ab
		addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
Packit Service 3880ab
	if (index)
Packit Service 3880ab
		addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
Packit Service 3880ab
	if (addr_set) {
Packit Service 3880ab
		int type;
Packit Service 3880ab
Packit Service 3880ab
		addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FAMILY,
Packit Service 3880ab
			  address.family);
Packit Service 3880ab
		type = address.family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
Packit Service 3880ab
						   MPTCP_PM_ADDR_ATTR_ADDR6;
Packit Service 3880ab
		addattr_l(n, MPTCP_BUFLEN, type, &address.data,
Packit Service 3880ab
			  address.bytelen);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	addattr_nest_end(n, attr_addr);
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_addr_modify(int argc, char **argv, int cmd)
Packit Service 3880ab
{
Packit Service 3880ab
	MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
Packit Service 3880ab
	int ret;
Packit Service 3880ab
Packit Service 3880ab
	ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR);
Packit Service 3880ab
	if (ret)
Packit Service 3880ab
		return ret;
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
Packit Service 3880ab
		return -2;
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int print_mptcp_addrinfo(struct rtattr *addrinfo)
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
Packit Service 3880ab
	__u8 family = AF_UNSPEC, addr_attr_type;
Packit Service 3880ab
	const char *ifname;
Packit Service 3880ab
	unsigned int flags;
Packit Service 3880ab
	int index;
Packit Service 3880ab
	__u16 id;
Packit Service 3880ab
Packit Service 3880ab
	parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
Packit Service 3880ab
Packit Service 3880ab
	open_json_object(NULL);
Packit Service 3880ab
	if (tb[MPTCP_PM_ADDR_ATTR_FAMILY])
Packit Service 3880ab
		family = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
Packit Service 3880ab
Packit Service 3880ab
	addr_attr_type = family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
Packit Service 3880ab
					     MPTCP_PM_ADDR_ATTR_ADDR6;
Packit Service 3880ab
	if (tb[addr_attr_type]) {
Packit Service 3880ab
		print_string(PRINT_ANY, "address", "%s ",
Packit Service 3880ab
			     format_host_rta(family, tb[addr_attr_type]));
Packit Service 3880ab
	}
Packit Service 3880ab
	if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
Packit Service 3880ab
		id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
Packit Service 3880ab
		print_uint(PRINT_ANY, "id", "id %u ", id);
Packit Service 3880ab
	}
Packit Service 3880ab
	if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) {
Packit Service 3880ab
		flags = rta_getattr_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
Packit Service 3880ab
		print_mptcp_addr_flags(flags);
Packit Service 3880ab
	}
Packit Service 3880ab
	if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) {
Packit Service 3880ab
		index = rta_getattr_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
Packit Service 3880ab
		ifname = index ? ll_index_to_name(index) : NULL;
Packit Service 3880ab
Packit Service 3880ab
		if (ifname)
Packit Service 3880ab
			print_string(PRINT_ANY, "dev", "dev %s ", ifname);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	close_json_object();
Packit Service 3880ab
	print_string(PRINT_FP, NULL, "\n", NULL);
Packit Service 3880ab
	fflush(stdout);
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int print_mptcp_addr(struct nlmsghdr *n, void *arg)
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
Packit Service 3880ab
	struct genlmsghdr *ghdr;
Packit Service 3880ab
	struct rtattr *addrinfo;
Packit Service 3880ab
	int len = n->nlmsg_len;
Packit Service 3880ab
Packit Service 3880ab
	if (n->nlmsg_type != genl_family)
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	len -= NLMSG_LENGTH(GENL_HDRLEN);
Packit Service 3880ab
	if (len < 0)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	ghdr = NLMSG_DATA(n);
Packit Service 3880ab
	parse_rtattr_flags(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN,
Packit Service 3880ab
			   len, NLA_F_NESTED);
Packit Service 3880ab
	addrinfo = tb[MPTCP_PM_ATTR_ADDR];
Packit Service 3880ab
	if (!addrinfo)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	ll_init_map(&rth);
Packit Service 3880ab
	return print_mptcp_addrinfo(addrinfo);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_addr_dump(void)
Packit Service 3880ab
{
Packit Service 3880ab
	MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST | NLM_F_DUMP);
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
Packit Service 3880ab
		perror("Cannot send show request");
Packit Service 3880ab
		exit(1);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	new_json_obj(json);
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) {
Packit Service 3880ab
		fprintf(stderr, "Dump terminated\n");
Packit Service 3880ab
		delete_json_obj();
Packit Service 3880ab
		fflush(stdout);
Packit Service 3880ab
		return -2;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	close_json_object();
Packit Service 3880ab
	fflush(stdout);
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_addr_show(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST);
Packit Service 3880ab
	struct nlmsghdr *answer;
Packit Service 3880ab
	int ret;
Packit Service 3880ab
Packit Service 3880ab
	if (argc <= 0)
Packit Service 3880ab
		return mptcp_addr_dump();
Packit Service 3880ab
Packit Service 3880ab
	ret = mptcp_parse_opt(argc, argv, &req.n, false);
Packit Service 3880ab
	if (ret)
Packit Service 3880ab
		return ret;
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_talk(&genl_rth, &req.n, &answer) < 0)
Packit Service 3880ab
		return -2;
Packit Service 3880ab
Packit Service 3880ab
	return print_mptcp_addr(answer, stdout);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_addr_flush(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	MPTCP_REQUEST(req, MPTCP_PM_CMD_FLUSH_ADDRS, NLM_F_REQUEST);
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
Packit Service 3880ab
		return -2;
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_parse_limit(int argc, char **argv, struct nlmsghdr *n)
Packit Service 3880ab
{
Packit Service 3880ab
	bool set_rcv_add_addrs = false;
Packit Service 3880ab
	bool set_subflows = false;
Packit Service 3880ab
	__u32 rcv_add_addrs = 0;
Packit Service 3880ab
	__u32 subflows = 0;
Packit Service 3880ab
Packit Service 3880ab
	while (argc > 0) {
Packit Service 3880ab
		if (matches(*argv, "subflows") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
Packit Service 3880ab
			if (get_u32(&subflows, *argv, 0))
Packit Service 3880ab
				invarg("invalid subflows\n", *argv);
Packit Service 3880ab
			set_subflows = true;
Packit Service 3880ab
		} else if (matches(*argv, "add_addr_accepted") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
Packit Service 3880ab
			if (get_u32(&rcv_add_addrs, *argv, 0))
Packit Service 3880ab
				invarg("invalid add_addr_accepted\n", *argv);
Packit Service 3880ab
			set_rcv_add_addrs = true;
Packit Service 3880ab
		} else {
Packit Service 3880ab
			invarg("unknown limit", *argv);
Packit Service 3880ab
		}
Packit Service 3880ab
		NEXT_ARG_FWD();
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (set_rcv_add_addrs)
Packit Service 3880ab
		addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_RCV_ADD_ADDRS,
Packit Service 3880ab
			  rcv_add_addrs);
Packit Service 3880ab
	if (set_subflows)
Packit Service 3880ab
		addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_SUBFLOWS, subflows);
Packit Service 3880ab
	return set_rcv_add_addrs || set_subflows;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
Packit Service 3880ab
{
Packit Service 3880ab
	struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
Packit Service 3880ab
	struct genlmsghdr *ghdr;
Packit Service 3880ab
	int len = n->nlmsg_len;
Packit Service 3880ab
	__u32 val;
Packit Service 3880ab
Packit Service 3880ab
	if (n->nlmsg_type != genl_family)
Packit Service 3880ab
		return 0;
Packit Service 3880ab
Packit Service 3880ab
	len -= NLMSG_LENGTH(GENL_HDRLEN);
Packit Service 3880ab
	if (len < 0)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	ghdr = NLMSG_DATA(n);
Packit Service 3880ab
	parse_rtattr(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
Packit Service 3880ab
Packit Service 3880ab
	open_json_object(NULL);
Packit Service 3880ab
	if (tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]) {
Packit Service 3880ab
		val = rta_getattr_u32(tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]);
Packit Service 3880ab
Packit Service 3880ab
		print_uint(PRINT_ANY, "add_addr_accepted",
Packit Service 3880ab
			   "add_addr_accepted %d ", val);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[MPTCP_PM_ATTR_SUBFLOWS]) {
Packit Service 3880ab
		val = rta_getattr_u32(tb[MPTCP_PM_ATTR_SUBFLOWS]);
Packit Service 3880ab
Packit Service 3880ab
		print_uint(PRINT_ANY, "subflows", "subflows %d ", val);
Packit Service 3880ab
	}
Packit Service 3880ab
	print_string(PRINT_FP, NULL, "%s", "\n");
Packit Service 3880ab
	fflush(stdout);
Packit Service 3880ab
	close_json_object();
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int mptcp_limit_get_set(int argc, char **argv, int cmd)
Packit Service 3880ab
{
Packit Service 3880ab
	bool do_get = cmd == MPTCP_PM_CMD_GET_LIMITS;
Packit Service 3880ab
	MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
Packit Service 3880ab
	struct nlmsghdr *answer;
Packit Service 3880ab
	int ret;
Packit Service 3880ab
Packit Service 3880ab
	ret = mptcp_parse_limit(argc, argv, &req.n);
Packit Service 3880ab
	if (ret < 0)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0)
Packit Service 3880ab
		return -2;
Packit Service 3880ab
Packit Service 3880ab
	if (do_get)
Packit Service 3880ab
		return print_mptcp_limit(answer, stdout);
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
int do_mptcp(int argc, char **argv)
Packit Service 3880ab
{
Packit Service 3880ab
	if (argc == 0)
Packit Service 3880ab
		usage();
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "help") == 0)
Packit Service 3880ab
		usage();
Packit Service 3880ab
Packit Service 3880ab
	if (genl_init_handle(&genl_rth, MPTCP_PM_NAME, &genl_family))
Packit Service 3880ab
		exit(1);
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "endpoint") == 0) {
Packit Service 3880ab
		NEXT_ARG_FWD();
Packit Service 3880ab
		if (argc == 0)
Packit Service 3880ab
			return mptcp_addr_show(0, NULL);
Packit Service 3880ab
Packit Service 3880ab
		if (matches(*argv, "add") == 0)
Packit Service 3880ab
			return mptcp_addr_modify(argc-1, argv+1,
Packit Service 3880ab
						 MPTCP_PM_CMD_ADD_ADDR);
Packit Service 3880ab
		if (matches(*argv, "delete") == 0)
Packit Service 3880ab
			return mptcp_addr_modify(argc-1, argv+1,
Packit Service 3880ab
						 MPTCP_PM_CMD_DEL_ADDR);
Packit Service 3880ab
		if (matches(*argv, "show") == 0)
Packit Service 3880ab
			return mptcp_addr_show(argc-1, argv+1);
Packit Service 3880ab
		if (matches(*argv, "flush") == 0)
Packit Service 3880ab
			return mptcp_addr_flush(argc-1, argv+1);
Packit Service 3880ab
Packit Service 3880ab
		goto unknown;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (matches(*argv, "limits") == 0) {
Packit Service 3880ab
		NEXT_ARG_FWD();
Packit Service 3880ab
		if (argc == 0)
Packit Service 3880ab
			return mptcp_limit_get_set(0, NULL,
Packit Service 3880ab
						   MPTCP_PM_CMD_GET_LIMITS);
Packit Service 3880ab
Packit Service 3880ab
		if (matches(*argv, "set") == 0)
Packit Service 3880ab
			return mptcp_limit_get_set(argc-1, argv+1,
Packit Service 3880ab
						   MPTCP_PM_CMD_SET_LIMITS);
Packit Service 3880ab
		if (matches(*argv, "show") == 0)
Packit Service 3880ab
			return mptcp_limit_get_set(argc-1, argv+1,
Packit Service 3880ab
						   MPTCP_PM_CMD_GET_LIMITS);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
unknown:
Packit Service 3880ab
	fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
Packit Service 3880ab
		*argv);
Packit Service 3880ab
	exit(-1);
Packit Service 3880ab
}