Ejemplo n.º 1
0
static int do_add(int argc, char **argv)
{
	struct l2tp_parm p;
	int ret = 0;

	if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
		return -1;

	if (!p.tunnel && !p.session)
		missarg("tunnel or session");

	if (p.tunnel_id == 0)
		missarg("tunnel_id");

	/* session_id and peer_session_id must be provided for sessions */
	if ((p.session) && (p.peer_session_id == 0))
		missarg("peer_session_id");
	if ((p.session) && (p.session_id == 0))
		missarg("session_id");

	/* peer_tunnel_id is needed for tunnels */
	if ((p.tunnel) && (p.peer_tunnel_id == 0))
		missarg("peer_tunnel_id");

	if (p.tunnel) {
		if (p.local_ip.family == AF_UNSPEC)
			missarg("local");

		if (p.peer_ip.family == AF_UNSPEC)
			missarg("remote");

		if (p.encap == L2TP_ENCAPTYPE_UDP) {
			if (p.local_udp_port == 0)
				missarg("udp_sport");
			if (p.peer_udp_port == 0)
				missarg("udp_dport");
		}

		ret = create_tunnel(&p);
	}

	if (p.session) {
		/* Only ethernet pseudowires supported */
		p.pw_type = L2TP_PWTYPE_ETH;

		ret = create_session(&p);
	}

	return ret;
}
Ejemplo n.º 2
0
static int do_del(int argc, char **argv)
{
	struct l2tp_parm p;

	if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
		return -1;

	if (!p.tunnel && !p.session)
		missarg("tunnel or session");

	if ((p.tunnel) && (p.tunnel_id == 0))
		missarg("tunnel_id");
	if ((p.session) && (p.session_id == 0))
		missarg("session_id");

	if (p.session_id)
		return delete_session(&p);
	else
		return delete_tunnel(&p);

	return -1;
}
Ejemplo n.º 3
0
static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl,
			   int *argcp, char ***argvp)
{
	int argc = *argcp;
	char **argv = *argvp;
	char *idp = NULL;

	while (1) {
		if (strcmp(*argv, "mode") == 0) {
			NEXT_ARG();
			xfrm_mode_parse(&tmpl->mode,  &argc, &argv);
		} else if (strcmp(*argv, "reqid") == 0) {
			NEXT_ARG();
			xfrm_reqid_parse(&tmpl->reqid, &argc, &argv);
		} else if (strcmp(*argv, "level") == 0) {
			NEXT_ARG();

			if (strcmp(*argv, "required") == 0)
				tmpl->optional = 0;
			else if (strcmp(*argv, "use") == 0)
				tmpl->optional = 1;
			else
				invarg("\"LEVEL\" is invalid\n", *argv);

		} else {
			if (idp) {
				PREV_ARG(); /* back track */
				break;
			}
			idp = *argv;
			xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family,
				      0, &argc, &argv);
			if (preferred_family == AF_UNSPEC)
				preferred_family = tmpl->family;
		}

		if (!NEXT_ARG_OK())
			break;

		NEXT_ARG();
	}
	if (argc == *argcp)
		missarg("TMPL");

	*argcp = argc;
	*argvp = argv;

	return 0;
}
Ejemplo n.º 4
0
static int do_show(int argc, char **argv)
{
	struct l2tp_data data;
	struct l2tp_parm *p = &data.config;

	if (parse_args(argc, argv, L2TP_GET, p) < 0)
		return -1;

	if (!p->tunnel && !p->session)
		missarg("tunnel or session");

	if (p->session)
		get_session(&data);
	else
		get_tunnel(&data);

	return 0;
}
Ejemplo n.º 5
0
int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
		  int loose, int *argcp, char ***argvp)
{
	int argc = *argcp;
	char **argv = *argvp;
	inet_prefix dst;
	inet_prefix src;

	memset(&dst, 0, sizeof(dst));
	memset(&src, 0, sizeof(src));

	while (1) {
		if (strcmp(*argv, "src") == 0) {
			NEXT_ARG();

			get_prefix(&src, *argv, preferred_family);
			if (src.family == AF_UNSPEC)
				invarg("\"src\" address family is AF_UNSPEC", *argv);
			if (family)
				*family = src.family;

			memcpy(saddr, &src.data, sizeof(*saddr));

			filter.id_src_mask = src.bitlen;

		} else if (strcmp(*argv, "dst") == 0) {
			NEXT_ARG();

			get_prefix(&dst, *argv, preferred_family);
			if (dst.family == AF_UNSPEC)
				invarg("\"dst\" address family is AF_UNSPEC", *argv);
			if (family)
				*family = dst.family;

			memcpy(&id->daddr, &dst.data, sizeof(id->daddr));

			filter.id_dst_mask = dst.bitlen;

		} else if (strcmp(*argv, "proto") == 0) {
			int ret;

			NEXT_ARG();

			ret = xfrm_xfrmproto_getbyname(*argv);
			if (ret < 0)
				invarg("\"XFRM_PROTO\" is invalid", *argv);

			id->proto = (__u8)ret;

			filter.id_proto_mask = XFRM_FILTER_MASK_FULL;

		} else if (strcmp(*argv, "spi") == 0) {
			__u32 spi;

			NEXT_ARG();
			if (get_u32(&spi, *argv, 0))
				invarg("\"SPI\" is invalid", *argv);

			spi = htonl(spi);
			id->spi = spi;

			filter.id_spi_mask = XFRM_FILTER_MASK_FULL;

		} else {
			PREV_ARG(); /* back track */
			break;
		}

		if (!NEXT_ARG_OK())
			break;
		NEXT_ARG();
	}

	if (src.family && dst.family && (src.family != dst.family))
		invarg("the same address family is required between \"src\" and \"dst\"", *argv);

	if (loose == 0 && id->proto == 0)
		missarg("XFRM_PROTO");
	if (argc == *argcp)
		missarg("ID");

	*argcp = argc;
	*argvp = argv;

	return 0;
}
Ejemplo n.º 6
0
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
{
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr 	n;
		struct xfrm_usersa_info xsinfo;
		char   			buf[RTA_BUF_SIZE];
	} req;
	struct xfrm_replay_state replay;
	char *idp = NULL;
	char *aeadop = NULL;
	char *ealgop = NULL;
	char *aalgop = NULL;
	char *calgop = NULL;
	char *coap = NULL;
	char *sctxp = NULL;
	struct xfrm_mark mark = {0, 0};
	struct {
		struct xfrm_user_sec_ctx sctx;
		char    str[CTX_BUF_SIZE];
	} ctx;

	memset(&req, 0, sizeof(req));
	memset(&replay, 0, sizeof(replay));
	memset(&ctx, 0, sizeof(ctx));

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
	req.n.nlmsg_type = cmd;
	req.xsinfo.family = preferred_family;

	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
	req.xsinfo.lft.hard_packet_limit = XFRM_INF;

	while (argc > 0) {
		if (strcmp(*argv, "mode") == 0) {
			NEXT_ARG();
			xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv);
		} else if (strcmp(*argv, "mark") == 0) {
			xfrm_parse_mark(&mark, &argc, &argv);
		} else if (strcmp(*argv, "reqid") == 0) {
			NEXT_ARG();
			xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv);
		} else if (strcmp(*argv, "seq") == 0) {
			NEXT_ARG();
			xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv);
		} else if (strcmp(*argv, "replay-window") == 0) {
			NEXT_ARG();
			if (get_u8(&req.xsinfo.replay_window, *argv, 0))
				invarg("\"replay-window\" value is invalid", *argv);
		} else if (strcmp(*argv, "replay-seq") == 0) {
			NEXT_ARG();
			if (get_u32(&replay.seq, *argv, 0))
				invarg("\"replay-seq\" value is invalid", *argv);
		} else if (strcmp(*argv, "replay-oseq") == 0) {
			NEXT_ARG();
			if (get_u32(&replay.oseq, *argv, 0))
				invarg("\"replay-oseq\" value is invalid", *argv);
		} else if (strcmp(*argv, "flag") == 0) {
			NEXT_ARG();
			xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv);
		} else if (strcmp(*argv, "sel") == 0) {
			NEXT_ARG();
			xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv);
		} else if (strcmp(*argv, "limit") == 0) {
			NEXT_ARG();
			xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv);
		} else if (strcmp(*argv, "encap") == 0) {
			struct xfrm_encap_tmpl encap;
			inet_prefix oa;
		        NEXT_ARG();
			xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
			NEXT_ARG();
			if (get_u16(&encap.encap_sport, *argv, 0))
				invarg("\"encap\" sport value is invalid", *argv);
			encap.encap_sport = htons(encap.encap_sport);
			NEXT_ARG();
			if (get_u16(&encap.encap_dport, *argv, 0))
				invarg("\"encap\" dport value is invalid", *argv);
			encap.encap_dport = htons(encap.encap_dport);
			NEXT_ARG();
			get_addr(&oa, *argv, AF_UNSPEC);
			memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
			addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP,
				  (void *)&encap, sizeof(encap));
		} else if (strcmp(*argv, "coa") == 0) {
			inet_prefix coa;
			xfrm_address_t xcoa;

			if (coap)
				duparg("coa", *argv);
			coap = *argv;

			NEXT_ARG();

			get_prefix(&coa, *argv, preferred_family);
			if (coa.family == AF_UNSPEC)
				invarg("\"coa\" address family is AF_UNSPEC", *argv);
			if (coa.bytelen > sizeof(xcoa))
				invarg("\"coa\" address length is too large", *argv);

			memset(&xcoa, 0, sizeof(xcoa));
			memcpy(&xcoa, &coa.data, coa.bytelen);

			addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
				  (void *)&xcoa, sizeof(xcoa));
		} else if (strcmp(*argv, "ctx") == 0) {
			char *context;

			if (sctxp)
				duparg("ctx", *argv);
			sctxp = *argv;

			NEXT_ARG();
			context = *argv;

			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
			addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX,
				  (void *)&ctx, ctx.sctx.len);
		} else {
			/* try to assume ALGO */
			int type = xfrm_algotype_getbyname(*argv);
			switch (type) {
			case XFRMA_ALG_AEAD:
			case XFRMA_ALG_CRYPT:
			case XFRMA_ALG_AUTH:
			case XFRMA_ALG_AUTH_TRUNC:
			case XFRMA_ALG_COMP:
			{
				/* ALGO */
				struct {
					union {
						struct xfrm_algo alg;
						struct xfrm_algo_aead aead;
						struct xfrm_algo_auth auth;
					} u;
					char buf[XFRM_ALGO_KEY_BUF_SIZE];
				} alg = {};
				int len;
				__u32 icvlen, trunclen;
				char *name;
				char *key;
				char *buf;

				switch (type) {
				case XFRMA_ALG_AEAD:
					if (aeadop)
						duparg("ALGO-TYPE", *argv);
					aeadop = *argv;
					break;
				case XFRMA_ALG_CRYPT:
					if (ealgop)
						duparg("ALGO-TYPE", *argv);
					ealgop = *argv;
					break;
				case XFRMA_ALG_AUTH:
				case XFRMA_ALG_AUTH_TRUNC:
					if (aalgop)
						duparg("ALGO-TYPE", *argv);
					aalgop = *argv;
					break;
				case XFRMA_ALG_COMP:
					if (calgop)
						duparg("ALGO-TYPE", *argv);
					calgop = *argv;
					break;
				default:
					/* not reached */
					invarg("\"ALGO-TYPE\" is invalid\n", *argv);
				}

				if (!NEXT_ARG_OK())
					missarg("ALGO-NAME");
				NEXT_ARG();
				name = *argv;

				if (!NEXT_ARG_OK())
					missarg("ALGO-KEY");
				NEXT_ARG();
				key = *argv;

				buf = alg.u.alg.alg_key;
				len = sizeof(alg.u.alg);

				switch (type) {
				case XFRMA_ALG_AEAD:
					if (!NEXT_ARG_OK())
						missarg("ALGO-ICV-LEN");
					NEXT_ARG();
					if (get_u32(&icvlen, *argv, 0))
						invarg("\"aead\" ICV length is invalid",
						       *argv);
					alg.u.aead.alg_icv_len = icvlen;

					buf = alg.u.aead.alg_key;
					len = sizeof(alg.u.aead);
					break;
				case XFRMA_ALG_AUTH_TRUNC:
					if (!NEXT_ARG_OK())
						missarg("ALGO-TRUNC-LEN");
					NEXT_ARG();
					if (get_u32(&trunclen, *argv, 0))
						invarg("\"auth\" trunc length is invalid",
						       *argv);
					alg.u.auth.alg_trunc_len = trunclen;

					buf = alg.u.auth.alg_key;
					len = sizeof(alg.u.auth);
					break;
				}

				xfrm_algo_parse((void *)&alg, type, name, key,
						buf, sizeof(alg.buf));
				len += alg.u.alg.alg_key_len;

				addattr_l(&req.n, sizeof(req.buf), type,
					  (void *)&alg, len);
				break;
			}
			default:
				/* try to assume ID */
				if (idp)
					invarg("unknown", *argv);
				idp = *argv;

				/* ID */
				xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id,
					      &req.xsinfo.family, 0, &argc, &argv);
				if (preferred_family == AF_UNSPEC)
					preferred_family = req.xsinfo.family;
			}
		}
		argc--; argv++;
	}

	if (replay.seq || replay.oseq)
		addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
			  (void *)&replay, sizeof(replay));

	if (!idp) {
		fprintf(stderr, "Not enough information: \"ID\" is required\n");
		exit(1);
	}

	if (mark.m & mark.v) {
		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
				  (void *)&mark, sizeof(mark));
		if (r < 0) {
			fprintf(stderr, "XFRMA_MARK failed\n");
			exit(1);
		}
	}

	switch (req.xsinfo.mode) {
	case XFRM_MODE_TRANSPORT:
	case XFRM_MODE_TUNNEL:
		if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"mode\" is invalid with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
		break;
	case XFRM_MODE_ROUTEOPTIMIZATION:
	case XFRM_MODE_IN_TRIGGER:
		if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"mode\" is invalid with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
		if (req.xsinfo.id.spi != 0) {
			fprintf(stderr, "\"spi\" must be 0 with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
		break;
	default:
		break;
	}

	if (aeadop || ealgop || aalgop || calgop) {
		if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	} else {
		if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"ALGO\" is required with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit (1);
		}
	}

	if (coap) {
		if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"coa\" is invalid with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	} else {
		if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
			fprintf(stderr, "\"coa\" is required with proto=%s\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit (1);
		}
	}

	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
		exit(1);

	if (req.xsinfo.family == AF_UNSPEC)
		req.xsinfo.family = AF_INET;

	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
		exit(2);

	rtnl_close(&rth);

	return 0;
}
Ejemplo n.º 7
0
static int xfrm_selector_upspec_parse(struct xfrm_selector *sel,
				      int *argcp, char ***argvp)
{
	int argc = *argcp;
	char **argv = *argvp;
	char *sportp = NULL;
	char *dportp = NULL;
	char *typep = NULL;
	char *codep = NULL;

	while (1) {
		if (strcmp(*argv, "proto") == 0) {
			__u8 upspec;

			NEXT_ARG();

			if (strcmp(*argv, "any") == 0)
				upspec = 0;
			else {
				struct protoent *pp;
				pp = getprotobyname(*argv);
				if (pp)
					upspec = pp->p_proto;
				else {
					if (get_u8(&upspec, *argv, 0))
						invarg("\"PROTO\" is invalid", *argv);
				}
			}
			sel->proto = upspec;

			filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL;

		} else if (strcmp(*argv, "sport") == 0) {
			sportp = *argv;

			NEXT_ARG();

			if (get_u16(&sel->sport, *argv, 0))
				invarg("\"PORT\" is invalid", *argv);
			sel->sport = htons(sel->sport);
			if (sel->sport)
				sel->sport_mask = ~((__u16)0);

			filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL;

		} else if (strcmp(*argv, "dport") == 0) {
			dportp = *argv;

			NEXT_ARG();

			if (get_u16(&sel->dport, *argv, 0))
				invarg("\"PORT\" is invalid", *argv);
			sel->dport = htons(sel->dport);
			if (sel->dport)
				sel->dport_mask = ~((__u16)0);

			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;

		} else if (strcmp(*argv, "type") == 0) {
			typep = *argv;

			NEXT_ARG();

			if (get_u16(&sel->sport, *argv, 0) ||
			    (sel->sport & ~((__u16)0xff)))
				invarg("\"type\" value is invalid", *argv);
			sel->sport = htons(sel->sport);
			sel->sport_mask = ~((__u16)0);

			filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL;


		} else if (strcmp(*argv, "code") == 0) {
			codep = *argv;

			NEXT_ARG();

			if (get_u16(&sel->dport, *argv, 0) ||
			    (sel->dport & ~((__u16)0xff)))
				invarg("\"code\" value is invalid", *argv);
			sel->dport = htons(sel->dport);
			sel->dport_mask = ~((__u16)0);

			filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL;

		} else {
			PREV_ARG(); /* back track */
			break;
		}

		if (!NEXT_ARG_OK())
			break;
		NEXT_ARG();
	}
	if (argc == *argcp)
		missarg("UPSPEC");
	if (sportp || dportp) {
		switch (sel->proto) {
		case IPPROTO_TCP:
		case IPPROTO_UDP:
		case IPPROTO_SCTP:
		case IPPROTO_DCCP:
			break;
		default:
			fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto));
			exit(1);
		}
	}
	if (typep || codep) {
		switch (sel->proto) {
		case IPPROTO_ICMP:
		case IPPROTO_ICMPV6:
		case IPPROTO_MH:
			break;
		default:
			fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto));
			exit(1);
		}
	}

	*argcp = argc;
	*argvp = argv;

	return 0;
}
Ejemplo n.º 8
0
int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp)
{
	int argc = *argcp;
	char **argv = *argvp;
	inet_prefix dst;
	inet_prefix src;
	char *upspecp = NULL;

	memset(&dst, 0, sizeof(dst));
	memset(&src, 0, sizeof(src));

	while (1) {
		if (strcmp(*argv, "src") == 0) {
			NEXT_ARG();

			get_prefix(&src, *argv, preferred_family);
			if (src.family == AF_UNSPEC)
				invarg("\"src\" address family is AF_UNSPEC", *argv);
			sel->family = src.family;

			memcpy(&sel->saddr, &src.data, sizeof(sel->saddr));
			sel->prefixlen_s = src.bitlen;

			filter.sel_src_mask = src.bitlen;

		} else if (strcmp(*argv, "dst") == 0) {
			NEXT_ARG();

			get_prefix(&dst, *argv, preferred_family);
			if (dst.family == AF_UNSPEC)
				invarg("\"dst\" address family is AF_UNSPEC", *argv);
			sel->family = dst.family;

			memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr));
			sel->prefixlen_d = dst.bitlen;

			filter.sel_dst_mask = dst.bitlen;

		} else if (strcmp(*argv, "dev") == 0) {
			int ifindex;

			NEXT_ARG();

			if (strcmp(*argv, "none") == 0)
				ifindex = 0;
			else {
				ifindex = ll_name_to_index(*argv);
				if (ifindex <= 0)
					invarg("\"DEV\" is invalid", *argv);
			}
			sel->ifindex = ifindex;

			filter.sel_dev_mask = XFRM_FILTER_MASK_FULL;

		} else {
			if (upspecp) {
				PREV_ARG(); /* back track */
				break;
			} else {
				upspecp = *argv;
				xfrm_selector_upspec_parse(sel, &argc, &argv);
			}
		}

		if (!NEXT_ARG_OK())
			break;

		NEXT_ARG();
	}

	if (src.family && dst.family && (src.family != dst.family))
		invarg("the same address family is required between \"src\" and \"dst\"", *argv);

	if (argc == *argcp)
		missarg("SELECTOR");

	*argcp = argc;
	*argvp = argv;

	return 0;
}
Ejemplo n.º 9
0
int iplink_parse(int argc, char **argv, struct iplink_req *req,
		char **name, char **type, char **link, char **dev, int *group, int *index)
{
	int ret, len;
	char abuf[32];
	int qlen = -1;
	int mtu = -1;
	int netns = -1;
	int vf = -1;
	int numtxqueues = -1;
	int numrxqueues = -1;
	int dev_index = 0;

	*group = -1;
	ret = argc;

	while (argc > 0) {
		if (strcmp(*argv, "up") == 0) {
			req->i.ifi_change |= IFF_UP;
			req->i.ifi_flags |= IFF_UP;
		} else if (strcmp(*argv, "down") == 0) {
			req->i.ifi_change |= IFF_UP;
			req->i.ifi_flags &= ~IFF_UP;
		} else if (strcmp(*argv, "name") == 0) {
			NEXT_ARG();
			*name = *argv;
		} else if (strcmp(*argv, "index") == 0) {
			NEXT_ARG();
			*index = atoi(*argv);
		} else if (matches(*argv, "link") == 0) {
			NEXT_ARG();
			*link = *argv;
		} else if (matches(*argv, "address") == 0) {
			NEXT_ARG();
			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
			if (len < 0)
				return -1;
			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
		} else if (matches(*argv, "broadcast") == 0 ||
				strcmp(*argv, "brd") == 0) {
			NEXT_ARG();
			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
			if (len < 0)
				return -1;
			addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
		} else if (matches(*argv, "txqueuelen") == 0 ||
				strcmp(*argv, "qlen") == 0 ||
				matches(*argv, "txqlen") == 0) {
			NEXT_ARG();
			if (qlen != -1)
				duparg("txqueuelen", *argv);
			if (get_integer(&qlen,  *argv, 0))
				invarg("Invalid \"txqueuelen\" value\n", *argv);
			addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
		} else if (strcmp(*argv, "mtu") == 0) {
			NEXT_ARG();
			if (mtu != -1)
				duparg("mtu", *argv);
			if (get_integer(&mtu, *argv, 0))
				invarg("Invalid \"mtu\" value\n", *argv);
			addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
		} else if (strcmp(*argv, "netns") == 0) {
			NEXT_ARG();
			if (netns != -1)
				duparg("netns", *argv);
			if ((netns = get_netns_fd(*argv)) >= 0)
				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
			else if (get_integer(&netns, *argv, 0) == 0)
				addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
			else
				invarg("Invalid \"netns\" value\n", *argv);
		} else if (strcmp(*argv, "multicast") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_MULTICAST;
			if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags |= IFF_MULTICAST;
			} else if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags &= ~IFF_MULTICAST;
			} else
				return on_off("multicast", *argv);
		} else if (strcmp(*argv, "allmulticast") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_ALLMULTI;
			if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags |= IFF_ALLMULTI;
			} else if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags &= ~IFF_ALLMULTI;
			} else
				return on_off("allmulticast", *argv);
		} else if (strcmp(*argv, "promisc") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_PROMISC;
			if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags |= IFF_PROMISC;
			} else if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags &= ~IFF_PROMISC;
			} else
				return on_off("promisc", *argv);
		} else if (strcmp(*argv, "trailers") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_NOTRAILERS;
			if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags |= IFF_NOTRAILERS;
			} else if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags &= ~IFF_NOTRAILERS;
			} else
				return on_off("trailers", *argv);
		} else if (strcmp(*argv, "arp") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_NOARP;
			if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags &= ~IFF_NOARP;
			} else if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags |= IFF_NOARP;
			} else
				return on_off("noarp", *argv);
		} else if (strcmp(*argv, "vf") == 0) {
			struct rtattr *vflist;
			NEXT_ARG();
			if (get_integer(&vf,  *argv, 0)) {
				invarg("Invalid \"vf\" value\n", *argv);
			}
			vflist = addattr_nest(&req->n, sizeof(*req),
					      IFLA_VFINFO_LIST);
			if (dev_index == 0)
				missarg("dev");

			len = iplink_parse_vf(vf, &argc, &argv, req, dev_index);
			if (len < 0)
				return -1;
			addattr_nest_end(&req->n, vflist);
		} else if (matches(*argv, "master") == 0) {
			int ifindex;
			NEXT_ARG();
			ifindex = ll_name_to_index(*argv);
			if (!ifindex)
				invarg("Device does not exist\n", *argv);
			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
				  &ifindex, 4);
		} else if (matches(*argv, "nomaster") == 0) {
			int ifindex = 0;
			addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
				  &ifindex, 4);
		} else if (matches(*argv, "dynamic") == 0) {
			NEXT_ARG();
			req->i.ifi_change |= IFF_DYNAMIC;
			if (strcmp(*argv, "on") == 0) {
				req->i.ifi_flags |= IFF_DYNAMIC;
			} else if (strcmp(*argv, "off") == 0) {
				req->i.ifi_flags &= ~IFF_DYNAMIC;
			} else
				return on_off("dynamic", *argv);
		} else if (matches(*argv, "type") == 0) {
			NEXT_ARG();
			*type = *argv;
			argc--; argv++;
			break;
		} else if (matches(*argv, "alias") == 0) {
			NEXT_ARG();
			addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
				  *argv, strlen(*argv));
			argc--; argv++;
			break;
		} else if (strcmp(*argv, "group") == 0) {
			NEXT_ARG();
			if (*group != -1)
				duparg("group", *argv);
			if (rtnl_group_a2n(group, *argv))
				invarg("Invalid \"group\" value\n", *argv);
		} else if (strcmp(*argv, "mode") == 0) {
			int mode;
			NEXT_ARG();
			mode = get_link_mode(*argv);
			if (mode < 0)
				invarg("Invalid link mode\n", *argv);
			addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode);
		} else if (strcmp(*argv, "state") == 0) {
			int state;
			NEXT_ARG();
			state = get_operstate(*argv);
			if (state < 0)
				invarg("Invalid operstate\n", *argv);

			addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state);
		} else if (matches(*argv, "numtxqueues") == 0) {
			NEXT_ARG();
			if (numtxqueues != -1)
				duparg("numtxqueues", *argv);
			if (get_integer(&numtxqueues, *argv, 0))
				invarg("Invalid \"numtxqueues\" value\n", *argv);
			addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES,
				  &numtxqueues, 4);
		} else if (matches(*argv, "numrxqueues") == 0) {
			NEXT_ARG();
			if (numrxqueues != -1)
				duparg("numrxqueues", *argv);
			if (get_integer(&numrxqueues, *argv, 0))
				invarg("Invalid \"numrxqueues\" value\n", *argv);
			addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
				  &numrxqueues, 4);
		} else {
			if (strcmp(*argv, "dev") == 0) {
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (*dev)
				duparg2("dev", *argv);
			*dev = *argv;
			dev_index = ll_name_to_index(*dev);
			if (dev_index == 0)
				invarg("Unknown device", *argv);
		}
		argc--; argv++;
	}

	return ret - argc;
}
Ejemplo n.º 10
0
int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
		  int loose, int *argcp, char ***argvp)
{
	int argc = *argcp;
	char **argv = *argvp;
	inet_prefix dst;
	inet_prefix src;

	memset(&dst, 0, sizeof(dst));
	memset(&src, 0, sizeof(src));

	while (1) {
		if (strcmp(*argv, "src") == 0) {
			NEXT_ARG();

			get_prefix(&src, *argv, preferred_family);
			if (src.family == AF_UNSPEC)
				invarg("value after \"src\" has an unrecognized address family", *argv);
			if (family)
				*family = src.family;

			memcpy(saddr, &src.data, sizeof(*saddr));

			filter.id_src_mask = src.bitlen;

		} else if (strcmp(*argv, "dst") == 0) {
			NEXT_ARG();

			get_prefix(&dst, *argv, preferred_family);
			if (dst.family == AF_UNSPEC)
				invarg("value after \"dst\" has an unrecognized address family", *argv);
			if (family)
				*family = dst.family;

			memcpy(&id->daddr, &dst.data, sizeof(id->daddr));

			filter.id_dst_mask = dst.bitlen;

		} else if (strcmp(*argv, "proto") == 0) {
			int ret;

			NEXT_ARG();

			ret = xfrm_xfrmproto_getbyname(*argv);
			if (ret < 0)
				invarg("XFRM-PROTO value is invalid", *argv);

			id->proto = (__u8)ret;

			filter.id_proto_mask = XFRM_FILTER_MASK_FULL;

		} else if (strcmp(*argv, "spi") == 0) {
			__u32 spi;

			NEXT_ARG();
			if (get_u32(&spi, *argv, 0))
				invarg("SPI value is invalid", *argv);

			spi = htonl(spi);
			id->spi = spi;

			filter.id_spi_mask = XFRM_FILTER_MASK_FULL;

		} else {
			PREV_ARG(); /* back track */
			break;
		}

		if (!NEXT_ARG_OK())
			break;
		NEXT_ARG();
	}

	if (src.family && dst.family && (src.family != dst.family))
		invarg("the same address family is required between values after \"src\" and \"dst\"", *argv);

	if (id->spi && id->proto) {
		if (xfrm_xfrmproto_is_ro(id->proto)) {
			fprintf(stderr, "\"spi\" is invalid with XFRM-PROTO value \"%s\"\n",
			        strxf_xfrmproto(id->proto));
			exit(1);
		} else if (id->proto == IPPROTO_COMP && ntohl(id->spi) >= 0x10000) {
			fprintf(stderr, "SPI value is too large with XFRM-PROTO value \"%s\"\n",
			        strxf_xfrmproto(id->proto));
			exit(1);
		}
	}

	if (loose == 0 && id->proto == 0)
		missarg("XFRM-PROTO");
	if (argc == *argcp)
		missarg("ID");

	*argcp = argc;
	*argvp = argv;

	return 0;
}
Ejemplo n.º 11
0
static int ipntable_modify(int cmd, int flags, int argc, char **argv)
{
	struct {
		struct nlmsghdr 	n;
		struct ndtmsg		ndtm;
		char   			buf[1024];
	} req;
	char *namep = NULL;
	char *threshsp = NULL;
	char *gc_intp = NULL;
	char parms_buf[1024];
	struct rtattr *parms_rta = (struct rtattr *)parms_buf;
	int parms_change = 0;

	memset(&req, 0, sizeof(req));

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
	req.n.nlmsg_type = cmd;

	req.ndtm.ndtm_family = preferred_family;
	req.ndtm.ndtm_pad1 = 0;
	req.ndtm.ndtm_pad2 = 0;

	memset(&parms_buf, 0, sizeof(parms_buf));

	parms_rta->rta_type = NDTA_PARMS;
	parms_rta->rta_len = RTA_LENGTH(0);

	while (argc > 0) {
		if (strcmp(*argv, "name") == 0) {
			int len;

			NEXT_ARG();
			if (namep)
				duparg("NAME", *argv);

			namep = *argv;
			len = strlen(namep) + 1;
			addattr_l(&req.n, sizeof(req), NDTA_NAME, namep, len);
		} else if (strcmp(*argv, "thresh1") == 0) {
			__u32 thresh1;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh1, *argv, 0))
				invarg("\"thresh1\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH1, thresh1);
		} else if (strcmp(*argv, "thresh2") == 0) {
			__u32 thresh2;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh2, *argv, 0))
				invarg("\"thresh2\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH2, thresh2);
		} else if (strcmp(*argv, "thresh3") == 0) {
			__u32 thresh3;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh3, *argv, 0))
				invarg("\"thresh3\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH3, thresh3);
		} else if (strcmp(*argv, "gc_int") == 0) {
			__u64 gc_int;

			NEXT_ARG();
			gc_intp = *argv;

			if (get_u64(&gc_int, *argv, 0))
				invarg("\"gc_int\" value is invalid", *argv);

			addattr_l(&req.n, sizeof(req), NDTA_GC_INTERVAL,
				  &gc_int, sizeof(gc_int));
		} else if (strcmp(*argv, "dev") == 0) {
			__u32 ifindex;

			NEXT_ARG();
			ifindex = ll_name_to_index(*argv);
			if (ifindex == 0) {
				fprintf(stderr, "Cannot find device \"%s\"\n", *argv);
				return -1;
			}

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_IFINDEX, ifindex);
		} else if (strcmp(*argv, "base_reachable") == 0) {
			__u64 breachable;

			NEXT_ARG();

			if (get_u64(&breachable, *argv, 0))
				invarg("\"base_reachable\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_BASE_REACHABLE_TIME,
				      &breachable, sizeof(breachable));
			parms_change = 1;
		} else if (strcmp(*argv, "retrans") == 0) {
			__u64 retrans;

			NEXT_ARG();

			if (get_u64(&retrans, *argv, 0))
				invarg("\"retrans\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_RETRANS_TIME,
				      &retrans, sizeof(retrans));
			parms_change = 1;
		} else if (strcmp(*argv, "gc_stale") == 0) {
			__u64 gc_stale;

			NEXT_ARG();

			if (get_u64(&gc_stale, *argv, 0))
				invarg("\"gc_stale\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_GC_STALETIME,
				      &gc_stale, sizeof(gc_stale));
			parms_change = 1;
		} else if (strcmp(*argv, "delay_probe") == 0) {
			__u64 delay_probe;

			NEXT_ARG();

			if (get_u64(&delay_probe, *argv, 0))
				invarg("\"delay_probe\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_DELAY_PROBE_TIME,
				      &delay_probe, sizeof(delay_probe));
			parms_change = 1;
		} else if (strcmp(*argv, "queue") == 0) {
			__u32 queue;

			NEXT_ARG();

			if (get_u32(&queue, *argv, 0))
				invarg("\"queue\" value is invalid", *argv);

			if (!parms_rta)
				parms_rta = (struct rtattr *)&parms_buf;
			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_QUEUE_LEN, queue);
			parms_change = 1;
		} else if (strcmp(*argv, "app_probes") == 0) {
			__u32 aprobe;

			NEXT_ARG();

			if (get_u32(&aprobe, *argv, 0))
				invarg("\"app_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_APP_PROBES, aprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "ucast_probes") == 0) {
			__u32 uprobe;

			NEXT_ARG();

			if (get_u32(&uprobe, *argv, 0))
				invarg("\"ucast_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_UCAST_PROBES, uprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "mcast_probes") == 0) {
			__u32 mprobe;

			NEXT_ARG();

			if (get_u32(&mprobe, *argv, 0))
				invarg("\"mcast_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_MCAST_PROBES, mprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "anycast_delay") == 0) {
			__u64 anycast_delay;

			NEXT_ARG();

			if (get_u64(&anycast_delay, *argv, 0))
				invarg("\"anycast_delay\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_ANYCAST_DELAY,
				      &anycast_delay, sizeof(anycast_delay));
			parms_change = 1;
		} else if (strcmp(*argv, "proxy_delay") == 0) {
			__u64 proxy_delay;

			NEXT_ARG();

			if (get_u64(&proxy_delay, *argv, 0))
				invarg("\"proxy_delay\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_PROXY_DELAY,
				      &proxy_delay, sizeof(proxy_delay));
			parms_change = 1;
		} else if (strcmp(*argv, "proxy_queue") == 0) {
			__u32 pqueue;

			NEXT_ARG();

			if (get_u32(&pqueue, *argv, 0))
				invarg("\"proxy_queue\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_PROXY_QLEN, pqueue);
			parms_change = 1;
		} else if (strcmp(*argv, "locktime") == 0) {
			__u64 locktime;

			NEXT_ARG();

			if (get_u64(&locktime, *argv, 0))
				invarg("\"locktime\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_LOCKTIME,
				      &locktime, sizeof(locktime));
			parms_change = 1;
		} else {
			invarg("unknown", *argv);
		}

		argc--; argv++;
	}

	if (!namep)
		missarg("NAME");
	if (!threshsp && !gc_intp && !parms_change) {
		fprintf(stderr, "Not enough information: changable attributes required.\n");
		exit(-1);
	}

	if (parms_rta->rta_len > RTA_LENGTH(0)) {
		addattr_l(&req.n, sizeof(req), NDTA_PARMS, RTA_DATA(parms_rta),
			  RTA_PAYLOAD(parms_rta));
	}

	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
		exit(2);

	return 0;
}
Ejemplo n.º 12
0
static int tcpm_do_cmd(int cmd, int argc, char **argv)
{
	TCPM_REQUEST(req, 1024, TCP_METRICS_CMD_GET, NLM_F_REQUEST);
	int atype = -1, stype = -1;
	int ack;

	memset(&f, 0, sizeof(f));
	f.daddr.bitlen = -1;
	f.daddr.family = preferred_family;
	f.saddr.bitlen = -1;
	f.saddr.family = preferred_family;

	switch (preferred_family) {
	case AF_UNSPEC:
	case AF_INET:
	case AF_INET6:
		break;
	default:
		fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family);
		return -1;
	}

	for (; argc > 0; argc--, argv++) {
		if (strcmp(*argv, "src") == 0 ||
		    strcmp(*argv, "source") == 0) {
			char *who = *argv;

			NEXT_ARG();
			if (matches(*argv, "help") == 0)
				usage();
			if (f.saddr.bitlen >= 0)
				duparg2(who, *argv);

			get_prefix(&f.saddr, *argv, preferred_family);
			if (f.saddr.bytelen && f.saddr.bytelen * 8 == f.saddr.bitlen) {
				if (f.saddr.family == AF_INET)
					stype = TCP_METRICS_ATTR_SADDR_IPV4;
				else if (f.saddr.family == AF_INET6)
					stype = TCP_METRICS_ATTR_SADDR_IPV6;
			}

			if (stype < 0) {
				fprintf(stderr, "Error: a specific IP address is expected rather than \"%s\"\n",
					*argv);
				return -1;
			}
		} else {
			char *who = "address";

			if (strcmp(*argv, "addr") == 0 ||
			    strcmp(*argv, "address") == 0) {
				who = *argv;
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (f.daddr.bitlen >= 0)
				duparg2(who, *argv);

			get_prefix(&f.daddr, *argv, preferred_family);
			if (f.daddr.bytelen && f.daddr.bytelen * 8 == f.daddr.bitlen) {
				if (f.daddr.family == AF_INET)
					atype = TCP_METRICS_ATTR_ADDR_IPV4;
				else if (f.daddr.family == AF_INET6)
					atype = TCP_METRICS_ATTR_ADDR_IPV6;
			}
			if ((CMD_DEL & cmd) && atype < 0) {
				fprintf(stderr, "Error: a specific IP address is expected rather than \"%s\"\n",
					*argv);
				return -1;
			}
		}
		argc--; argv++;
	}

	if (cmd == CMD_DEL && atype < 0)
		missarg("address");

	/* flush for exact address ? Single del */
	if (cmd == CMD_FLUSH && atype >= 0)
		cmd = CMD_DEL;

	/* flush for all addresses ? Single del without address */
	if (cmd == CMD_FLUSH && f.daddr.bitlen <= 0 &&
	    f.saddr.bitlen <= 0 && preferred_family == AF_UNSPEC) {
		cmd = CMD_DEL;
		req.g.cmd = TCP_METRICS_CMD_DEL;
		ack = 1;
	} else if (cmd == CMD_DEL) {
		req.g.cmd = TCP_METRICS_CMD_DEL;
		ack = 1;
	} else {	/* CMD_FLUSH, CMD_LIST */
		ack = 0;
	}

	if (genl_init_handle(&grth, TCP_METRICS_GENL_NAME, &genl_family))
		exit(1);
	req.n.nlmsg_type = genl_family;

	if (!(cmd & CMD_FLUSH) && (atype >= 0 || (cmd & CMD_DEL))) {
		if (ack)
			req.n.nlmsg_flags |= NLM_F_ACK;
		if (atype >= 0)
			addattr_l(&req.n, sizeof(req), atype, &f.daddr.data,
				  f.daddr.bytelen);
		if (stype >= 0)
			addattr_l(&req.n, sizeof(req), stype, &f.saddr.data,
				  f.saddr.bytelen);
	} else {
		req.n.nlmsg_flags |= NLM_F_DUMP;
	}

	f.cmd = cmd;
	if (cmd & CMD_FLUSH) {
		int round = 0;
		char flushb[4096-512];

		f.flushb = flushb;
		f.flushp = 0;
		f.flushe = sizeof(flushb);

		for (;;) {
			req.n.nlmsg_seq = grth.dump = ++grth.seq;
			if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
				perror("Failed to send flush request");
				exit(1);
			}
			f.flushed = 0;
			if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
				fprintf(stderr, "Flush terminated\n");
				exit(1);
			}
			if (f.flushed == 0) {
				if (round == 0) {
					fprintf(stderr, "Nothing to flush.\n");
				} else if (show_stats)
					printf("*** Flush is complete after %d round%s ***\n",
					       round, round > 1 ? "s" : "");
				fflush(stdout);
				return 0;
			}
			round++;
			if (flush_update() < 0)
				exit(1);
			if (show_stats) {
				printf("\n*** Round %d, deleting %d entries ***\n",
				       round, f.flushed);
				fflush(stdout);
			}
		}
		return 0;
	}

	if (ack) {
		if (rtnl_talk(&grth, &req.n, NULL, 0) < 0)
			return -2;
	} else if (atype >= 0) {
		if (rtnl_talk(&grth, &req.n, &req.n, sizeof(req)) < 0)
			return -2;
		if (process_msg(NULL, &req.n, stdout) < 0) {
			fprintf(stderr, "Dump terminated\n");
			exit(1);
		}
	} else {
		req.n.nlmsg_seq = grth.dump = ++grth.seq;
		if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
			perror("Failed to send dump request");
			exit(1);
		}

		if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
			fprintf(stderr, "Dump terminated\n");
			exit(1);
		}
	}
	return 0;
}
Ejemplo n.º 13
0
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
{
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr 	n;
		struct xfrm_usersa_info xsinfo;
		char   			buf[RTA_BUF_SIZE];
	} req;
	char *idp = NULL;
	char *ealgop = NULL;
	char *aalgop = NULL;
	char *calgop = NULL;

	memset(&req, 0, sizeof(req));

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
	req.n.nlmsg_type = cmd;
	req.xsinfo.family = preferred_family;

	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
	req.xsinfo.lft.hard_packet_limit = XFRM_INF;

	while (argc > 0) {
		if (strcmp(*argv, "mode") == 0) {
			NEXT_ARG();
			xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv);
		} else if (strcmp(*argv, "reqid") == 0) {
			NEXT_ARG();
			xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv);
		} else if (strcmp(*argv, "seq") == 0) {
			NEXT_ARG();
			xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv);
		} else if (strcmp(*argv, "replay-window") == 0) {
			NEXT_ARG();
			if (get_u8(&req.xsinfo.replay_window, *argv, 0))
				invarg("\"replay-window\" value is invalid", *argv);
		} else if (strcmp(*argv, "flag") == 0) {
			NEXT_ARG();
			xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv);
		} else if (strcmp(*argv, "sel") == 0) {
			NEXT_ARG();
			xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv);
		} else if (strcmp(*argv, "limit") == 0) {
			NEXT_ARG();
			xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv);
		} else if (strcmp(*argv, "encap") == 0) {
			struct xfrm_encap_tmpl encap;
			inet_prefix oa;
		        NEXT_ARG();
			xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
			NEXT_ARG();
			if (get_u16(&encap.encap_sport, *argv, 0))
				invarg("\"encap\" sport value is invalid", *argv);
			encap.encap_sport = htons(encap.encap_sport);
			NEXT_ARG();
			if (get_u16(&encap.encap_dport, *argv, 0))
				invarg("\"encap\" dport value is invalid", *argv);
			encap.encap_dport = htons(encap.encap_dport);
			NEXT_ARG();
			get_addr(&oa, *argv, AF_UNSPEC);
			memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
			addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP,
				  (void *)&encap, sizeof(encap));
		} else {
			/* try to assume ALGO */
			int type = xfrm_algotype_getbyname(*argv);
			switch (type) {
			case XFRMA_ALG_CRYPT:
			case XFRMA_ALG_AUTH:
			case XFRMA_ALG_COMP:
			{
				/* ALGO */
				struct {
					struct xfrm_algo alg;
					char buf[XFRM_ALGO_KEY_BUF_SIZE];
				} alg;
				int len;
				char *name;
				char *key;

				switch (type) {
				case XFRMA_ALG_CRYPT:
					if (ealgop)
						duparg("ALGOTYPE", *argv);
					ealgop = *argv;
					break;
				case XFRMA_ALG_AUTH:
					if (aalgop)
						duparg("ALGOTYPE", *argv);
					aalgop = *argv;
					break;
				case XFRMA_ALG_COMP:
					if (calgop)
						duparg("ALGOTYPE", *argv);
					calgop = *argv;
					break;
				default:
					/* not reached */
					invarg("\"ALGOTYPE\" is invalid\n", *argv);
				}

				if (!NEXT_ARG_OK())
					missarg("ALGONAME");
				NEXT_ARG();
				name = *argv;

				if (!NEXT_ARG_OK())
					missarg("ALGOKEY");
				NEXT_ARG();
				key = *argv;

				memset(&alg, 0, sizeof(alg));

				xfrm_algo_parse((void *)&alg, type, name, key,
						sizeof(alg.buf));
				len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len;

				addattr_l(&req.n, sizeof(req.buf), type,
					  (void *)&alg, len);
				break;
			}
			default:
				/* try to assume ID */
				if (idp)
					invarg("unknown", *argv);
				idp = *argv;

				/* ID */
				xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id,
					      &req.xsinfo.family, 0, &argc, &argv);
				if (preferred_family == AF_UNSPEC)
					preferred_family = req.xsinfo.family;
			}
		}
		argc--; argv++;
	}

	if (!idp) {
//		fprintf(stderr, "Not enough information: \"ID\" is required\n");
		exit(1);
	}

	if (ealgop || aalgop || calgop) {
		if (req.xsinfo.id.proto != IPPROTO_ESP &&
		    req.xsinfo.id.proto != IPPROTO_AH &&
		    req.xsinfo.id.proto != IPPROTO_COMP) {
//			fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	} else {
		if (req.xsinfo.id.proto == IPPROTO_ESP ||
		    req.xsinfo.id.proto == IPPROTO_AH ||
		    req.xsinfo.id.proto == IPPROTO_COMP) {
//			fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto));
			exit (1);
		}
	}

	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
		exit(1);

	if (req.xsinfo.family == AF_UNSPEC)
		req.xsinfo.family = AF_INET;

	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
		exit(2);

	rtnl_close(&rth);

	return 0;
}
Ejemplo n.º 14
0
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
{
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr	n;
		struct xfrm_usersa_info xsinfo;
		char  			buf[RTA_BUF_SIZE];
	} req;
	struct xfrm_replay_state replay;
	struct xfrm_replay_state_esn replay_esn;
	__u32 replay_window = 0;
	__u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0;
	char *idp = NULL;
	char *aeadop = NULL;
	char *ealgop = NULL;
	char *aalgop = NULL;
	char *calgop = NULL;
	char *coap = NULL;
	char *sctxp = NULL;
	__u32 extra_flags = 0;
	struct xfrm_mark mark = {0, 0};
	struct {
		struct xfrm_user_sec_ctx sctx;
		char    str[CTX_BUF_SIZE];
	} ctx;

	memset(&req, 0, sizeof(req));
	memset(&replay, 0, sizeof(replay));
	memset(&replay_esn, 0, sizeof(replay_esn));
	memset(&ctx, 0, sizeof(ctx));

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo));
	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
	req.n.nlmsg_type = cmd;
	req.xsinfo.family = preferred_family;

	req.xsinfo.lft.soft_byte_limit = XFRM_INF;
	req.xsinfo.lft.hard_byte_limit = XFRM_INF;
	req.xsinfo.lft.soft_packet_limit = XFRM_INF;
	req.xsinfo.lft.hard_packet_limit = XFRM_INF;

	while (argc > 0) {
		if (strcmp(*argv, "mode") == 0) {
			NEXT_ARG();
			xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv);
		} else if (strcmp(*argv, "mark") == 0) {
			xfrm_parse_mark(&mark, &argc, &argv);
		} else if (strcmp(*argv, "reqid") == 0) {
			NEXT_ARG();
			xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv);
		} else if (strcmp(*argv, "seq") == 0) {
			NEXT_ARG();
			xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv);
		} else if (strcmp(*argv, "replay-window") == 0) {
			NEXT_ARG();
			if (get_u32(&replay_window, *argv, 0))
				invarg("value after \"replay-window\" is invalid", *argv);
		} else if (strcmp(*argv, "replay-seq") == 0) {
			NEXT_ARG();
			if (get_u32(&seq, *argv, 0))
				invarg("value after \"replay-seq\" is invalid", *argv);
		} else if (strcmp(*argv, "replay-seq-hi") == 0) {
			NEXT_ARG();
			if (get_u32(&seq_hi, *argv, 0))
				invarg("value after \"replay-seq-hi\" is invalid", *argv);
		} else if (strcmp(*argv, "replay-oseq") == 0) {
			NEXT_ARG();
			if (get_u32(&oseq, *argv, 0))
				invarg("value after \"replay-oseq\" is invalid", *argv);
		} else if (strcmp(*argv, "replay-oseq-hi") == 0) {
			NEXT_ARG();
			if (get_u32(&oseq_hi, *argv, 0))
				invarg("value after \"replay-oseq-hi\" is invalid", *argv);
		} else if (strcmp(*argv, "flag") == 0) {
			NEXT_ARG();
			xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv);
		} else if (strcmp(*argv, "extra-flag") == 0) {
			NEXT_ARG();
			xfrm_state_extra_flag_parse(&extra_flags, &argc, &argv);
		} else if (strcmp(*argv, "sel") == 0) {
			NEXT_ARG();
			preferred_family = AF_UNSPEC;
			xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv);
			preferred_family = req.xsinfo.sel.family;
		} else if (strcmp(*argv, "limit") == 0) {
			NEXT_ARG();
			xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv);
		} else if (strcmp(*argv, "encap") == 0) {
			struct xfrm_encap_tmpl encap;
			inet_prefix oa;
		        NEXT_ARG();
			xfrm_encap_type_parse(&encap.encap_type, &argc, &argv);
			NEXT_ARG();
			if (get_u16(&encap.encap_sport, *argv, 0))
				invarg("SPORT value after \"encap\" is invalid", *argv);
			encap.encap_sport = htons(encap.encap_sport);
			NEXT_ARG();
			if (get_u16(&encap.encap_dport, *argv, 0))
				invarg("DPORT value after \"encap\" is invalid", *argv);
			encap.encap_dport = htons(encap.encap_dport);
			NEXT_ARG();
			get_addr(&oa, *argv, AF_UNSPEC);
			memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa));
			addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP,
				  (void *)&encap, sizeof(encap));
		} else if (strcmp(*argv, "coa") == 0) {
			inet_prefix coa;
			xfrm_address_t xcoa;

			if (coap)
				duparg("coa", *argv);
			coap = *argv;

			NEXT_ARG();

			get_prefix(&coa, *argv, preferred_family);
			if (coa.family == AF_UNSPEC)
				invarg("value after \"coa\" has an unrecognized address family", *argv);
			if (coa.bytelen > sizeof(xcoa))
				invarg("value after \"coa\" is too large", *argv);

			memset(&xcoa, 0, sizeof(xcoa));
			memcpy(&xcoa, &coa.data, coa.bytelen);

			addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR,
				  (void *)&xcoa, sizeof(xcoa));
		} else if (strcmp(*argv, "ctx") == 0) {
			char *context;

			if (sctxp)
				duparg("ctx", *argv);
			sctxp = *argv;

			NEXT_ARG();
			context = *argv;

			xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx);
			addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX,
				  (void *)&ctx, ctx.sctx.len);
		} else {
			/* try to assume ALGO */
			int type = xfrm_algotype_getbyname(*argv);
			switch (type) {
			case XFRMA_ALG_AEAD:
			case XFRMA_ALG_CRYPT:
			case XFRMA_ALG_AUTH:
			case XFRMA_ALG_AUTH_TRUNC:
			case XFRMA_ALG_COMP:
			{
				/* ALGO */
				struct {
					union {
						struct xfrm_algo alg;
						struct xfrm_algo_aead aead;
						struct xfrm_algo_auth auth;
					} u;
					char buf[XFRM_ALGO_KEY_BUF_SIZE];
				} alg = {};
				int len;
				__u32 icvlen, trunclen;
				char *name;
				char *key = "";
				char *buf;

				switch (type) {
				case XFRMA_ALG_AEAD:
					if (ealgop || aalgop || aeadop)
						duparg("ALGO-TYPE", *argv);
					aeadop = *argv;
					break;
				case XFRMA_ALG_CRYPT:
					if (ealgop || aeadop)
						duparg("ALGO-TYPE", *argv);
					ealgop = *argv;
					break;
				case XFRMA_ALG_AUTH:
				case XFRMA_ALG_AUTH_TRUNC:
					if (aalgop || aeadop)
						duparg("ALGO-TYPE", *argv);
					aalgop = *argv;
					break;
				case XFRMA_ALG_COMP:
					if (calgop)
						duparg("ALGO-TYPE", *argv);
					calgop = *argv;
					break;
				default:
					/* not reached */
					invarg("ALGO-TYPE value is invalid\n", *argv);
				}

				if (!NEXT_ARG_OK())
					missarg("ALGO-NAME");
				NEXT_ARG();
				name = *argv;

				switch (type) {
				case XFRMA_ALG_AEAD:
				case XFRMA_ALG_CRYPT:
				case XFRMA_ALG_AUTH:
				case XFRMA_ALG_AUTH_TRUNC:
					if (!NEXT_ARG_OK())
						missarg("ALGO-KEYMAT");
					NEXT_ARG();
					key = *argv;
					break;
				}

				buf = alg.u.alg.alg_key;
				len = sizeof(alg.u.alg);

				switch (type) {
				case XFRMA_ALG_AEAD:
					if (!NEXT_ARG_OK())
						missarg("ALGO-ICV-LEN");
					NEXT_ARG();
					if (get_u32(&icvlen, *argv, 0))
						invarg("ALGO-ICV-LEN value is invalid",
						       *argv);
					alg.u.aead.alg_icv_len = icvlen;

					buf = alg.u.aead.alg_key;
					len = sizeof(alg.u.aead);
					break;
				case XFRMA_ALG_AUTH_TRUNC:
					if (!NEXT_ARG_OK())
						missarg("ALGO-TRUNC-LEN");
					NEXT_ARG();
					if (get_u32(&trunclen, *argv, 0))
						invarg("ALGO-TRUNC-LEN value is invalid",
						       *argv);
					alg.u.auth.alg_trunc_len = trunclen;

					buf = alg.u.auth.alg_key;
					len = sizeof(alg.u.auth);
					break;
				}

				xfrm_algo_parse((void *)&alg, type, name, key,
						buf, sizeof(alg.buf));
				len += alg.u.alg.alg_key_len;

				addattr_l(&req.n, sizeof(req.buf), type,
					  (void *)&alg, len);
				break;
			}
			default:
				/* try to assume ID */
				if (idp)
					invarg("unknown", *argv);
				idp = *argv;

				/* ID */
				xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id,
					      &req.xsinfo.family, 0, &argc, &argv);
				if (preferred_family == AF_UNSPEC)
					preferred_family = req.xsinfo.family;
			}
		}
		argc--; argv++;
	}

	if (req.xsinfo.flags & XFRM_STATE_ESN &&
	    replay_window == 0) {
		fprintf(stderr, "Error: esn flag set without replay-window.\n");
		exit(-1);
	}

	if (replay_window > XFRMA_REPLAY_ESN_MAX) {
		fprintf(stderr,
			"Error: replay-window (%u) > XFRMA_REPLAY_ESN_MAX (%u).\n",
			replay_window, XFRMA_REPLAY_ESN_MAX);
		exit(-1);
	}

	if (req.xsinfo.flags & XFRM_STATE_ESN ||
	    replay_window > (sizeof(replay.bitmap) * 8)) {
		replay_esn.seq = seq;
		replay_esn.oseq = oseq;
		replay_esn.seq_hi = seq_hi;
		replay_esn.oseq_hi = oseq_hi;
		replay_esn.replay_window = replay_window;
		replay_esn.bmp_len = (replay_window + sizeof(__u32) * 8 - 1) /
				     (sizeof(__u32) * 8);
		addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_ESN_VAL,
			  &replay_esn, sizeof(replay_esn));
	} else {
		if (seq || oseq) {
			replay.seq = seq;
			replay.oseq = oseq;
			addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL,
				  &replay, sizeof(replay));
		}
		req.xsinfo.replay_window = replay_window;
	}

	if (extra_flags)
		addattr32(&req.n, sizeof(req.buf), XFRMA_SA_EXTRA_FLAGS,
			  extra_flags);

	if (!idp) {
		fprintf(stderr, "Not enough information: ID is required\n");
		exit(1);
	}

	if (mark.m) {
		int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
				  (void *)&mark, sizeof(mark));
		if (r < 0) {
			fprintf(stderr, "XFRMA_MARK failed\n");
			exit(1);
		}
	}

	if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
		switch (req.xsinfo.mode) {
		case XFRM_MODE_TRANSPORT:
		case XFRM_MODE_TUNNEL:
			break;
		case XFRM_MODE_BEET:
			if (req.xsinfo.id.proto == IPPROTO_ESP)
				break;
		default:
			fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}

		switch (req.xsinfo.id.proto) {
		case IPPROTO_ESP:
			if (calgop) {
				fprintf(stderr, "ALGO-TYPE value \"%s\" is invalid with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_COMP),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			if (!ealgop && !aeadop) {
				fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_CRYPT),
					strxf_algotype(XFRMA_ALG_AEAD),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			break;
		case IPPROTO_AH:
			if (ealgop || aeadop || calgop) {
				fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_CRYPT),
					strxf_algotype(XFRMA_ALG_AEAD),
					strxf_algotype(XFRMA_ALG_COMP),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			if (!aalgop) {
				fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_AUTH),
					strxf_algotype(XFRMA_ALG_AUTH_TRUNC),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			break;
		case IPPROTO_COMP:
			if (ealgop || aalgop || aeadop) {
				fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_CRYPT),
					strxf_algotype(XFRMA_ALG_AUTH),
					strxf_algotype(XFRMA_ALG_AUTH_TRUNC),
					strxf_algotype(XFRMA_ALG_AEAD),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			if (!calgop) {
				fprintf(stderr, "ALGO-TYPE value \"%s\" is required with XFRM-PROTO value \"%s\"\n",
					strxf_algotype(XFRMA_ALG_COMP),
					strxf_xfrmproto(req.xsinfo.id.proto));
				exit(1);
			}
			break;
		}
	} else {
		if (ealgop || aalgop || aeadop || calgop) {
			fprintf(stderr, "ALGO is invalid with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	}

	if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) {
		switch (req.xsinfo.mode) {
		case XFRM_MODE_ROUTEOPTIMIZATION:
		case XFRM_MODE_IN_TRIGGER:
			break;
		case 0:
			fprintf(stderr, "\"mode\" is required with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		default:
			fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}

		if (!coap) {
			fprintf(stderr, "\"coa\" is required with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	} else {
		if (coap) {
			fprintf(stderr, "\"coa\" is invalid with XFRM-PROTO value \"%s\"\n",
				strxf_xfrmproto(req.xsinfo.id.proto));
			exit(1);
		}
	}

	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
		exit(1);

	if (req.xsinfo.family == AF_UNSPEC)
		req.xsinfo.family = AF_INET;

	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
		exit(2);

	rtnl_close(&rth);

	return 0;
}
Ejemplo n.º 15
0
static int ipntable_modify(int cmd, int flags, int argc, char **argv)
{
	struct {
		struct nlmsghdr	n;
		struct ndtmsg		ndtm;
		char			buf[1024];
	} req = {
		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)),
		.n.nlmsg_flags = NLM_F_REQUEST | flags,
		.n.nlmsg_type = cmd,
		.ndtm.ndtm_family = preferred_family,
	};
	char *namep = NULL;
	char *threshsp = NULL;
	char *gc_intp = NULL;
	char parms_buf[1024] = {};
	struct rtattr *parms_rta = (struct rtattr *)parms_buf;
	int parms_change = 0;

	parms_rta->rta_type = NDTA_PARMS;
	parms_rta->rta_len = RTA_LENGTH(0);

	while (argc > 0) {
		if (strcmp(*argv, "name") == 0) {
			int len;

			NEXT_ARG();
			if (namep)
				duparg("NAME", *argv);

			namep = *argv;
			len = strlen(namep) + 1;
			addattr_l(&req.n, sizeof(req), NDTA_NAME, namep, len);
		} else if (strcmp(*argv, "thresh1") == 0) {
			__u32 thresh1;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh1, *argv, 0))
				invarg("\"thresh1\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH1, thresh1);
		} else if (strcmp(*argv, "thresh2") == 0) {
			__u32 thresh2;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh2, *argv, 0))
				invarg("\"thresh2\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH2, thresh2);
		} else if (strcmp(*argv, "thresh3") == 0) {
			__u32 thresh3;

			NEXT_ARG();
			threshsp = *argv;

			if (get_u32(&thresh3, *argv, 0))
				invarg("\"thresh3\" value is invalid", *argv);

			addattr32(&req.n, sizeof(req), NDTA_THRESH3, thresh3);
		} else if (strcmp(*argv, "gc_int") == 0) {
			__u64 gc_int;

			NEXT_ARG();
			gc_intp = *argv;

			if (get_u64(&gc_int, *argv, 0))
				invarg("\"gc_int\" value is invalid", *argv);

			addattr_l(&req.n, sizeof(req), NDTA_GC_INTERVAL,
				  &gc_int, sizeof(gc_int));
		} else if (strcmp(*argv, "dev") == 0) {
			__u32 ifindex;

			NEXT_ARG();
			ifindex = ll_name_to_index(*argv);
			if (!ifindex)
				return nodev(*argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_IFINDEX, ifindex);
		} else if (strcmp(*argv, "base_reachable") == 0) {
			__u64 breachable;

			NEXT_ARG();

			if (get_u64(&breachable, *argv, 0))
				invarg("\"base_reachable\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_BASE_REACHABLE_TIME,
				      &breachable, sizeof(breachable));
			parms_change = 1;
		} else if (strcmp(*argv, "retrans") == 0) {
			__u64 retrans;

			NEXT_ARG();

			if (get_u64(&retrans, *argv, 0))
				invarg("\"retrans\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_RETRANS_TIME,
				      &retrans, sizeof(retrans));
			parms_change = 1;
		} else if (strcmp(*argv, "gc_stale") == 0) {
			__u64 gc_stale;

			NEXT_ARG();

			if (get_u64(&gc_stale, *argv, 0))
				invarg("\"gc_stale\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_GC_STALETIME,
				      &gc_stale, sizeof(gc_stale));
			parms_change = 1;
		} else if (strcmp(*argv, "delay_probe") == 0) {
			__u64 delay_probe;

			NEXT_ARG();

			if (get_u64(&delay_probe, *argv, 0))
				invarg("\"delay_probe\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_DELAY_PROBE_TIME,
				      &delay_probe, sizeof(delay_probe));
			parms_change = 1;
		} else if (strcmp(*argv, "queue") == 0) {
			__u32 queue;

			NEXT_ARG();

			if (get_u32(&queue, *argv, 0))
				invarg("\"queue\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_QUEUE_LEN, queue);
			parms_change = 1;
		} else if (strcmp(*argv, "app_probes") == 0) {
			__u32 aprobe;

			NEXT_ARG();

			if (get_u32(&aprobe, *argv, 0))
				invarg("\"app_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_APP_PROBES, aprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "ucast_probes") == 0) {
			__u32 uprobe;

			NEXT_ARG();

			if (get_u32(&uprobe, *argv, 0))
				invarg("\"ucast_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_UCAST_PROBES, uprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "mcast_probes") == 0) {
			__u32 mprobe;

			NEXT_ARG();

			if (get_u32(&mprobe, *argv, 0))
				invarg("\"mcast_probes\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_MCAST_PROBES, mprobe);
			parms_change = 1;
		} else if (strcmp(*argv, "anycast_delay") == 0) {
			__u64 anycast_delay;

			NEXT_ARG();

			if (get_u64(&anycast_delay, *argv, 0))
				invarg("\"anycast_delay\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_ANYCAST_DELAY,
				      &anycast_delay, sizeof(anycast_delay));
			parms_change = 1;
		} else if (strcmp(*argv, "proxy_delay") == 0) {
			__u64 proxy_delay;

			NEXT_ARG();

			if (get_u64(&proxy_delay, *argv, 0))
				invarg("\"proxy_delay\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_PROXY_DELAY,
				      &proxy_delay, sizeof(proxy_delay));
			parms_change = 1;
		} else if (strcmp(*argv, "proxy_queue") == 0) {
			__u32 pqueue;

			NEXT_ARG();

			if (get_u32(&pqueue, *argv, 0))
				invarg("\"proxy_queue\" value is invalid", *argv);

			rta_addattr32(parms_rta, sizeof(parms_buf),
				      NDTPA_PROXY_QLEN, pqueue);
			parms_change = 1;
		} else if (strcmp(*argv, "locktime") == 0) {
			__u64 locktime;

			NEXT_ARG();

			if (get_u64(&locktime, *argv, 0))
				invarg("\"locktime\" value is invalid", *argv);

			rta_addattr_l(parms_rta, sizeof(parms_buf),
				      NDTPA_LOCKTIME,
				      &locktime, sizeof(locktime));
			parms_change = 1;
		} else {
			invarg("unknown", *argv);
		}

		argc--; argv++;
	}

	if (!namep)
		missarg("NAME");
	if (!threshsp && !gc_intp && !parms_change) {
		fprintf(stderr, "Not enough information: changeable attributes required.\n");
		exit(-1);
	}

	if (parms_rta->rta_len > RTA_LENGTH(0)) {
		addattr_l(&req.n, sizeof(req), NDTA_PARMS, RTA_DATA(parms_rta),
			  RTA_PAYLOAD(parms_rta));
	}

	if (rtnl_talk(&rth, &req.n, NULL) < 0)
		exit(2);

	return 0;
}

static const char *ntable_strtime_delta(__u32 msec)
{
	static char str[32];
	struct timeval now = {};
	time_t t;
	struct tm *tp;

	if (msec == 0)
		goto error;

	if (gettimeofday(&now, NULL) < 0) {
		perror("gettimeofday");
		goto error;
	}

	t = now.tv_sec - (msec / 1000);
	tp = localtime(&t);
	if (!tp)
		goto error;

	strftime(str, sizeof(str), "%Y-%m-%d %T", tp);

	return str;
 error:
	strcpy(str, "(error)");
	return str;
}

static void print_ndtconfig(const struct ndt_config *ndtc)
{

	print_uint(PRINT_ANY, "key_length",
		   "    config key_len %u ", ndtc->ndtc_key_len);
	print_uint(PRINT_ANY, "entry_size",
		   "entry_size %u ", ndtc->ndtc_entry_size);
	print_uint(PRINT_ANY, "entries", "entries %u ", ndtc->ndtc_entries);

	print_nl();

	print_string(PRINT_ANY, "last_flush",
		     "        last_flush %s ",
		     ntable_strtime_delta(ndtc->ndtc_last_flush));
	print_string(PRINT_ANY, "last_rand",
		     "last_rand %s ",
		     ntable_strtime_delta(ndtc->ndtc_last_rand));

	print_nl();

	print_uint(PRINT_ANY, "hash_rnd",
		   "        hash_rnd %u ", ndtc->ndtc_hash_rnd);
	print_0xhex(PRINT_ANY, "hash_mask",
		    "hash_mask %08llx ", ndtc->ndtc_hash_mask);

	print_uint(PRINT_ANY, "hash_chain_gc",
		   "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc);
	print_uint(PRINT_ANY, "proxy_qlen",
		   "proxy_qlen %u ", ndtc->ndtc_proxy_qlen);

	print_nl();
}