Example #1
0
/* Return value becomes exitcode. It's okay to not return at all */
int ipaddr_list_or_flush(int argc, char **argv, int flush)
{
	static const char option[] ALIGN1 = "to\0""scope\0""up\0""label\0""dev\0";

	struct nlmsg_list *linfo = NULL;
	struct nlmsg_list *ainfo = NULL;
	struct nlmsg_list *l;
	struct rtnl_handle rth;
	char *filter_dev = NULL;
	int no_link = 0;

	ipaddr_reset_filter(oneline);
	filter.showqueue = 1;

	if (filter.family == AF_UNSPEC)
		filter.family = preferred_family;

	if (flush) {
		if (argc <= 0) {
			bb_error_msg_and_die(bb_msg_requires_arg, "flush");
		}
		if (filter.family == AF_PACKET) {
			bb_error_msg_and_die("cannot flush link addresses");
		}
	}

	while (argc > 0) {
		const int option_num = index_in_strings(option, *argv);
		switch (option_num) {
			case 0: /* to */
				NEXT_ARG();
				get_prefix(&filter.pfx, *argv, filter.family);
				if (filter.family == AF_UNSPEC) {
					filter.family = filter.pfx.family;
				}
				break;
			case 1: /* scope */
			{
				uint32_t scope = 0;
				NEXT_ARG();
				filter.scopemask = -1;
				if (rtnl_rtscope_a2n(&scope, *argv)) {
					if (strcmp(*argv, "all") != 0) {
						invarg(*argv, "scope");
					}
					scope = RT_SCOPE_NOWHERE;
					filter.scopemask = 0;
				}
				filter.scope = scope;
				break;
			}
			case 2: /* up */
				filter.up = 1;
				break;
			case 3: /* label */
				NEXT_ARG();
				filter.label = *argv;
				break;
			case 4: /* dev */
				NEXT_ARG();
			default:
				if (filter_dev) {
					duparg2("dev", *argv);
				}
				filter_dev = *argv;
		}
		argv++;
		argc--;
	}

	xrtnl_open(&rth);

	xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK);
	xrtnl_dump_filter(&rth, store_nlmsg, &linfo);

	if (filter_dev) {
		filter.ifindex = xll_name_to_index(filter_dev);
	}

	if (flush) {
		char flushb[4096-512];

		filter.flushb = flushb;
		filter.flushp = 0;
		filter.flushe = sizeof(flushb);
		filter.rth = &rth;

		for (;;) {
			xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
			filter.flushed = 0;
			xrtnl_dump_filter(&rth, print_addrinfo, stdout);
			if (filter.flushed == 0) {
				return 0;
			}
			if (flush_update() < 0)
				return 1;
		}
	}

	if (filter.family != AF_PACKET) {
		xrtnl_wilddump_request(&rth, filter.family, RTM_GETADDR);
		xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
	}


	if (filter.family && filter.family != AF_PACKET) {
		struct nlmsg_list **lp;
		lp=&linfo;

		if (filter.oneline)
			no_link = 1;

		while ((l=*lp)!=NULL) {
			int ok = 0;
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			struct nlmsg_list *a;

			for (a=ainfo; a; a=a->next) {
				struct nlmsghdr *n = &a->h;
				struct ifaddrmsg *ifa = NLMSG_DATA(n);

				if (ifa->ifa_index != ifi->ifi_index ||
				    (filter.family && filter.family != ifa->ifa_family))
					continue;
				if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
					continue;
				if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
					continue;
				if (filter.pfx.family || filter.label) {
					struct rtattr *tb[IFA_MAX+1];
					memset(tb, 0, sizeof(tb));
					parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
					if (!tb[IFA_LOCAL])
						tb[IFA_LOCAL] = tb[IFA_ADDRESS];

					if (filter.pfx.family && tb[IFA_LOCAL]) {
						inet_prefix dst;
						memset(&dst, 0, sizeof(dst));
						dst.family = ifa->ifa_family;
						memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
						if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
							continue;
					}
					if (filter.label) {
						SPRINT_BUF(b1);
						const char *label;
						if (tb[IFA_LABEL])
							label = RTA_DATA(tb[IFA_LABEL]);
						else
							label = ll_idx_n2a(ifa->ifa_index, b1);
						if (fnmatch(filter.label, label, 0) != 0)
							continue;
					}
				}

				ok = 1;
				break;
			}
			if (!ok)
				*lp = l->next;
			else
				lp = &l->next;
		}
	}

	for (l = linfo; l; l = l->next) {
		if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			if (filter.family != AF_PACKET)
				print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
		}
		fflush(stdout); /* why? */
	}

	return 0;
}
Example #2
0
/* Return value becomes exitcode. It's okay to not return at all */
static int ipaddr_modify(int cmd, int argc, char **argv)
{
	static const char option[] ALIGN1 =
		"peer\0""remote\0""broadcast\0""brd\0"
		"anycast\0""scope\0""dev\0""label\0""local\0";
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr  n;
		struct ifaddrmsg ifa;
		char             buf[256];
	} req;
	char *d = NULL;
	char *l = NULL;
	inet_prefix lcl;
	inet_prefix peer;
	int local_len = 0;
	int peer_len = 0;
	int brd_len = 0;
	int any_len = 0;
	bool scoped = 0;

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

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.n.nlmsg_type = cmd;
	req.ifa.ifa_family = preferred_family;

	while (argc > 0) {
		const int option_num = index_in_strings(option, *argv);
		switch (option_num) {
			case 0: /* peer */
			case 1: /* remote */
				NEXT_ARG();

				if (peer_len) {
					duparg("peer", *argv);
				}
				get_prefix(&peer, *argv, req.ifa.ifa_family);
				peer_len = peer.bytelen;
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = peer.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
				req.ifa.ifa_prefixlen = peer.bitlen;
				break;
			case 2: /* broadcast */
			case 3: /* brd */
			{
				inet_prefix addr;
				NEXT_ARG();
				if (brd_len) {
					duparg("broadcast", *argv);
				}
				if (LONE_CHAR(*argv, '+')) {
					brd_len = -1;
				}
				else if (LONE_DASH(*argv)) {
					brd_len = -2;
				} else {
					get_addr(&addr, *argv, req.ifa.ifa_family);
					if (req.ifa.ifa_family == AF_UNSPEC)
						req.ifa.ifa_family = addr.family;
					addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
					brd_len = addr.bytelen;
				}
				break;
			}
			case 4: /* anycast */
			{
				inet_prefix addr;
				NEXT_ARG();
				if (any_len) {
					duparg("anycast", *argv);
				}
				get_addr(&addr, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = addr.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
				any_len = addr.bytelen;
				break;
			}
			case 5: /* scope */
			{
				uint32_t scope = 0;
				NEXT_ARG();
				if (rtnl_rtscope_a2n(&scope, *argv)) {
					invarg(*argv, "scope");
				}
				req.ifa.ifa_scope = scope;
				scoped = 1;
				break;
			}
			case 6: /* dev */
				NEXT_ARG();
				d = *argv;
				break;
			case 7: /* label */
				NEXT_ARG();
				l = *argv;
				addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
				break;
			case 8:	/* local */
				NEXT_ARG();
			default:
				if (local_len) {
					duparg2("local", *argv);
				}
				get_prefix(&lcl, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = lcl.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
				local_len = lcl.bytelen;
		}
		argc--;
		argv++;
	}

	if (d == NULL) {
		bb_error_msg(bb_msg_requires_arg,"\"dev\"");
		return -1;
	}
	if (l && strncmp(d, l, strlen(d)) != 0) {
		bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
	}

	if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
		peer = lcl;
		addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
	}
	if (req.ifa.ifa_prefixlen == 0)
		req.ifa.ifa_prefixlen = lcl.bitlen;

	if (brd_len < 0 && cmd != RTM_DELADDR) {
		inet_prefix brd;
		int i;
		if (req.ifa.ifa_family != AF_INET) {
			bb_error_msg_and_die("broadcast can be set only for IPv4 addresses");
		}
		brd = peer;
		if (brd.bitlen <= 30) {
			for (i=31; i>=brd.bitlen; i--) {
				if (brd_len == -1)
					brd.data[0] |= htonl(1<<(31-i));
				else
					brd.data[0] &= ~htonl(1<<(31-i));
			}
			addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
			brd_len = brd.bytelen;
		}
	}
	if (!scoped && cmd != RTM_DELADDR)
		req.ifa.ifa_scope = default_scope(&lcl);

	xrtnl_open(&rth);

	ll_init_map(&rth);

	req.ifa.ifa_index = xll_name_to_index(d);

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

	return 0;
}
extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
{
	const char *option[] = { "to", "scope", "up", "label", "dev", 0 };
	struct nlmsg_list *linfo = NULL;
	struct nlmsg_list *ainfo = NULL;
	struct nlmsg_list *l;
	struct rtnl_handle rth;
	char *filter_dev = NULL;
	int no_link = 0;

	ipaddr_reset_filter(oneline);
	filter.showqueue = 1;

	if (filter.family == AF_UNSPEC)
		filter.family = preferred_family;

	if (flush) {
		if (argc <= 0) {
			fprintf(stderr, "Flush requires arguments.\n");
			return -1;
		}
		if (filter.family == AF_PACKET) {
			fprintf(stderr, "Cannot flush link addresses.\n");
			return -1;
		}
	}

	while (argc > 0) {
		const unsigned short option_num = compare_string_array(option, *argv);
		switch (option_num) {
			case 0: /* to */
				NEXT_ARG();
				get_prefix(&filter.pfx, *argv, filter.family);
				if (filter.family == AF_UNSPEC) {
					filter.family = filter.pfx.family;
				}
				break;
			case 1: /* scope */
			{
				int scope = 0;
				NEXT_ARG();
				filter.scopemask = -1;
				if (rtnl_rtscope_a2n(&scope, *argv)) {
					if (strcmp(*argv, "all") != 0) {
						invarg("invalid \"scope\"\n", *argv);
					}
					scope = RT_SCOPE_NOWHERE;
					filter.scopemask = 0;
				}
				filter.scope = scope;
				break;
			}
			case 2: /* up */
				filter.up = 1;
				break;
			case 3: /* label */
				NEXT_ARG();
				filter.label = *argv;
				break;
			case 4: /* dev */
				NEXT_ARG();
			default:
				if (filter_dev) {
					duparg2("dev", *argv);
				}
				filter_dev = *argv;
		}
		argv++;
		argc--;
	}

	if (rtnl_open(&rth, 0) < 0)
		exit(1);

	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
		bb_perror_msg_and_die("Cannot send dump request");
	}

	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
		bb_error_msg_and_die("Dump terminated");
	}

	if (filter_dev) {
		filter.ifindex = ll_name_to_index(filter_dev);
		if (filter.ifindex <= 0) {
			bb_error_msg("Device \"%s\" does not exist.", filter_dev);
			return -1;
		}
	}

	if (flush) {
		int round = 0;
		char flushb[4096-512];

		filter.flushb = flushb;
		filter.flushp = 0;
		filter.flushe = sizeof(flushb);
		filter.rth = &rth;

		for (;;) {
			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
				perror("Cannot send dump request");
				exit(1);
			}
			filter.flushed = 0;
			if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
				fprintf(stderr, "Flush terminated\n");
				exit(1);
			}
			if (filter.flushed == 0) {
#if 0
				if (round == 0)
					fprintf(stderr, "Nothing to flush.\n");
#endif
				fflush(stdout);
				return 0;
			}
			round++;
			if (flush_update() < 0)
				exit(1);
		}
	}

	if (filter.family != AF_PACKET) {
		if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
			bb_perror_msg_and_die("Cannot send dump request");
		}

		if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
			bb_error_msg_and_die("Dump terminated");
		}
	}


	if (filter.family && filter.family != AF_PACKET) {
		struct nlmsg_list **lp;
		lp=&linfo;

		if (filter.oneline)
			no_link = 1;

		while ((l=*lp)!=NULL) {
			int ok = 0;
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			struct nlmsg_list *a;

			for (a=ainfo; a; a=a->next) {
				struct nlmsghdr *n = &a->h;
				struct ifaddrmsg *ifa = NLMSG_DATA(n);

				if (ifa->ifa_index != ifi->ifi_index ||
				    (filter.family && filter.family != ifa->ifa_family))
					continue;
				if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
					continue;
				if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
					continue;
				if (filter.pfx.family || filter.label) {
					struct rtattr *tb[IFA_MAX+1];
					memset(tb, 0, sizeof(tb));
					parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
					if (!tb[IFA_LOCAL])
						tb[IFA_LOCAL] = tb[IFA_ADDRESS];

					if (filter.pfx.family && tb[IFA_LOCAL]) {
						inet_prefix dst;
						memset(&dst, 0, sizeof(dst));
						dst.family = ifa->ifa_family;
						memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
						if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
							continue;
					}
					if (filter.label) {
						SPRINT_BUF(b1);
						const char *label;
						if (tb[IFA_LABEL])
							label = RTA_DATA(tb[IFA_LABEL]);
						else
							label = ll_idx_n2a(ifa->ifa_index, b1);
						if (fnmatch(filter.label, label, 0) != 0)
							continue;
					}
				}

				ok = 1;
				break;
			}
			if (!ok)
				*lp = l->next;
			else
				lp = &l->next;
		}
	}

	for (l=linfo; l; l = l->next) {
		if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			if (filter.family != AF_PACKET)
				print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
		}
		fflush(stdout);
	}

	exit(0);
}
static int ipaddr_modify(int cmd, int argc, char **argv)
{
	const char *option[] = { "peer", "remote", "broadcast", "brd",
		"anycast", "scope", "dev", "label", "local", 0 };
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr 	n;
		struct ifaddrmsg 	ifa;
		char   			buf[256];
	} req;
	char  *d = NULL;
	char  *l = NULL;
	inet_prefix lcl;
	inet_prefix peer;
	int local_len = 0;
	int peer_len = 0;
	int brd_len = 0;
	int any_len = 0;
	int scoped = 0;

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

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.n.nlmsg_type = cmd;
	req.ifa.ifa_family = preferred_family;

	while (argc > 0) {
		const unsigned short option_num = compare_string_array(option, *argv);
		switch (option_num) {
			case 0: /* peer */
			case 1: /* remote */
				NEXT_ARG();

				if (peer_len) {
					duparg("peer", *argv);
				}
				get_prefix(&peer, *argv, req.ifa.ifa_family);
				peer_len = peer.bytelen;
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = peer.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
				req.ifa.ifa_prefixlen = peer.bitlen;
				break;
			case 2: /* broadcast */
			case 3: /* brd */
			{
				inet_prefix addr;
				NEXT_ARG();
				if (brd_len) {
					duparg("broadcast", *argv);
				}
				if (strcmp(*argv, "+") == 0) {
					brd_len = -1;
				}
				else if (strcmp(*argv, "-") == 0) {
					brd_len = -2;
				} else {
					get_addr(&addr, *argv, req.ifa.ifa_family);
					if (req.ifa.ifa_family == AF_UNSPEC)
						req.ifa.ifa_family = addr.family;
					addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
					brd_len = addr.bytelen;
				}
				break;
			}
			case 4: /* anycast */
			{
				inet_prefix addr;
				NEXT_ARG();
				if (any_len) {
					duparg("anycast", *argv);
				}
				get_addr(&addr, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = addr.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
				any_len = addr.bytelen;
				break;
			}
			case 5: /* scope */
			{
				int scope = 0;
				NEXT_ARG();
				if (rtnl_rtscope_a2n(&scope, *argv)) {
					invarg(*argv, "invalid scope value.");
				}
				req.ifa.ifa_scope = scope;
				scoped = 1;
				break;
			}
			case 6: /* dev */
				NEXT_ARG();
				d = *argv;
				break;
			case 7: /* label */
				NEXT_ARG();
				l = *argv;
				addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
				break;
			case 8:	/* local */
				NEXT_ARG();
			default:
				if (local_len) {
					duparg2("local", *argv);
				}
				get_prefix(&lcl, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC) {
					req.ifa.ifa_family = lcl.family;
				}
				addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
				local_len = lcl.bytelen;
		}
		argc--;
		argv++;
	}

	if (d == NULL) {
		bb_error_msg("Not enough information: \"dev\" argument is required.");
		return -1;
	}
	if (l && matches(d, l) != 0) {
		bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l);
	}

	if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
		peer = lcl;
		addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
	}
	if (req.ifa.ifa_prefixlen == 0)
		req.ifa.ifa_prefixlen = lcl.bitlen;

	if (brd_len < 0 && cmd != RTM_DELADDR) {
		inet_prefix brd;
		int i;
		if (req.ifa.ifa_family != AF_INET) {
			bb_error_msg("Broadcast can be set only for IPv4 addresses");
			return -1;
		}
		brd = peer;
		if (brd.bitlen <= 30) {
			for (i=31; i>=brd.bitlen; i--) {
				if (brd_len == -1)
					brd.data[0] |= htonl(1<<(31-i));
				else
					brd.data[0] &= ~htonl(1<<(31-i));
			}
			addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
			brd_len = brd.bytelen;
		}
	}
	if (!scoped && cmd != RTM_DELADDR)
		req.ifa.ifa_scope = default_scope(&lcl);

	if (rtnl_open(&rth, 0) < 0)
		exit(1);

	ll_init_map(&rth);

	if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
		bb_error_msg("Cannot find device \"%s\"", d);
		return -1;
	}

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

	exit(0);
}
static int ipaddr_list_or_flush(int argc, char **argv, int flush)
{
	struct nlmsg_list *linfo = NULL;
	struct nlmsg_list *ainfo = NULL;
	struct nlmsg_list *l, *n;
	char *filter_dev = NULL;
	int no_link = 0;

	ipaddr_reset_filter(oneline);
	filter.showqueue = 1;

	if (filter.family == AF_UNSPEC)
		filter.family = preferred_family;

	if (flush) {
		if (argc <= 0) {
			fprintf(stderr, "Flush requires arguments.\n");
			return -1;
		}
		if (filter.family == AF_PACKET) {
			fprintf(stderr, "Cannot flush link addresses.\n");
			return -1;
		}
	}

	while (argc > 0) {
		if (strcmp(*argv, "to") == 0) {
			NEXT_ARG();
			get_prefix(&filter.pfx, *argv, filter.family);
			if (filter.family == AF_UNSPEC)
				filter.family = filter.pfx.family;
		} else if (strcmp(*argv, "scope") == 0) {
			unsigned scope = 0;
			NEXT_ARG();
			filter.scopemask = -1;
			if (rtnl_rtscope_a2n(&scope, *argv)) {
				if (strcmp(*argv, "all") != 0)
					invarg("invalid \"scope\"\n", *argv);
				scope = RT_SCOPE_NOWHERE;
				filter.scopemask = 0;
			}
			filter.scope = scope;
		} else if (strcmp(*argv, "up") == 0) {
			filter.up = 1;
		} else if (strcmp(*argv, "dynamic") == 0) {
			filter.flags &= ~IFA_F_PERMANENT;
			filter.flagmask |= IFA_F_PERMANENT;
		} else if (strcmp(*argv, "permanent") == 0) {
			filter.flags |= IFA_F_PERMANENT;
			filter.flagmask |= IFA_F_PERMANENT;
		} else if (strcmp(*argv, "secondary") == 0 ||
			   strcmp(*argv, "temporary") == 0) {
			filter.flags |= IFA_F_SECONDARY;
			filter.flagmask |= IFA_F_SECONDARY;
		} else if (strcmp(*argv, "primary") == 0) {
			filter.flags &= ~IFA_F_SECONDARY;
			filter.flagmask |= IFA_F_SECONDARY;
		} else if (strcmp(*argv, "tentative") == 0) {
			filter.flags |= IFA_F_TENTATIVE;
			filter.flagmask |= IFA_F_TENTATIVE;
		} else if (strcmp(*argv, "deprecated") == 0) {
			filter.flags |= IFA_F_DEPRECATED;
			filter.flagmask |= IFA_F_DEPRECATED;
		} else if (strcmp(*argv, "home") == 0) {
			filter.flags |= IFA_F_HOMEADDRESS;
			filter.flagmask |= IFA_F_HOMEADDRESS;
		} else if (strcmp(*argv, "nodad") == 0) {
			filter.flags |= IFA_F_NODAD;
			filter.flagmask |= IFA_F_NODAD;
		} else if (strcmp(*argv, "dadfailed") == 0) {
			filter.flags |= IFA_F_DADFAILED;
			filter.flagmask |= IFA_F_DADFAILED;
		} else if (strcmp(*argv, "label") == 0) {
			NEXT_ARG();
			filter.label = *argv;
		} else {
			if (strcmp(*argv, "dev") == 0) {
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (filter_dev)
				duparg2("dev", *argv);
			filter_dev = *argv;
		}
		argv++; argc--;
	}

	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
		perror("Cannot send dump request");
		exit(1);
	}

	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
		fprintf(stderr, "Dump terminated\n");
		exit(1);
	}

	if (filter_dev) {
		filter.ifindex = ll_name_to_index(filter_dev);
		if (filter.ifindex <= 0) {
			fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev);
			return -1;
		}
	}

	if (flush) {
		int round = 0;
		char flushb[4096-512];

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

		while (round < MAX_ROUNDS) {
			const struct rtnl_dump_filter_arg a[3] = {
				{
					.filter = print_addrinfo_secondary,
					.arg1 = stdout,
					.junk = NULL,
					.arg2 = NULL
				},
				{
					.filter = print_addrinfo_primary,
					.arg1 = stdout,
					.junk = NULL,
					.arg2 = NULL
				},
				{
					.filter = NULL,
					.arg1 = NULL,
					.junk = NULL,
					.arg2 = NULL
				},
			};
int ipaddr_modify(int cmd, int argc, char **argv)
{
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr 	n;
		struct ifaddrmsg 	ifa;
		char   			buf[256];
	} req;
	char  *d = NULL;
	char  *l = NULL;
	char  *valid_lftp = NULL;
	char  *preferred_lftp = NULL;
	inet_prefix lcl;
	inet_prefix peer;
	int local_len = 0;
	int peer_len = 0;
	int brd_len = 0;
	int any_len = 0;
	int scoped = 0;
	__u32 preferred_lft = INFINITY_LIFE_TIME;
	__u32 valid_lft = INFINITY_LIFE_TIME;
	struct ifa_cacheinfo cinfo;

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

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.n.nlmsg_type = cmd;
	req.ifa.ifa_family = preferred_family;

	while (argc > 0) {
		if (strcmp(*argv, "peer") == 0 ||
		    strcmp(*argv, "remote") == 0) {
			NEXT_ARG();

			if (peer_len)
				duparg("peer", *argv);
			get_prefix(&peer, *argv, req.ifa.ifa_family);
			peer_len = peer.bytelen;
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = peer.family;
			addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
			req.ifa.ifa_prefixlen = peer.bitlen;
		} else if (matches(*argv, "broadcast") == 0 ||
			   strcmp(*argv, "brd") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			if (brd_len)
				duparg("broadcast", *argv);
			if (strcmp(*argv, "+") == 0)
				brd_len = -1;
			else if (strcmp(*argv, "-") == 0)
				brd_len = -2;
			else {
				get_addr(&addr, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC)
					req.ifa.ifa_family = addr.family;
				addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
				brd_len = addr.bytelen;
			}
		} else if (strcmp(*argv, "anycast") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			if (any_len)
				duparg("anycast", *argv);
			get_addr(&addr, *argv, req.ifa.ifa_family);
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = addr.family;
			addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
			any_len = addr.bytelen;
		} else if (strcmp(*argv, "scope") == 0) {
			int scope = 0;
			NEXT_ARG();
			if (rtnl_rtscope_a2n(&scope, *argv))
				invarg(*argv, "invalid scope value.");
			req.ifa.ifa_scope = scope;
			scoped = 1;
		} else if (strcmp(*argv, "dev") == 0) {
			NEXT_ARG();
			d = *argv;
		} else if (strcmp(*argv, "label") == 0) {
			NEXT_ARG();
			l = *argv;
			addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
		} else if (matches(*argv, "valid_lft") == 0) {
			if (valid_lftp)
				duparg("valid_lft", *argv);
			NEXT_ARG();
			valid_lftp = *argv;
			if (set_lifetime(&valid_lft, *argv))
				invarg("valid_lft value", *argv);
		} else if (matches(*argv, "preferred_lft") == 0) {
			if (preferred_lftp)
				duparg("preferred_lft", *argv);
			NEXT_ARG();
			preferred_lftp = *argv;
			if (set_lifetime(&preferred_lft, *argv))
				invarg("preferred_lft value", *argv);
		} else {
			if (strcmp(*argv, "local") == 0) {
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (local_len)
				duparg2("local", *argv);
			get_prefix(&lcl, *argv, req.ifa.ifa_family);
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = lcl.family;
			addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
			local_len = lcl.bytelen;
		}
		argc--; argv++;
	}
	if (d == NULL) {
		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
		return -1;
	}
	if (l && matches(d, l) != 0) {
		fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
		exit(1);
	}

	if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
		peer = lcl;
		addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
	}
	if (req.ifa.ifa_prefixlen == 0)
		req.ifa.ifa_prefixlen = lcl.bitlen;

	if (brd_len < 0 && cmd != RTM_DELADDR) {
		inet_prefix brd;
		int i;
		if (req.ifa.ifa_family != AF_INET) {
			fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
			return -1;
		}
		brd = peer;
		if (brd.bitlen <= 30) {
			for (i=31; i>=brd.bitlen; i--) {
				if (brd_len == -1)
					brd.data[0] |= htonl(1<<(31-i));
				else
					brd.data[0] &= ~htonl(1<<(31-i));
			}
			addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
			brd_len = brd.bytelen;
		}
	}
	if (!scoped && cmd != RTM_DELADDR)
		req.ifa.ifa_scope = default_scope(&lcl);

	if (rtnl_open(&rth, 0) < 0)
		exit(1);

	ll_init_map(&rth);

	if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
		fprintf(stderr, "Cannot find device \"%s\"\n", d);
		return -1;
	}

	if (valid_lftp || preferred_lftp) {
		if (!valid_lft) {
			fprintf(stderr, "valid_lft is zero\n");
			return -1;
		}
		if (valid_lft < preferred_lft) {
			fprintf(stderr, "preferred_lft is greater than valid_lft\n");
			return -1;
		}

		memset(&cinfo, 0, sizeof(cinfo));
		cinfo.ifa_prefered = preferred_lft;
		cinfo.ifa_valid = valid_lft;
		addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
			  sizeof(cinfo));
	}

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

	exit(0);
}
int ipaddr_list_or_flush(int argc, char **argv, int flush)
{
	struct nlmsg_list *linfo = NULL;
	struct nlmsg_list *ainfo = NULL;
	struct nlmsg_list *l;
	struct rtnl_handle rth;
	char *filter_dev = NULL;
	int no_link = 0;

	ipaddr_reset_filter(oneline);
	filter.showqueue = 1;

	if (filter.family == AF_UNSPEC)
		filter.family = preferred_family;

	if (flush) {
		if (argc <= 0) {
			fprintf(stderr, "Flush requires arguments.\n");
			return -1;
		}
		if (filter.family == AF_PACKET) {
			fprintf(stderr, "Cannot flush link addresses.\n");
			return -1;
		}
	}

	while (argc > 0) {
		if (strcmp(*argv, "to") == 0) {
			NEXT_ARG();
			get_prefix(&filter.pfx, *argv, filter.family);
			if (filter.family == AF_UNSPEC)
				filter.family = filter.pfx.family;
		} else if (strcmp(*argv, "scope") == 0) {
			int scope = 0;
			NEXT_ARG();
			filter.scopemask = -1;
			if (rtnl_rtscope_a2n(&scope, *argv)) {
				if (strcmp(*argv, "all") != 0)
					invarg("invalid \"scope\"\n", *argv);
				scope = RT_SCOPE_NOWHERE;
				filter.scopemask = 0;
			}
			filter.scope = scope;
		} else if (strcmp(*argv, "up") == 0) {
			filter.up = 1;
		} else if (strcmp(*argv, "dynamic") == 0) {
			filter.flags &= ~IFA_F_PERMANENT;
			filter.flagmask |= IFA_F_PERMANENT;
		} else if (strcmp(*argv, "permanent") == 0) {
			filter.flags |= IFA_F_PERMANENT;
			filter.flagmask |= IFA_F_PERMANENT;
		} else if (strcmp(*argv, "secondary") == 0) {
			filter.flags |= IFA_F_SECONDARY;
			filter.flagmask |= IFA_F_SECONDARY;
		} else if (strcmp(*argv, "primary") == 0) {
			filter.flags &= ~IFA_F_SECONDARY;
			filter.flagmask |= IFA_F_SECONDARY;
		} else if (strcmp(*argv, "tentative") == 0) {
			filter.flags |= IFA_F_TENTATIVE;
			filter.flagmask |= IFA_F_TENTATIVE;
		} else if (strcmp(*argv, "deprecated") == 0) {
			filter.flags |= IFA_F_DEPRECATED;
			filter.flagmask |= IFA_F_DEPRECATED;
		} else if (strcmp(*argv, "label") == 0) {
			NEXT_ARG();
			filter.label = *argv;
		} else {
			if (strcmp(*argv, "dev") == 0) {
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (filter_dev)
				duparg2("dev", *argv);
			filter_dev = *argv;
		}
		argv++; argc--;
	}

	if (rtnl_open(&rth, 0) < 0)
		exit(1);

	if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
		perror("Cannot send dump request");
		exit(1);
	}

	if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
		fprintf(stderr, "Dump terminated\n");
		exit(1);
	}

	if (filter_dev) {
		filter.ifindex = ll_name_to_index(filter_dev);
		if (filter.ifindex <= 0) {
			fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev);
			return -1;
		}
	}

	if (flush) {
		int round = 0;
		char flushb[4096-512];

		filter.flushb = flushb;
		filter.flushp = 0;
		filter.flushe = sizeof(flushb);
		filter.rth = &rth;

		for (;;) {
			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
				perror("Cannot send dump request");
				exit(1);
			}
			filter.flushed = 0;
			if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
				fprintf(stderr, "Flush terminated\n");
				exit(1);
			}
			if (filter.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 addresses ***\n", round, filter.flushed);
				fflush(stdout);
			}
		}
	}

	if (filter.family != AF_PACKET) {
		if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
			perror("Cannot send dump request");
			exit(1);
		}

		if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
			fprintf(stderr, "Dump terminated\n");
			exit(1);
		}
	}


	if (filter.family && filter.family != AF_PACKET) {
		struct nlmsg_list **lp;
		lp=&linfo;

		if (filter.oneline)
			no_link = 1;

		while ((l=*lp)!=NULL) {
			int ok = 0;
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			struct nlmsg_list *a;

			for (a=ainfo; a; a=a->next) {
				struct nlmsghdr *n = &a->h;
				struct ifaddrmsg *ifa = NLMSG_DATA(n);

				if (ifa->ifa_index != ifi->ifi_index || 
				    (filter.family && filter.family != ifa->ifa_family))
					continue;
				if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
					continue;
				if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
					continue;
				if (filter.pfx.family || filter.label) {
					struct rtattr *tb[IFA_MAX+1];
					memset(tb, 0, sizeof(tb));
					parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
					if (!tb[IFA_LOCAL])
						tb[IFA_LOCAL] = tb[IFA_ADDRESS];

					if (filter.pfx.family && tb[IFA_LOCAL]) {
						inet_prefix dst;
						memset(&dst, 0, sizeof(dst));
						dst.family = ifa->ifa_family;
						memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
						if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
							continue;
					}
					if (filter.label) {
						SPRINT_BUF(b1);
						const char *label;
						if (tb[IFA_LABEL])
							label = RTA_DATA(tb[IFA_LABEL]);
						else
							label = ll_idx_n2a(ifa->ifa_index, b1);
						if (fnmatch(filter.label, label, 0) != 0)
							continue;
					}
				}

				ok = 1;
				break;
			}
			if (!ok)
				*lp = l->next;
			else
				lp = &l->next;
		}
	}

	for (l=linfo; l; l = l->next) {
		if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
			if (filter.family != AF_PACKET)
				print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
		}
		fflush(stdout);
	}

	exit(0);
}
Example #8
0
int ipaddr_modify(int cmd, int argc, char **argv)
{
	struct rtnl_handle rth;
	struct {
		struct nlmsghdr 	n;
		struct ifaddrmsg 	ifa;
		char   			buf[256];
	} req;
	char  *d = NULL;
	char  *l = NULL;
	char  *lcl_arg = NULL;
	inet_prefix lcl;
	inet_prefix peer;
	int local_len = 0;
	int peer_len = 0;
	int brd_len = 0;
	int any_len = 0;
	int scoped = 0;

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

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.n.nlmsg_type = cmd;
	req.ifa.ifa_family = preferred_family;

	while (argc > 0) {
		if (strcmp(*argv, "peer") == 0 ||
		    strcmp(*argv, "remote") == 0) {
			NEXT_ARG();

			if (peer_len)
				duparg("peer", *argv);
			get_prefix(&peer, *argv, req.ifa.ifa_family);
			peer_len = peer.bytelen;
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = peer.family;
			addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
			req.ifa.ifa_prefixlen = peer.bitlen;
		} else if (matches(*argv, "broadcast") == 0 ||
			   strcmp(*argv, "brd") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			if (brd_len)
				duparg("broadcast", *argv);
			if (strcmp(*argv, "+") == 0)
				brd_len = -1;
			else if (strcmp(*argv, "-") == 0)
				brd_len = -2;
			else {
				get_addr(&addr, *argv, req.ifa.ifa_family);
				if (req.ifa.ifa_family == AF_UNSPEC)
					req.ifa.ifa_family = addr.family;
				addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
				brd_len = addr.bytelen;
			}
		} else if (strcmp(*argv, "anycast") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			if (any_len)
				duparg("anycast", *argv);
			get_addr(&addr, *argv, req.ifa.ifa_family);
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = addr.family;
			addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
			any_len = addr.bytelen;
		} else if (strcmp(*argv, "scope") == 0) {
			int scope = 0;
			NEXT_ARG();
			if (rtnl_rtscope_a2n(&scope, *argv))
				invarg(*argv, "invalid scope value.");
			req.ifa.ifa_scope = scope;
			scoped = 1;
		} else if (strcmp(*argv, "dev") == 0) {
			NEXT_ARG();
			d = *argv;
		} else if (strcmp(*argv, "label") == 0) {
			NEXT_ARG();
			l = *argv;
			addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
		} else {
			if (strcmp(*argv, "local") == 0) {
				NEXT_ARG();
			}
			if (matches(*argv, "help") == 0)
				usage();
			if (local_len)
				duparg2("local", *argv);
			lcl_arg = *argv;
			get_prefix(&lcl, *argv, req.ifa.ifa_family);
			if (req.ifa.ifa_family == AF_UNSPEC)
				req.ifa.ifa_family = lcl.family;
			addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
			local_len = lcl.bytelen;
		}
		argc--; argv++;
	}
	if (d == NULL) {
		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
		return -1;
	}
	if (l && matches(d, l) != 0) {
		fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
		exit(1);
	}

	if (peer_len == 0 && local_len) {
		if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
			fprintf(stderr,
			    "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \
			    "         Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \
			    "         This special behaviour is likely to disappear in further releases,\n" \
			    "         fix your scripts!\n", lcl_arg, local_len*8);
		} else {
			peer = lcl;
			addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
		}
	}
	if (req.ifa.ifa_prefixlen == 0)
		req.ifa.ifa_prefixlen = lcl.bitlen;

	if (brd_len < 0 && cmd != RTM_DELADDR) {
		inet_prefix brd;
		int i;
		if (req.ifa.ifa_family != AF_INET) {
			fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
			return -1;
		}
		brd = peer;
		if (brd.bitlen <= 30) {
			for (i=31; i>=brd.bitlen; i--) {
				if (brd_len == -1)
					brd.data[0] |= htonl(1<<(31-i));
				else
					brd.data[0] &= ~htonl(1<<(31-i));
			}
			addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
			brd_len = brd.bytelen;
		}
	}
	if (!scoped && cmd != RTM_DELADDR)
		req.ifa.ifa_scope = default_scope(&lcl);

	if (rtnl_open(&rth, 0) < 0)
		exit(1);

	ll_init_map(&rth);

	if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
		fprintf(stderr, "Cannot find device \"%s\"\n", d);
		return -1;
	}

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

	exit(0);
}
Example #9
0
int iproute_modify(int cmd, unsigned flags, int argc, char *argv)
{
	REQ req;
	char  mxbuf[256];
	struct rtattr * mxrta = (void*)mxbuf;
	unsigned mxlock = 0;
	char  *d = NULL;
	int gw_ok = 0;
	int dst_ok = 0;
	int nhs_ok = 0;
	int scope_ok = 0;
	int table_ok = 0;
//	int type_ok = 0;

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

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
	req.n.nlmsg_type = cmd;
	req.r.rtm_family = preferred_family;
	req.r.rtm_table = RT_TABLE_MAIN;
	req.r.rtm_scope = RT_SCOPE_NOWHERE;

	if(cmd != RTM_DELROUTE) {
		req.r.rtm_protocol = RTPROT_BOOT;
		req.r.rtm_scope = RT_SCOPE_UNIVERSE;
		req.r.rtm_type = RTN_UNICAST;
	}

	mxrta->rta_type = RTA_METRICS;
	mxrta->rta_len = RTA_LENGTH(0);

	while (argc > 0) {
		if(strcmp(argv, "src") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			get_addr(&addr, argv, req.r.rtm_family);
			if(req.r.rtm_family == AF_UNSPEC)
				req.r.rtm_family = addr.family;
			addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
		} else if(strcmp(argv, "via") == 0) {
			inet_prefix addr;
			gw_ok = 1;
			NEXT_ARG();
			get_addr(&addr, argv, req.r.rtm_family);
			if(req.r.rtm_family == AF_UNSPEC)
				req.r.rtm_family = addr.family;
			addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
		} else if(strcmp(argv, "from") == 0) {
			inet_prefix addr;
			NEXT_ARG();
			get_prefix(&addr, argv, req.r.rtm_family);
			if(req.r.rtm_family == AF_UNSPEC)
				req.r.rtm_family = addr.family;
			if(addr.bytelen)
				addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
			req.r.rtm_src_len = addr.bitlen;
		} else if(strcmp(argv, "tos") == 0 ||
			   matches(argv, "dsfield") == 0) {
			__u32 tos;
			NEXT_ARG();
			if(rtnl_dsfield_a2n(&tos, argv))
				invarg("\"tos\" value is invalid\n", argv);
			req.r.rtm_tos = tos;
		} else if(matches(argv, "metric") == 0 ||
			   matches(argv, "priority") == 0 ||
			   matches(argv, "preference") == 0) {
			__u32 metric;
			NEXT_ARG();
			if(get_u32(&metric, argv, 0))
				invarg("\"metric\" value is invalid\n", argv);
			addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
		} else if(strcmp(argv, "scope") == 0) {
			__u32 scope = 0;
			NEXT_ARG();
			if(rtnl_rtscope_a2n(&scope, argv))
				invarg("invalid \"scope\" value\n", argv);
			req.r.rtm_scope = scope;
			scope_ok = 1;
		} else if(strcmp(argv, "mtu") == 0) {
			unsigned mtu;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_MTU);
				NEXT_ARG();
			}
			if(get_unsigned(&mtu, argv, 0))
				invarg("\"mtu\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
#ifdef RTAX_ADVMSS
		} else if(strcmp(argv, "advmss") == 0) {
			unsigned mss;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_ADVMSS);
				NEXT_ARG();
			}
			if(get_unsigned(&mss, argv, 0))
				invarg("\"mss\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
#endif
#ifdef RTAX_REORDERING
		} else if(matches(argv, "reordering") == 0) {
			unsigned reord;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_REORDERING);
				NEXT_ARG();
			}
			if(get_unsigned(&reord, argv, 0))
				invarg("\"reordering\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
#endif
		} else if(strcmp(argv, "rtt") == 0) {
			unsigned rtt;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_RTT);
				NEXT_ARG();
			}
			if(get_unsigned(&rtt, argv, 0))
				invarg("\"rtt\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, rtt);
		} else if(matches(argv, "window") == 0) {
			unsigned win;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_WINDOW);
				NEXT_ARG();
			}
			if(get_unsigned(&win, argv, 0))
				invarg("\"window\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
		} else if(matches(argv, "cwnd") == 0) {
			unsigned win;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_CWND);
				NEXT_ARG();
			}
			if(get_unsigned(&win, argv, 0))
				invarg("\"cwnd\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
		} else if(matches(argv, "rttvar") == 0) {
			unsigned win;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_RTTVAR);
				NEXT_ARG();
			}
			if(get_unsigned(&win, argv, 0))
				invarg("\"rttvar\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, win);
		} else if(matches(argv, "ssthresh") == 0) {
			unsigned win;
			NEXT_ARG();
			if(strcmp(argv, "lock") == 0) {
				mxlock |= (1<<RTAX_SSTHRESH);
				NEXT_ARG();
			}
			if(get_unsigned(&win, argv, 0))
				invarg("\"ssthresh\" value is invalid\n", argv);
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
//		} else if(matches(argv, "realms") == 0) {
//			__u32 realm;
//			NEXT_ARG();
//			if(get_rt_realms(&realm, argv))
//				invarg("\"realm\" value is invalid\n", argv);
//			addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
		} else if(strcmp(argv, "onlink") == 0) {
			req.r.rtm_flags |= RTNH_F_ONLINK;
		} else if(matches(argv, "equalize") == 0 ||
			   strcmp(argv, "eql") == 0) {
			req.r.rtm_flags |= RTM_F_EQUALIZE;
		} else if(strcmp(argv, "nexthop") == 0) {
			nhs_ok = 1;
			break;
		} else if(matches(argv, "protocol") == 0) {
			__u32 prot;
			NEXT_ARG();
			if(rtnl_rtprot_a2n(&prot, argv))
				invarg("\"protocol\" value is invalid\n", argv);
			req.r.rtm_protocol = prot;
		} else if(matches(argv, "table") == 0) {
			__u32 tid;
			NEXT_ARG();
			if(rtnl_rttable_a2n(&tid, argv))
				invarg("\"table\" value is invalid\n", argv);
			req.r.rtm_table = tid;
			table_ok = 1;
		} else if(strcmp(argv, "dev") == 0 ||
			   strcmp(argv, "oif") == 0) {
			NEXT_ARG();
			d = argv;
//		} else if(strcmp(argv, "mpath") == 0 ||
//			   strcmp(argv, "mp") == 0) {
//			int i;
//			__u32 mp_alg = IP_MP_ALG_NONE;
//
//			NEXT_ARG();
//			for (i = 1; i < ARRAY_SIZE(mp_alg_names); i++)
//				if(strcmp(argv, mp_alg_names[i]) == 0)
//					mp_alg = i;
//			if(mp_alg == IP_MP_ALG_NONE)
//				invarg("\"mpath\" value is invalid\n", argv);
//			addattr_l(&req.n, sizeof(req), RTA_MP_ALGO, &mp_alg, sizeof(mp_alg));
		} else {
//			int type;
			inet_prefix dst;

			if(strcmp(argv, "to") == 0) {
				NEXT_ARG();
			}
//			if((*argv < '0' || *argv > '9') &&
//			    rtnl_rtntype_a2n(&type, argv) == 0) {
//				NEXT_ARG();
//				req.r.rtm_type = type;
//				type_ok = 1;
//			}

			if(dst_ok)
				duparg2("to", argv);
			get_prefix(&dst, argv, req.r.rtm_family);
			if(req.r.rtm_family == AF_UNSPEC)
				req.r.rtm_family = dst.family;
			req.r.rtm_dst_len = dst.bitlen;
			dst_ok = 1;
			if(dst.bytelen)
				addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
		}
		argc--; argv++;
	}

	if(d || nhs_ok)  {
		int idx;

		ll_init_map(&rth);

		if(d) {
			if((idx = ll_name_to_index(d)) == 0) {
				fprintf(stderr, "Cannot find device \"%s\"\n", d);
				return -1;
			}
			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
		}
	}

	if(mxrta->rta_len > RTA_LENGTH(0)) {
		if(mxlock)
			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
		addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
	}

	if(nhs_ok)
		parse_nexthops(&req.n, &req.r, argc, argv);

	if(!table_ok) {
		if(req.r.rtm_type == RTN_LOCAL ||
		    req.r.rtm_type == RTN_BROADCAST ||
		    req.r.rtm_type == RTN_NAT ||
		    req.r.rtm_type == RTN_ANYCAST)
			req.r.rtm_table = RT_TABLE_LOCAL;
	}
	if(!scope_ok) {
		if(req.r.rtm_type == RTN_LOCAL ||
		    req.r.rtm_type == RTN_NAT)
			req.r.rtm_scope = RT_SCOPE_HOST;
		else if(req.r.rtm_type == RTN_BROADCAST ||
			 req.r.rtm_type == RTN_MULTICAST ||
			 req.r.rtm_type == RTN_ANYCAST)
			req.r.rtm_scope = RT_SCOPE_LINK;
		else if(req.r.rtm_type == RTN_UNICAST ||
			 req.r.rtm_type == RTN_UNSPEC) {
			if(cmd == RTM_DELROUTE)
				req.r.rtm_scope = RT_SCOPE_NOWHERE;
			else if(!gw_ok && !nhs_ok)
				req.r.rtm_scope = RT_SCOPE_LINK;
		}
	}

	if(req.r.rtm_family == AF_UNSPEC)
		req.r.rtm_family = AF_INET;

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

	return 0;
}