iplink: use netlink for link configuration
Add support for using netlink for link configuration. Kernel-support is probed, when not available it falls back to using ioctls. Signed-off-by: Patrick McHardy <kaber@trash.net>
This commit is contained in:
parent
f7cd9b0354
commit
1d93483985
|
|
@ -22,3 +22,5 @@ install: all
|
|||
clean:
|
||||
rm -f $(ALLOBJ) $(TARGETS)
|
||||
|
||||
LDLIBS += -ldl
|
||||
LDFLAGS += -Wl,-export-dynamic
|
||||
|
|
|
|||
5
ip/ip.c
5
ip/ip.c
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
int preferred_family = AF_UNSPEC;
|
||||
int show_stats = 0;
|
||||
int show_details = 0;
|
||||
int resolve_hosts = 0;
|
||||
int oneline = 0;
|
||||
int timestamp = 0;
|
||||
|
|
@ -48,7 +49,7 @@ static void usage(void)
|
|||
" ip [ -force ] [-batch filename\n"
|
||||
"where OBJECT := { link | addr | route | rule | neigh | ntable | tunnel |\n"
|
||||
" maddr | mroute | monitor | xfrm | veth }\n"
|
||||
" OPTIONS := { -V[ersion] | -s[tatistics] | -r[esolve] |\n"
|
||||
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
|
||||
" -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
|
||||
" -o[neline] | -t[imestamp] }\n");
|
||||
exit(-1);
|
||||
|
|
@ -190,6 +191,8 @@ int main(int argc, char **argv)
|
|||
} else if (matches(opt, "-stats") == 0 ||
|
||||
matches(opt, "-statistics") == 0) {
|
||||
++show_stats;
|
||||
} else if (matches(opt, "-details") == 0) {
|
||||
++show_details;
|
||||
} else if (matches(opt, "-resolve") == 0) {
|
||||
++resolve_hosts;
|
||||
} else if (matches(opt, "-oneline") == 0) {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,21 @@ static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
|
|||
|
||||
extern struct rtnl_handle rth;
|
||||
|
||||
struct link_util
|
||||
{
|
||||
struct link_util *next;
|
||||
const char *id;
|
||||
int maxattr;
|
||||
int (*parse_opt)(struct link_util *, int, char **,
|
||||
struct nlmsghdr *);
|
||||
void (*print_opt)(struct link_util *, FILE *,
|
||||
struct rtattr *[]);
|
||||
void (*print_xstats)(struct link_util *, FILE *,
|
||||
struct rtattr *);
|
||||
};
|
||||
|
||||
struct link_util *get_link_kind(const char *kind);
|
||||
|
||||
#ifndef INFINITY_LIFE_TIME
|
||||
#define INFINITY_LIFE_TIME 0xFFFFFFFFU
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -136,6 +136,41 @@ void print_queuelen(char *name)
|
|||
printf("qlen %d", ifr.ifr_qlen);
|
||||
}
|
||||
|
||||
static void print_linktype(FILE *fp, struct rtattr *tb)
|
||||
{
|
||||
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
|
||||
struct link_util *lu;
|
||||
char *kind;
|
||||
|
||||
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
|
||||
|
||||
if (!linkinfo[IFLA_INFO_KIND])
|
||||
return;
|
||||
kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]);
|
||||
|
||||
fprintf(fp, "%s", _SL_);
|
||||
fprintf(fp, " %s ", kind);
|
||||
|
||||
lu = get_link_kind(kind);
|
||||
if (!lu || !lu->print_opt)
|
||||
return;
|
||||
|
||||
if (1) {
|
||||
struct rtattr *attr[lu->maxattr+1], **data = NULL;
|
||||
|
||||
if (linkinfo[IFLA_INFO_DATA]) {
|
||||
parse_rtattr_nested(attr, lu->maxattr,
|
||||
linkinfo[IFLA_INFO_DATA]);
|
||||
data = attr;
|
||||
}
|
||||
lu->print_opt(lu, fp, data);
|
||||
|
||||
if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
|
||||
lu->print_xstats)
|
||||
lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
|
||||
}
|
||||
}
|
||||
|
||||
int print_linkinfo(const struct sockaddr_nl *who,
|
||||
struct nlmsghdr *n, void *arg)
|
||||
{
|
||||
|
|
@ -223,6 +258,10 @@ int print_linkinfo(const struct sockaddr_nl *who,
|
|||
b1, sizeof(b1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (do_link && tb[IFLA_LINKINFO] && show_details)
|
||||
print_linktype(fp, tb[IFLA_LINKINFO]);
|
||||
|
||||
if (do_link && tb[IFLA_STATS] && show_stats) {
|
||||
struct rtnl_link_stats slocal;
|
||||
struct rtnl_link_stats *s = RTA_DATA(tb[IFLA_STATS]);
|
||||
|
|
|
|||
311
ip/iplink.c
311
ip/iplink.c
|
|
@ -15,6 +15,7 @@
|
|||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/if.h>
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
#include "utils.h"
|
||||
#include "ip_common.h"
|
||||
|
||||
#define IPLINK_IOCTL_COMPAT 1
|
||||
|
||||
static void usage(void) __attribute__((noreturn));
|
||||
|
||||
|
|
@ -62,6 +64,290 @@ static int on_off(char *msg)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static void *BODY; /* cached dlopen(NULL) handle */
|
||||
static struct link_util *linkutil_list;
|
||||
|
||||
struct link_util *get_link_kind(const char *id)
|
||||
{
|
||||
void *dlh;
|
||||
char buf[256];
|
||||
struct link_util *l;
|
||||
|
||||
for (l = linkutil_list; l; l = l->next)
|
||||
if (strcmp(l->id, id) == 0)
|
||||
return l;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/usr/lib/ip/link_%s.so", id);
|
||||
dlh = dlopen(buf, RTLD_LAZY);
|
||||
if (dlh == NULL) {
|
||||
/* look in current binary, only open once */
|
||||
dlh = BODY;
|
||||
if (dlh == NULL) {
|
||||
dlh = BODY = dlopen(NULL, RTLD_LAZY);
|
||||
if (dlh == NULL)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s_link_util", id);
|
||||
l = dlsym(dlh, buf);
|
||||
if (l == NULL)
|
||||
return NULL;
|
||||
|
||||
l->next = linkutil_list;
|
||||
linkutil_list = l;
|
||||
return l;
|
||||
}
|
||||
|
||||
#if IPLINK_IOCTL_COMPAT
|
||||
static int have_rtnl_newlink = -1;
|
||||
|
||||
static int accept_msg(const struct sockaddr_nl *who,
|
||||
struct nlmsghdr *n, void *arg)
|
||||
{
|
||||
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
|
||||
|
||||
if (n->nlmsg_type == NLMSG_ERROR && err->error == -EOPNOTSUPP)
|
||||
have_rtnl_newlink = 0;
|
||||
else
|
||||
have_rtnl_newlink = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int iplink_have_newlink(void)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg i;
|
||||
char buf[1024];
|
||||
} req;
|
||||
|
||||
if (have_rtnl_newlink < 0) {
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
|
||||
req.n.nlmsg_type = RTM_NEWLINK;
|
||||
req.i.ifi_family = AF_UNSPEC;
|
||||
|
||||
rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len);
|
||||
rtnl_listen(&rth, accept_msg, NULL);
|
||||
}
|
||||
return have_rtnl_newlink;
|
||||
}
|
||||
#else /* IPLINK_IOCTL_COMPAT */
|
||||
static int iplink_have_newlink(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* ! IPLINK_IOCTL_COMPAT */
|
||||
|
||||
static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||
{
|
||||
int qlen = -1;
|
||||
int mtu = -1;
|
||||
int len;
|
||||
char abuf[32];
|
||||
char *dev = NULL;
|
||||
char *name = NULL;
|
||||
char *link = NULL;
|
||||
char *type = NULL;
|
||||
struct link_util *lu = NULL;
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg i;
|
||||
char buf[1024];
|
||||
} req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST|flags;
|
||||
req.n.nlmsg_type = cmd;
|
||||
req.i.ifi_family = preferred_family;
|
||||
|
||||
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 (matches(*argv, "link") == 0) {
|
||||
NEXT_ARG();
|
||||
link = *argv;
|
||||
} else if (matches(*argv, "address") == 0) {
|
||||
NEXT_ARG();
|
||||
len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
|
||||
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);
|
||||
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, "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");
|
||||
} 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");
|
||||
} 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");
|
||||
} 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");
|
||||
} 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");
|
||||
#ifdef IFF_DYNAMIC
|
||||
} 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");
|
||||
#endif
|
||||
} else if (matches(*argv, "type") == 0) {
|
||||
NEXT_ARG();
|
||||
type = *argv;
|
||||
argc--; argv++;
|
||||
break;
|
||||
} else {
|
||||
if (strcmp(*argv, "dev") == 0) {
|
||||
NEXT_ARG();
|
||||
}
|
||||
if (dev)
|
||||
duparg2("dev", *argv);
|
||||
dev = *argv;
|
||||
}
|
||||
argc--; argv++;
|
||||
}
|
||||
|
||||
ll_init_map(&rth);
|
||||
|
||||
if (type) {
|
||||
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,
|
||||
strlen(type));
|
||||
|
||||
lu = get_link_kind(type);
|
||||
if (lu && argc) {
|
||||
struct rtattr * data = NLMSG_TAIL(&req.n);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
|
||||
|
||||
if (lu->parse_opt &&
|
||||
lu->parse_opt(lu, argc, argv, &req.n))
|
||||
return -1;
|
||||
|
||||
data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
|
||||
} else if (argc) {
|
||||
if (matches(*argv, "help") == 0)
|
||||
usage();
|
||||
fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
|
||||
"Try \"ip link help\".\n", *argv);
|
||||
return -1;
|
||||
}
|
||||
linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
|
||||
}
|
||||
|
||||
if (!(flags & NLM_F_CREATE)) {
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Not enough information: \"dev\" "
|
||||
"argument is required.\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
req.i.ifi_index = ll_name_to_index(dev);
|
||||
if (req.i.ifi_index == 0) {
|
||||
fprintf(stderr, "Cannot find device \"%s\"\n", dev);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* Allow "ip link add dev" and "ip link add name" */
|
||||
if (!name)
|
||||
name = dev;
|
||||
|
||||
if (link) {
|
||||
int ifindex;
|
||||
|
||||
ifindex = ll_name_to_index(link);
|
||||
if (ifindex == 0) {
|
||||
fprintf(stderr, "Cannot find device \"%s\"\n",
|
||||
link);
|
||||
return -1;
|
||||
}
|
||||
addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
|
||||
}
|
||||
}
|
||||
|
||||
if (name) {
|
||||
len = strlen(name) + 1;
|
||||
if (len > IFNAMSIZ)
|
||||
invarg("\"name\" too long\n", *argv);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
|
||||
}
|
||||
|
||||
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
|
||||
exit(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IPLINK_IOCTL_COMPAT
|
||||
static int get_ctl_fd(void)
|
||||
{
|
||||
int s_errno;
|
||||
|
|
@ -410,12 +696,33 @@ static int do_set(int argc, char **argv)
|
|||
return do_chflags(dev, flags, mask);
|
||||
return 0;
|
||||
}
|
||||
#endif /* IPLINK_IOCTL_COMPAT */
|
||||
|
||||
int do_iplink(int argc, char **argv)
|
||||
{
|
||||
if (argc > 0) {
|
||||
if (matches(*argv, "set") == 0)
|
||||
return do_set(argc-1, argv+1);
|
||||
if (iplink_have_newlink()) {
|
||||
if (matches(*argv, "add") == 0)
|
||||
return iplink_modify(RTM_NEWLINK,
|
||||
NLM_F_CREATE|NLM_F_EXCL,
|
||||
argc-1, argv+1);
|
||||
if (matches(*argv, "set") == 0 ||
|
||||
matches(*argv, "change") == 0)
|
||||
return iplink_modify(RTM_NEWLINK, 0,
|
||||
argc-1, argv+1);
|
||||
if (matches(*argv, "replace") == 0)
|
||||
return iplink_modify(RTM_NEWLINK,
|
||||
NLM_F_CREATE|NLM_F_REPLACE,
|
||||
argc-1, argv+1);
|
||||
if (matches(*argv, "delete") == 0)
|
||||
return iplink_modify(RTM_DELLINK, 0,
|
||||
argc-1, argv+1);
|
||||
} else {
|
||||
#if IPLINK_IOCTL_COMPAT
|
||||
if (matches(*argv, "set") == 0)
|
||||
return do_set(argc-1, argv+1);
|
||||
#endif
|
||||
}
|
||||
if (matches(*argv, "show") == 0 ||
|
||||
matches(*argv, "lst") == 0 ||
|
||||
matches(*argv, "list") == 0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue