aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--networking/libiproute/iplink.c93
1 files changed, 91 insertions, 2 deletions
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
index df8a354a1..a28df6e79 100644
--- a/networking/libiproute/iplink.c
+++ b/networking/libiproute/iplink.c
@@ -283,11 +283,98 @@ static int ipaddr_list_link(char **argv)
return ipaddr_list_or_flush(argv, 0);
}
+#ifndef NLMSG_TAIL
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+#endif
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_change(char **argv, const unsigned rtm)
+{
+ static const char keywords[] ALIGN1 =
+ "link\0""name\0""type\0""dev\0";
+ enum {
+ ARG_link,
+ ARG_name,
+ ARG_type,
+ ARG_dev,
+ };
+ struct rtnl_handle rth;
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg i;
+ char buf[1024];
+ } req;
+ int arg;
+ char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = rtm;
+ req.i.ifi_family = preferred_family;
+ if (rtm == RTM_NEWLINK)
+ req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+
+ while (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == ARG_link) {
+ NEXT_ARG();
+ link_str = *argv;
+ } else if (arg == ARG_name) {
+ NEXT_ARG();
+ name_str = *argv;
+ } else if (arg == ARG_type) {
+ NEXT_ARG();
+ type_str = *argv;
+ } else {
+ if (arg == ARG_dev) {
+ if (dev_str)
+ duparg(*argv, "dev");
+ NEXT_ARG();
+ }
+ dev_str = *argv;
+ }
+ argv++;
+ }
+ xrtnl_open(&rth);
+ ll_init_map(&rth);
+ if (type_str) {
+ struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
+
+ addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
+ addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
+ strlen(type_str));
+ linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
+ }
+ if (rtm != RTM_NEWLINK) {
+ if (!dev_str)
+ return 1; /* Need a device to delete */
+ req.i.ifi_index = xll_name_to_index(dev_str);
+ } else {
+ if (!name_str)
+ name_str = dev_str;
+ if (link_str) {
+ int idx = xll_name_to_index(link_str);
+ addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
+ }
+ }
+ if (name_str) {
+ const size_t name_len = strlen(name_str) + 1;
+ if (name_len < 2 || name_len > IFNAMSIZ)
+ invarg(name_str, "name");
+ addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
+ }
+ if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+ return 2;
+ return 0;
+}
+
/* Return value becomes exitcode. It's okay to not return at all */
int do_iplink(char **argv)
{
static const char keywords[] ALIGN1 =
- "set\0""show\0""lst\0""list\0";
+ "add\0""delete\0""set\0""show\0""lst\0""list\0";
int key;
if (!*argv)
return ipaddr_list_link(argv);
@@ -295,7 +382,9 @@ int do_iplink(char **argv)
if (key < 0)
bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
argv++;
- if (key == 0) /* set */
+ if (key <= 1) /* add/delete */
+ return do_change(argv, key ? RTM_DELLINK : RTM_NEWLINK);
+ else if (key == 2) /* set */
return do_set(argv);
/* show, lst, list */
return ipaddr_list_link(argv);