commit f389090cd1dc2f9945c19f837ade98bd733015cb Author: Pavel Šimerda Date: Thu May 28 23:39:22 2015 +0200 backport selected library functions diff --git a/include/libnetlink.h b/include/libnetlink.h index ec3d657..5dcc0c3 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -18,6 +18,7 @@ struct rtnl_handle struct sockaddr_nl peer; __u32 seq; __u32 dump; + FILE *dump_fp; }; extern int rcvbuf; @@ -68,11 +69,15 @@ extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int le extern int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, int len, unsigned short flags); extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len); extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len); #define parse_rtattr_nested(tb, max, rta) \ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) +#define parse_rtattr_one_nested(type, rta) \ + (parse_rtattr_one(type, RTA_DATA(rta), RTA_PAYLOAD(rta))) + #define parse_rtattr_nested_compat(tb, max, rta, data, len) \ ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ __parse_rtattr_nested_compat(tb, max, rta, len); }) @@ -140,5 +145,17 @@ extern int rtnl_from_file(FILE *, rtnl_filter_t handler, #define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) #endif +#ifndef NETNS_RTA +#define NETNS_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) +#endif +#ifndef NETNS_PAYLOAD +#define NETNS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtgenmsg)) +#endif + +/* User defined nlmsg_type which is used mostly for logging netlink + * messages from dump file */ +#define NLMSG_TSTAMP 15 + #endif /* __LIBNETLINK_H__ */ diff --git a/include/namespace.h b/include/namespace.h new file mode 100644 index 0000000..a2ac7dc --- /dev/null +++ b/include/namespace.h @@ -0,0 +1,54 @@ +#ifndef __NAMESPACE_H__ +#define __NAMESPACE_H__ 1 + +#include +#include +#include +#include + +#define NETNS_RUN_DIR "/var/run/netns" +#define NETNS_ETC_DIR "/etc/netns" + +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 0x00000002 /* Just detach from the tree */ +#endif /* MNT_DETACH */ + +/* sys/mount.h may be out too old to have these */ +#ifndef MS_REC +#define MS_REC 16384 +#endif + +#ifndef MS_SLAVE +#define MS_SLAVE (1 << 19) +#endif + +#ifndef MS_SHARED +#define MS_SHARED (1 << 20) +#endif + +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif /* HAVE_SETNS */ + +extern int netns_switch(char *netns); +extern int netns_get_fd(const char *netns); +extern int netns_foreach(int (*func)(char *nsname, void *arg), void *arg); + +struct netns_func { + int (*func)(char *nsname, void *arg); + void *arg; +}; + +#endif /* __NAMESPACE_H__ */ diff --git a/include/rt_names.h b/include/rt_names.h index 37adbd3..c0ea4f9 100644 --- a/include/rt_names.h +++ b/include/rt_names.h @@ -8,6 +8,7 @@ const char *rtnl_rtscope_n2a(int id, char *buf, int len); const char *rtnl_rttable_n2a(__u32 id, char *buf, int len); const char *rtnl_rtrealm_n2a(int id, char *buf, int len); const char *rtnl_dsfield_n2a(int id, char *buf, int len); +const char *rtnl_group_n2a(int id, char *buf, int len); int rtnl_rtprot_a2n(__u32 *id, const char *arg); int rtnl_rtscope_a2n(__u32 *id, const char *arg); @@ -28,5 +29,7 @@ int ll_addr_a2n(char *lladdr, int len, const char *arg); const char * ll_proto_n2a(unsigned short id, char *buf, int len); int ll_proto_a2n(unsigned short *id, const char *buf); +const char *nl_proto_n2a(int id, char *buf, int len); +int nl_proto_a2n(__u32 *id, const char *arg); #endif diff --git a/include/utils.h b/include/utils.h index 24ff19f..ca05b02 100644 --- a/include/utils.h +++ b/include/utils.h @@ -5,12 +5,15 @@ #include #include #include +#include #include "libnetlink.h" #include "ll_map.h" #include "rtm_map.h" extern int preferred_family; +extern int human_readable; +extern int use_iec; extern int show_stats; extern int show_details; extern int show_raw; @@ -20,6 +23,7 @@ extern int timestamp; extern char * _SL_; extern int max_flush_loops; extern int batch_mode; +extern bool do_all; #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 @@ -151,9 +155,16 @@ int print_timestamp(FILE *fp); extern int cmdlineno; extern ssize_t getcmdline(char **line, size_t *len, FILE *in); extern int makeargs(char *line, char *argv[], int maxargs); +extern int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6); struct iplink_req; int iplink_parse(int argc, char **argv, struct iplink_req *req, char **name, char **type, char **link, char **dev, int *group); + +extern int do_each_netns(int (*func)(char *nsname, void *arg), void *arg, + bool show_label); + +char *int_to_str(int val, char *buf); + #endif /* __UTILS_H__ */ diff --git a/lib/Makefile b/lib/Makefile index a42b885..66f89f1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,8 +1,12 @@ include ../Config +ifeq ($(IP_CONFIG_SETNS),y) + CFLAGS += -DHAVE_SETNS +endif + CFLAGS += -fPIC -UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o +UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o namespace.o NLOBJ=libgenl.o ll_map.o libnetlink.o diff --git a/lib/namespace.c b/lib/namespace.c new file mode 100644 index 0000000..c03a103 --- /dev/null +++ b/lib/namespace.c @@ -0,0 +1,123 @@ +/* + * namespace.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include "utils.h" +#include "namespace.h" + +static void bind_etc(const char *name) +{ + char etc_netns_path[MAXPATHLEN]; + char netns_name[MAXPATHLEN]; + char etc_name[MAXPATHLEN]; + struct dirent *entry; + DIR *dir; + + snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name); + dir = opendir(etc_netns_path); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name); + snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name); + if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) { + fprintf(stderr, "Bind %s -> %s failed: %s\n", + netns_name, etc_name, strerror(errno)); + } + } + closedir(dir); +} + +int netns_switch(char *name) +{ + char net_path[MAXPATHLEN]; + int netns; + + snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); + netns = open(net_path, O_RDONLY | O_CLOEXEC); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace \"%s\": %s\n", + name, strerror(errno)); + return -1; + } + + if (setns(netns, CLONE_NEWNET) < 0) { + fprintf(stderr, "setting the network namespace \"%s\" failed: %s\n", + name, strerror(errno)); + return -1; + } + + if (unshare(CLONE_NEWNS) < 0) { + fprintf(stderr, "unshare failed: %s\n", strerror(errno)); + return -1; + } + /* Don't let any mounts propagate back to the parent */ + if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL)) { + fprintf(stderr, "\"mount --make-rslave /\" failed: %s\n", + strerror(errno)); + return -1; + } + /* Mount a version of /sys that describes the network namespace */ + if (umount2("/sys", MNT_DETACH) < 0) { + fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno)); + return -1; + } + if (mount(name, "/sys", "sysfs", 0, NULL) < 0) { + fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno)); + return -1; + } + + /* Setup bind mounts for config files in /etc */ + bind_etc(name); + return 0; +} + +int netns_get_fd(const char *name) +{ + char pathbuf[MAXPATHLEN]; + const char *path, *ptr; + + path = name; + ptr = strchr(name, '/'); + if (!ptr) { + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + NETNS_RUN_DIR, name ); + path = pathbuf; + } + return open(path, O_RDONLY); +} + +int netns_foreach(int (*func)(char *nsname, void *arg), void *arg) +{ + DIR *dir; + struct dirent *entry; + + dir = opendir(NETNS_RUN_DIR); + if (!dir) + return -1; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + if (func(entry->d_name, arg)) + break; + } + + closedir(dir); + return 0; +} diff --git a/lib/rt_names.c b/lib/rt_names.c index 02f1417..c2e60de 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -500,3 +500,104 @@ int rtnl_group_a2n(int *id, const char *arg) *id = i; return 0; } + +const char *rtnl_group_n2a(int id, char *buf, int len) +{ + struct rtnl_hash_entry *entry; + int i; + + if (!rtnl_group_init) + rtnl_group_initialize(); + + for (i=0; i<256; i++) { + entry = rtnl_group_hash[i]; + if (entry && entry->id == id) { + return entry->name; + } + } + + snprintf(buf, len, "%d", id); + return buf; +} + +static char *nl_proto_tab[256] = { + [NETLINK_ROUTE] = "rtnl", + [NETLINK_UNUSED] = "unused", + [NETLINK_USERSOCK] = "usersock", + [NETLINK_FIREWALL] = "fw", + [NETLINK_SOCK_DIAG] = "tcpdiag", + [NETLINK_NFLOG] = "nflog", + [NETLINK_XFRM] = "xfrm", + [NETLINK_SELINUX] = "selinux", + [NETLINK_ISCSI] = "iscsi", + [NETLINK_AUDIT] = "audit", + [NETLINK_FIB_LOOKUP] = "fiblookup", + [NETLINK_CONNECTOR] = "connector", + [NETLINK_NETFILTER] = "nft", + [NETLINK_IP6_FW] = "ip6fw", + [NETLINK_DNRTMSG] = "dec-rt", + [NETLINK_KOBJECT_UEVENT] = "uevent", + [NETLINK_GENERIC] = "genl", + [NETLINK_SCSITRANSPORT] = "scsi-trans", + [NETLINK_ECRYPTFS] = "ecryptfs", + [NETLINK_RDMA] = "rdma", + [NETLINK_CRYPTO] = "crypto", +}; + +static int nl_proto_init; + +static void nl_proto_initialize(void) +{ + nl_proto_init = 1; + rtnl_tab_initialize(CONFDIR "/nl_protos", + nl_proto_tab, 256); +} + +const char *nl_proto_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256) { + snprintf(buf, len, "%u", id); + return buf; + } + + if (!nl_proto_init) + nl_proto_initialize(); + + if (nl_proto_tab[id]) + return nl_proto_tab[id]; + + snprintf(buf, len, "%u", id); + return buf; +} + +int nl_proto_a2n(__u32 *id, const char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!nl_proto_init) + nl_proto_initialize(); + + for (i = 0; i < 256; i++) { + if (nl_proto_tab[i] && + strcmp(nl_proto_tab[i], arg) == 0) { + cache = nl_proto_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} diff --git a/lib/utils.c b/lib/utils.c index 0f9de02..299b485 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -31,6 +31,7 @@ #include "utils.h" +#include "namespace.h" int get_integer(int *val, const char *arg, int base) { @@ -864,3 +865,44 @@ int makeargs(char *line, char *argv[], int maxargs) return argc; } + +int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6) +{ + if (strchr(src, ':')) + return inet_pton(AF_INET6, src, dst6); + else + return inet_pton(AF_INET, src, dst); +} + +static int on_netns(char *nsname, void *arg) +{ + struct netns_func *f = arg; + + if (netns_switch(nsname)) + return -1; + + return f->func(nsname, f->arg); +} + +static int on_netns_label(char *nsname, void *arg) +{ + printf("\nnetns: %s\n", nsname); + return on_netns(nsname, arg); +} + +int do_each_netns(int (*func)(char *nsname, void *arg), void *arg, + bool show_label) +{ + struct netns_func nsf = { .func = func, .arg = arg }; + + if (show_label) + return netns_foreach(on_netns_label, &nsf); + + return netns_foreach(on_netns, &nsf); +} + +char *int_to_str(int val, char *buf) +{ + sprintf(buf, "%d", val); + return buf; +} diff --git a/misc/Makefile b/misc/Makefile index a59ff87..a516bd8 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -5,6 +5,10 @@ TARGETS=ss nstat ifstat rtacct arpd lnstat include ../Config +ifeq ($(IP_CONFIG_SETNS),y) + CFLAGS += -DHAVE_SETNS +endif + all: $(TARGETS) ss: $(SSOBJ)