Contributors: 5
Author Tokens Token Proportion Commits Commit Proportion
Paolo Abeni 5978 59.77% 2 8.00%
Kishen Maloor 3019 30.19% 7 28.00%
Geliang Tang 917 9.17% 11 44.00%
Matthieu Baerts 85 0.85% 4 16.00%
John Hubbard 2 0.02% 1 4.00%
Total 10001 25

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
// 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 [<args>]\n", argv[0]);
	fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id <nr>] [dev <name>] <ip>\n");
	fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>]\n");
	fprintf(stderr, "\trem id <local-id> token <token>\n");
	fprintf(stderr, "\tcsf lip <local-ip> lid <local-id> rip <remote-ip> rport <remote-port> token <token>\n");
	fprintf(stderr, "\tdsf lip <local-ip> lport <local-port> rip <remote-ip> rport <remote-port> token <token>\n");
	fprintf(stderr, "\tdel <id> [<ip>]\n");
	fprintf(stderr, "\tget <id>\n");
	fprintf(stderr, "\tset [<ip>] [id <nr>] flags [no]backup|[no]fullmesh [port <nr>] [token <token>] [rip <ip>] [rport <port>]\n");
	fprintf(stderr, "\tflush\n");
	fprintf(stderr, "\tdump\n");
	fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
	fprintf(stderr, "\tevents\n");
	fprintf(stderr, "\tlisten <local-ip> <local-port>\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));

				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;
}