├── .gitignore ├── COPYING ├── Makefile ├── README ├── addr.c ├── addr.h ├── args.c ├── args.h ├── compat.h ├── ethtool.c ├── ethtool.h ├── frontend.c ├── frontend.h ├── frontends ├── dot.c ├── dot.h ├── json.c └── json.h ├── handler.c ├── handler.h ├── handlers ├── bond.c ├── bond.h ├── bridge.c ├── bridge.h ├── geneve.c ├── geneve.h ├── gre.c ├── gre.h ├── iov.c ├── iov.h ├── ipxipy.c ├── ipxipy.h ├── macsec.c ├── macsec.h ├── openvswitch.c ├── openvswitch.h ├── route.c ├── route.h ├── team.c ├── team.h ├── veth.c ├── veth.h ├── vlan.c ├── vlan.h ├── vti.c ├── vti.h ├── vxlan.c ├── vxlan.h ├── xfrm.c └── xfrm.h ├── if.c ├── if.h ├── label.c ├── label.h ├── list.h ├── main.c ├── master.c ├── master.h ├── match.c ├── match.h ├── netlink.c ├── netlink.h ├── netns.c ├── netns.h ├── plotnetcfg-json.5 ├── plotnetcfg.8 ├── route.c ├── route.h ├── sysfs.c ├── sysfs.h ├── tunnel.c ├── tunnel.h ├── utils.c ├── utils.h └── version /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | plotnetcfg 3 | version.h 4 | tags 5 | Makefile.dep 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(jansson),) 2 | libs=$(shell pkg-config --libs jansson) 3 | else 4 | libs=$(wildcard $(jansson)/src/.libs/libjansson.a $(jansson)/lib/libjansson.a) 5 | INCLUDE=-I$(jansson)/include 6 | endif 7 | 8 | CFLAGS ?= -W -Wall 9 | EXTRA_CFLAGS = -std=c99 -D_GNU_SOURCE $(INCLUDE) 10 | 11 | OBJECTS=addr args ethtool frontend handler if label main master \ 12 | match netlink netns route sysfs tunnel utils 13 | HANDLERS=bond bridge geneve gre iov ipxipy macsec openvswitch team veth vlan vti vxlan xfrm route 14 | FRONTENDS=dot json 15 | 16 | OBJ=$(OBJECTS:%=%.o) $(HANDLERS:%=handlers/%.o) $(FRONTENDS:%=frontends/%.o) 17 | 18 | all: check-libs plotnetcfg 19 | 20 | plotnetcfg: $(OBJ) 21 | $(CC) $(LDFLAGS) -o $@ $+ $(libs) 22 | 23 | Makefile.dep: version.h $(OBJ:.o=.c) 24 | $(CC) -M $(CFLAGS) $(EXTRA_CFLAGS) $(OBJ:.o=.c) | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' >$@ 25 | 26 | -include Makefile.dep 27 | 28 | version.h: 29 | echo "#define VERSION \"`git describe 2> /dev/null || cat version`\"" > version.h 30 | 31 | %.o: %.c 32 | $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< 33 | 34 | clean: 35 | rm -f version.h Makefile.dep *.o frontends/*.o handlers/*.o plotnetcfg 36 | 37 | install: plotnetcfg 38 | install -d $(DESTDIR)/usr/sbin/ 39 | install plotnetcfg $(DESTDIR)/usr/sbin/ 40 | install -d $(DESTDIR)/usr/share/man/man8/ 41 | install -d $(DESTDIR)/usr/share/man/man5/ 42 | install -m 644 plotnetcfg.8 $(DESTDIR)/usr/share/man/man8/ 43 | install -m 644 plotnetcfg-json.5 $(DESTDIR)/usr/share/man/man5/ 44 | 45 | .PHONY: check-libs 46 | check-libs: 47 | @if [ "$(libs)" = "" ]; then \ 48 | echo "ERROR: libjansson not found."; \ 49 | exit 1; \ 50 | fi 51 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | plotnetcfg is a tool that scans networking configuration on the machine and 2 | plots a diagram of the configuration hierarchy. 3 | 4 | Design goals are: 5 | 1. Lighweight. It should have no run time dependencies. Just scp the 6 | binary to the target machine. 7 | 2. Works across net namespaces. 8 | 3. Allows multiple frontends (graphviz and json frontends are currently 9 | supported). 10 | 11 | 12 | Example usage: 13 | 14 | ssh target_machine plotnetcfg | dot -Tpdf > output.pdf 15 | 16 | 17 | Building: 18 | 19 | Jansson library is required, version 2.3 or newer: 20 | http://www.digip.org/jansson/ 21 | 22 | To statically link against Jansson, build the Jansson library and invoke in 23 | the plotnetcfg source directory: 24 | 25 | make jansson=/path/to/jansson 26 | 27 | To dynamically link against Jansson, ensure Jansson is properly installed in 28 | the system (e.g. using the devel package provided by your distribution) and 29 | invoke: 30 | 31 | make 32 | 33 | The following flags can be overridden during the build: 34 | - CC 35 | Choose a different compiler (such as clang or an arm cross compiler) 36 | - CFLAGS 37 | Additional compilation flags to pass to the compiler 38 | 39 | Bugs: 40 | 41 | Report bugs to jbenc@redhat.com. Patches are welcome. 42 | 43 | 44 | Contains code from iproute2, originally licensed under GPLv2 or later, 45 | http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 46 | -------------------------------------------------------------------------------- /addr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Jiri Benc 4 | * Ondrej Hlavaty 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2 9 | * of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "addr.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "netlink.h" 24 | 25 | int addr_init(struct addr *dest, int family, int prefixlen, const void *raw) 26 | { 27 | char buf[64]; 28 | unsigned int len = family == AF_INET ? 4 : 16; 29 | 30 | dest->family = family; 31 | dest->prefixlen = prefixlen; 32 | dest->raw = malloc(len); 33 | if (!dest->raw) 34 | goto err; 35 | memcpy(dest->raw, raw, len); 36 | 37 | if (!inet_ntop(family, raw, buf, sizeof(buf))) 38 | return errno; 39 | len = strlen(buf); 40 | if (prefixlen >= 0) 41 | snprintf(buf + len, sizeof(buf) - len, "/%d", prefixlen); 42 | dest->formatted = strdup(buf); 43 | if (!dest->formatted) 44 | goto err_raw; 45 | 46 | return 0; 47 | 48 | err_raw: 49 | free(dest->raw); 50 | err: 51 | return ENOMEM; 52 | } 53 | 54 | int addr_init_netlink(struct addr *dest, const struct ifaddrmsg *ifa, 55 | const struct nlattr *nla) 56 | { 57 | return addr_init(dest, ifa->ifa_family, ifa->ifa_prefixlen, nla_read(nla)); 58 | } 59 | 60 | int addr_parse_raw(void *dest, const char *src) 61 | { 62 | int af; 63 | 64 | af = strchr(src, ':') ? AF_INET6 : AF_INET; 65 | if (inet_pton(af, src, dest) <= 0) 66 | return -1; 67 | 68 | return af; 69 | } 70 | 71 | int addr_is_zero(struct addr *addr) 72 | { 73 | static const char zero [16]; 74 | unsigned int len = addr->family == AF_INET ? 4 : 16; 75 | 76 | return !memcmp(zero, addr->raw, len); 77 | } 78 | 79 | void addr_destruct(struct addr *addr) 80 | { 81 | if (addr->raw) { 82 | free(addr->raw); 83 | free(addr->formatted); 84 | addr->raw = addr->formatted = NULL; 85 | } 86 | } 87 | 88 | int mac_addr_init(struct mac_addr *addr) 89 | { 90 | addr->len = 0; 91 | addr->raw = NULL; 92 | addr->formatted = NULL; 93 | return 0; 94 | } 95 | 96 | int mac_addr_fill_netlink(struct mac_addr *addr, const struct nlattr *nla) 97 | { 98 | const unsigned char *data = nla_read(nla); 99 | int len = nla_len(nla); 100 | int i; 101 | 102 | addr->len = len; 103 | 104 | addr->raw = malloc(len); 105 | if (!addr->raw) 106 | return ENOMEM; 107 | 108 | addr->formatted = malloc(len * 3); 109 | if (!addr->raw) 110 | goto err_raw; 111 | 112 | memcpy(addr->raw, data, len); 113 | 114 | for (i = 0; i < len; i++) 115 | if (3 != snprintf(addr->formatted + i * 3, (i+1 == len) ? 3 : 4 , "%02x:", data[i])) 116 | goto err_formatted; 117 | 118 | return 0; 119 | 120 | err_formatted: 121 | free(addr->formatted); 122 | addr->formatted = NULL; 123 | err_raw: 124 | free(addr->raw); 125 | addr->raw = NULL; 126 | addr->len = 0; 127 | 128 | return errno; 129 | } 130 | 131 | void mac_addr_destruct(struct mac_addr *addr) 132 | { 133 | if (addr->raw) 134 | free(addr->raw); 135 | if (addr->formatted) 136 | free(addr->formatted); 137 | } 138 | -------------------------------------------------------------------------------- /addr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Jiri Benc 4 | * Ondrej Hlavaty 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2 9 | * of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #ifndef _ADDR_H 18 | #define _ADDR_H 19 | 20 | #include 21 | 22 | struct ifaddrmsg; 23 | struct nlattr; 24 | 25 | struct addr { 26 | int family; 27 | int prefixlen; 28 | char *formatted; 29 | void *raw; 30 | }; 31 | 32 | struct mac_addr { 33 | int len; 34 | unsigned char *raw; 35 | char *formatted; 36 | }; 37 | 38 | int addr_init(struct addr *addr, int ai_family, int prefixlen, const void *raw); 39 | int addr_init_netlink(struct addr *dest, const struct ifaddrmsg *ifa, 40 | const struct nlattr *nla); 41 | 42 | /* dest must point to at least 16 bytes long buffer */ 43 | int addr_parse_raw(void *dest, const char *str); 44 | 45 | static inline int addr_max_prefix_len(int ai_family) 46 | { 47 | switch (ai_family) { 48 | case AF_INET: return 32; 49 | case AF_INET6: return 128; 50 | } 51 | return 0; 52 | } 53 | 54 | int addr_is_zero(struct addr *addr); 55 | void addr_destruct(struct addr *addr); 56 | 57 | int mac_addr_init(struct mac_addr *addr); 58 | int mac_addr_fill_netlink(struct mac_addr *addr, const struct nlattr *nla); 59 | 60 | void mac_addr_destruct(struct mac_addr *addr); 61 | #endif 62 | -------------------------------------------------------------------------------- /args.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "args.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | struct { 23 | struct list list; 24 | int count; 25 | } options = { 26 | .list = LIST_INITIALIZER(options.list), 27 | .count = 0 28 | }; 29 | 30 | void arg_register(struct arg_option *opt) 31 | { 32 | options.count++; 33 | list_append(&options.list, node(opt)); 34 | } 35 | 36 | void arg_register_batch(struct arg_option *opt, int count) 37 | { 38 | int i; 39 | 40 | for (i = 0; i < count; i++) 41 | arg_register(opt + i); 42 | } 43 | 44 | int arg_parse(int argc, char **argv) 45 | { 46 | struct option *glong; 47 | char *gshort; 48 | struct arg_option *opt; 49 | int glong_index, gshort_index, i; 50 | int res = 0; 51 | 52 | /* construct parameter lists for getopt */ 53 | glong = calloc(options.count + 1, sizeof(*glong)); 54 | gshort = calloc(options.count * 3 + 1, 1); 55 | if (!glong || !gshort) 56 | return 1; 57 | glong_index = gshort_index = 0; 58 | list_for_each(opt, options.list) { 59 | if (opt->long_name) { 60 | glong[glong_index].name = opt->long_name; 61 | glong[glong_index].has_arg = opt->has_arg; 62 | glong_index++; 63 | } 64 | if (opt->short_name) { 65 | gshort[gshort_index++] = opt->short_name; 66 | for (i = 0; i < opt->has_arg; i++) 67 | gshort[gshort_index++] = ':'; 68 | } 69 | } 70 | 71 | while (1) { 72 | gshort_index = getopt_long(argc, argv, gshort, glong, &glong_index); 73 | if (gshort_index < 0) 74 | break; 75 | if (gshort_index == '?' || gshort_index == ':') { 76 | res = 1; 77 | break; 78 | } 79 | /* find the respective arg_option */ 80 | if (!gshort_index) { 81 | i = 0; 82 | list_for_each(opt, options.list) { 83 | if (i == glong_index) 84 | break; 85 | i++; 86 | } 87 | } else { 88 | list_for_each(opt, options.list) 89 | if (opt->short_name == gshort_index) 90 | break; 91 | } 92 | assert(opt); 93 | 94 | if (optarg) { 95 | switch (opt->type) { 96 | case ARG_NONE: 97 | assert(0); 98 | break; 99 | case ARG_INT: 100 | *opt->action.int_var = atoi(optarg); 101 | break; 102 | case ARG_CHAR: 103 | *opt->action.char_var = strdup(optarg); 104 | break; 105 | case ARG_CALLBACK: 106 | /* will be handled below */ 107 | break; 108 | } 109 | } 110 | if (opt->type == ARG_CALLBACK) { 111 | res = opt->action.callback(optarg); 112 | if (res) 113 | break; 114 | } 115 | } 116 | 117 | free(gshort); 118 | free(glong); 119 | 120 | return res; 121 | } 122 | 123 | static int str_append(char *dest, const char *src, int size, int col) 124 | { 125 | int len, i; 126 | 127 | if (!size) 128 | return 0; 129 | len = strlen(dest); 130 | dest += len; 131 | i = 0; 132 | while (i < size - 1 && len + i < col) { 133 | *dest++ = ' '; 134 | i++; 135 | } 136 | while (i < size - 1 && *src) { 137 | *dest++ = *src++; 138 | i++; 139 | } 140 | *dest = '\0'; 141 | return i; 142 | } 143 | 144 | #define HELP_BUF_SIZE 512 145 | void arg_get_help(arg_help_handler_t handler) 146 | { 147 | struct arg_option *opt; 148 | char *buf; 149 | int size; 150 | 151 | buf = malloc(HELP_BUF_SIZE); 152 | if (!buf) 153 | return; 154 | list_for_each(opt, options.list) { 155 | *buf = '\0'; 156 | size = HELP_BUF_SIZE; 157 | if (opt->short_name) { 158 | buf[0] = '-'; 159 | buf[1] = opt->short_name; 160 | buf[2] = '\0'; 161 | size -= 2; 162 | if (opt->has_arg && !opt->long_name) { 163 | if (opt->has_arg == 1) 164 | size -= str_append(buf, " ARG", size, 0); 165 | else 166 | size -= str_append(buf, " [ARG]", size, 0); 167 | } 168 | } 169 | if (opt->long_name) { 170 | if (opt->short_name) 171 | size -= str_append(buf, ", ", size, 0); 172 | size -= str_append(buf, "--", size, 4); 173 | size -= str_append(buf, opt->long_name, 16, 0); 174 | if (opt->has_arg == 1) 175 | size -= str_append(buf, "=ARG", size, 0); 176 | else if (opt->has_arg == 2) 177 | size -= str_append(buf, "[=ARG]", size, 0); 178 | } 179 | size -= str_append(buf, opt->help, size, 26); 180 | handler(buf); 181 | } 182 | free(buf); 183 | } 184 | -------------------------------------------------------------------------------- /args.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _ARGS_H 17 | #define _ARGS_H 18 | 19 | #include "list.h" 20 | 21 | enum arg_type { 22 | ARG_NONE, 23 | ARG_INT, 24 | ARG_CHAR, 25 | ARG_CALLBACK, 26 | }; 27 | 28 | union arg_action { 29 | void *void_var; 30 | int *int_var; 31 | char **char_var; 32 | /* callback returns 0 in case of success, >0 in case of error */ 33 | int (*callback)(char *arg); 34 | }; 35 | 36 | struct arg_option { 37 | struct node n; 38 | const char *long_name; 39 | char short_name; 40 | int has_arg; /* see getopt(3) for possible values */ 41 | enum arg_type type; 42 | union arg_action action; 43 | const char *help; 44 | }; 45 | 46 | void arg_register(struct arg_option *opt); 47 | void arg_register_batch(struct arg_option *opt, int count); 48 | int arg_parse(int argc, char **argv); 49 | 50 | typedef void (*arg_help_handler_t)(const char *help); 51 | 52 | void arg_get_help(arg_help_handler_t handler); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _COMPAT_H 17 | #define _COMPAT_H 18 | 19 | #ifndef UNIX_PATH_MAX 20 | #define UNIX_PATH_MAX 108 21 | #endif 22 | 23 | #define IFLA_LINK_NETNSID 37 24 | #define IFLA_XDP 43 25 | 26 | #if IFLA_MAX < IFLA_XDP 27 | #undef IFLA_MAX 28 | #define IFLA_MAX IFLA_XDP 29 | #endif 30 | 31 | #define NETNSA_NSID 1 32 | #define NETNSA_FD 3 33 | 34 | #if NETNSA_MAX < NETNSA_FD 35 | #undef NETNSA_MAX 36 | #define NETNSA_MAX NETNSA_FD 37 | #endif 38 | 39 | #ifndef RTM_GETNSID 40 | #define RTM_GETNSID 90 41 | #endif 42 | 43 | #if RTM_MAX < RTM_GETNSID 44 | #undef RTM_MAX 45 | #define RTM_MAX (((RTM_GETNSID + 4) & ~3) - 1) 46 | #endif 47 | 48 | #define OVS_VPORT_FAMILY "ovs_vport" 49 | #define OVS_VPORT_CMD_GET 3 50 | #define OVS_VPORT_ATTR_NAME 3 51 | 52 | struct ovs_header { 53 | int dp_ifindex; 54 | }; 55 | 56 | #define IFLA_VXLAN_GROUP6 16 57 | #define IFLA_VXLAN_LOCAL6 17 58 | #define IFLA_VXLAN_COLLECT_METADATA 25 59 | 60 | #if IFLA_VXLAN_MAX < IFLA_VXLAN_COLLECT_METADATA 61 | #undef IFLA_VXLAN_MAX 62 | #define IFLA_VXLAN_MAX IFLA_VXLAN_COLLECT_METADATA 63 | #endif 64 | 65 | #ifndef IFLA_BOND_MAX 66 | enum { 67 | IFLA_BOND_UNSPEC, 68 | IFLA_BOND_MODE, 69 | IFLA_BOND_ACTIVE_SLAVE, 70 | IFLA_BOND_MIIMON, 71 | IFLA_BOND_UPDELAY, 72 | IFLA_BOND_DOWNDELAY, 73 | IFLA_BOND_USE_CARRIER, 74 | IFLA_BOND_ARP_INTERVAL, 75 | IFLA_BOND_ARP_IP_TARGET, 76 | IFLA_BOND_ARP_VALIDATE, 77 | IFLA_BOND_ARP_ALL_TARGETS, 78 | IFLA_BOND_PRIMARY, 79 | IFLA_BOND_PRIMARY_RESELECT, 80 | IFLA_BOND_FAIL_OVER_MAC, 81 | IFLA_BOND_XMIT_HASH_POLICY, 82 | IFLA_BOND_RESEND_IGMP, 83 | IFLA_BOND_NUM_PEER_NOTIF, 84 | IFLA_BOND_ALL_SLAVES_ACTIVE, 85 | IFLA_BOND_MIN_LINKS, 86 | IFLA_BOND_LP_INTERVAL, 87 | IFLA_BOND_PACKETS_PER_SLAVE, 88 | IFLA_BOND_AD_LACP_RATE, 89 | IFLA_BOND_AD_SELECT, 90 | IFLA_BOND_AD_INFO, 91 | __IFLA_BOND_MAX, 92 | }; 93 | #define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) 94 | #endif 95 | 96 | #ifndef IFLA_XDP_MAX 97 | enum { 98 | IFLA_XDP_UNSPEC, 99 | IFLA_XDP_FD, 100 | IFLA_XDP_ATTACHED, 101 | IFLA_XDP_FLAGS, 102 | IFLA_XDP_PROG_ID, 103 | IFLA_XDP_DRV_PROG_ID, 104 | IFLA_XDP_SKB_PROG_ID, 105 | IFLA_XDP_HW_PROG_ID, 106 | __IFLA_XDP_MAX, 107 | }; 108 | #define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) 109 | 110 | enum { 111 | XDP_ATTACHED_NONE = 0, 112 | XDP_ATTACHED_DRV, 113 | XDP_ATTACHED_SKB, 114 | XDP_ATTACHED_HW, 115 | XDP_ATTACHED_MULTI, 116 | }; 117 | #endif 118 | 119 | #ifndef IFLA_XFRM_MAX 120 | enum { 121 | IFLA_XFRM_UNSPEC, 122 | IFLA_XFRM_LINK, 123 | IFLA_XFRM_IF_ID, 124 | __IFLA_XFRM_MAX 125 | }; 126 | #define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) 127 | #endif 128 | 129 | #ifndef RTAX_QUICKACK 130 | #define RTAX_QUICKACK 15 131 | #endif 132 | 133 | #ifndef RTAX_CC_ALGO 134 | #define RTAX_CC_ALGO 16 135 | #endif 136 | 137 | #if RTAX_MAX < RTAX_CC_ALGO 138 | #undef RTAX_MAX 139 | #define RTAX_MAX RTAX_CC_ALGO 140 | #endif 141 | 142 | #ifndef RTPROT_MROUTED 143 | #define RTPROT_MROUTED 17 144 | #endif 145 | 146 | #ifndef RTPROT_BABEL 147 | #define RTPROT_BABEL 42 148 | #endif 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /ethtool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "ethtool.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | static int ethtool_ioctl(const char *ifname, void *data) 30 | { 31 | struct ifreq ifr; 32 | int fd, err = 0; 33 | 34 | fd = socket(AF_INET, SOCK_DGRAM, 0); 35 | if (fd < 0) 36 | return errno; 37 | memset(&ifr, 0, sizeof(ifr)); 38 | strcpy(ifr.ifr_name, ifname); 39 | ifr.ifr_data = data; 40 | if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) 41 | err = errno; 42 | close(fd); 43 | return err; 44 | } 45 | 46 | char *ethtool_driver(const char *ifname) 47 | { 48 | struct ethtool_drvinfo info; 49 | 50 | memset(&info, 0, sizeof(info)); 51 | info.cmd = ETHTOOL_GDRVINFO; 52 | if (ethtool_ioctl(ifname, &info)) 53 | return NULL; 54 | return strdup(info.driver); 55 | } 56 | 57 | unsigned int ethtool_veth_peer(const char *ifname) 58 | { 59 | struct ethtool_drvinfo info; 60 | struct ethtool_gstrings *strs; 61 | struct ethtool_stats *stats; 62 | unsigned int i, res = 0; 63 | 64 | memset(&info, 0, sizeof(info)); 65 | info.cmd = ETHTOOL_GDRVINFO; 66 | if (ethtool_ioctl(ifname, &info)) 67 | return 0; 68 | if (!info.n_stats) 69 | return 0; 70 | 71 | strs = malloc(sizeof(struct ethtool_gstrings) + info.n_stats * ETH_GSTRING_LEN); 72 | if (!strs) 73 | return 0; 74 | memset(strs, 0, sizeof(struct ethtool_gstrings)); 75 | strs->cmd = ETHTOOL_GSTRINGS; 76 | strs->string_set = ETH_SS_STATS; 77 | strs->len = info.n_stats; 78 | if (ethtool_ioctl(ifname, strs)) 79 | goto fail_strs; 80 | if (strs->len != info.n_stats) 81 | goto fail_strs; 82 | 83 | stats = malloc(sizeof(struct ethtool_stats) + info.n_stats * sizeof(__u64)); 84 | if (!stats) 85 | goto fail_strs; 86 | memset(stats, 0, sizeof(struct ethtool_stats)); 87 | stats->cmd = ETHTOOL_GSTATS; 88 | stats->n_stats = info.n_stats; 89 | if (ethtool_ioctl(ifname, stats)) 90 | goto fail_stats; 91 | if (stats->n_stats != info.n_stats) 92 | goto fail_stats; 93 | 94 | for (i = 0; i < info.n_stats; i++) { 95 | if (!strcmp((char *)strs->data + i * ETH_GSTRING_LEN, "peer_ifindex")) { 96 | res = stats->data[i]; 97 | break; 98 | } 99 | } 100 | 101 | fail_stats: 102 | free(stats); 103 | fail_strs: 104 | free(strs); 105 | return res; 106 | } 107 | -------------------------------------------------------------------------------- /ethtool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _ETHTOOL_H 17 | #define _ETHTOOL_H 18 | 19 | char *ethtool_driver(const char *ifname); 20 | unsigned int ethtool_veth_peer(const char *ifname); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /frontend.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "frontend.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "args.h" 22 | #include "if.h" 23 | #include "utils.h" 24 | 25 | static struct output_entry default_output = { 26 | .format = "dot", 27 | .file = NULL, 28 | .print_mask = -1U, 29 | }; 30 | 31 | static int used_default_format = 0; 32 | 33 | static DECLARE_LIST(outputs); 34 | static DECLARE_LIST(frontends); 35 | 36 | static int add_output(char *format) 37 | { 38 | struct output_entry *out; 39 | 40 | out = malloc(sizeof(struct output_entry)); 41 | if (!out) 42 | return ENOMEM; 43 | 44 | *out = default_output; 45 | 46 | out->format = strdup(format); 47 | if (!out->format) { 48 | free(out); 49 | return ENOMEM; 50 | } 51 | 52 | list_prepend(&outputs, node(out)); 53 | 54 | return 0; 55 | } 56 | 57 | static int add_format(char *arg) 58 | { 59 | if (used_default_format) { 60 | fprintf(stderr, "Failed to parse arguments: output options must come after --format.\n"); 61 | return EINVAL; 62 | } 63 | 64 | return add_output(arg); 65 | } 66 | 67 | static int require_format() 68 | { 69 | int err; 70 | 71 | if (list_empty(outputs)) { 72 | if ((err = add_output(default_output.format))) 73 | return err; 74 | used_default_format = 1; 75 | } 76 | 77 | return 0; 78 | } 79 | 80 | static int set_output(char *arg) 81 | { 82 | int err; 83 | struct output_entry *head; 84 | 85 | if ((err = require_format())) 86 | return err; 87 | 88 | head = list_head(outputs); 89 | /* Multiple --outputs for one --format: clone last output */ 90 | if (head->file && (err = add_output(head->format))) 91 | return err; 92 | 93 | head = list_head(outputs); 94 | head->file = strdup(arg); 95 | if (!head->file) 96 | return ENOMEM; 97 | 98 | return 0; 99 | } 100 | 101 | static int set_nostate() 102 | { 103 | int err; 104 | struct output_entry *head; 105 | 106 | if ((err = require_format())) 107 | return err; 108 | 109 | head = list_head(outputs); 110 | head->print_mask &= ~IF_PROP_STATE; 111 | return 0; 112 | } 113 | 114 | static int print_formats(_unused char *arg) 115 | { 116 | struct frontend *f; 117 | 118 | printf("Available output formats:\n"); 119 | list_for_each(f, frontends) 120 | printf("\t%s - %s\n", f->format, f->desc); 121 | return 1; 122 | } 123 | 124 | static struct arg_option options[] = { 125 | { .long_name = "format", .short_name = 'f', .has_arg = 1, 126 | .type = ARG_CALLBACK, .action.callback = add_format, 127 | .help = "output format (default: dot)", 128 | }, 129 | { .long_name = "output", .short_name = 'o', .has_arg = 1, 130 | .type = ARG_CALLBACK, .action.callback = set_output, 131 | .help = "output file (default: standart output)", 132 | }, 133 | { .long_name = "config-only", .short_name = 'C', .has_arg = 0, 134 | .type = ARG_CALLBACK, .action.callback = set_nostate, 135 | .help = "skip state in output, print only configuration", 136 | }, 137 | { .long_name = "list-formats", .short_name = 'F', 138 | .type = ARG_CALLBACK, .action.callback = print_formats, 139 | .help = "list available output formats", 140 | }, 141 | }; 142 | 143 | void frontend_init(void) 144 | { 145 | arg_register_batch(options, ARRAY_SIZE(options)); 146 | } 147 | 148 | void frontend_register(struct frontend *f) 149 | { 150 | list_append(&frontends, node(f)); 151 | } 152 | 153 | int frontend_output(struct list *netns_list) 154 | { 155 | struct frontend *f; 156 | struct output_entry *out; 157 | FILE *file; 158 | 159 | require_format(); 160 | 161 | list_for_each(out, outputs) { 162 | list_for_each(f, frontends) { 163 | if (!strcmp(f->format, out->format)) { 164 | out->frontend = f; 165 | break; 166 | } 167 | } 168 | if (!out->frontend) 169 | return EINVAL; 170 | } 171 | 172 | list_for_each(out, outputs) { 173 | if (out->file && strcmp("-", out->file)) { 174 | file = fopen(out->file, "w"); 175 | if (!file) 176 | return errno; 177 | } else 178 | file = stdout; 179 | 180 | out->frontend->output(file, netns_list, out); 181 | 182 | if (file != stdout && fclose(file)) 183 | return errno; 184 | } 185 | 186 | return 0; 187 | } 188 | 189 | void frontend_cleanup() 190 | { 191 | struct output_entry *out; 192 | 193 | while ((out = list_pop(&outputs))) { 194 | free(out->format); 195 | free(out->file); 196 | free(out); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /frontend.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _FRONTEND_H 17 | #define _FRONTEND_H 18 | 19 | #include 20 | #include "list.h" 21 | 22 | struct netns_entry; 23 | 24 | struct output_entry { 25 | struct node n; 26 | char *format, *file; 27 | struct frontend *frontend; 28 | unsigned int print_mask; 29 | }; 30 | 31 | struct frontend { 32 | struct node n; 33 | const char *format; 34 | const char *desc; 35 | void (*output)(FILE *file, struct list *netns_list, struct output_entry *output_entry); 36 | }; 37 | 38 | void frontend_init(void); 39 | void frontend_register(struct frontend *f); 40 | int frontend_output(struct list *netns_list); 41 | void frontend_cleanup(void); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /frontends/dot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "dot.h" 17 | #include 18 | #include 19 | #include 20 | #include "../addr.h" 21 | #include "../frontend.h" 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../netns.h" 25 | #include "../utils.h" 26 | #include "../version.h" 27 | 28 | static void output_label(FILE *f, struct list *labels) 29 | { 30 | struct label *ptr; 31 | 32 | list_for_each(ptr, *labels) 33 | fprintf(f, "\\n%s", ptr->text); 34 | } 35 | 36 | static void output_label_properties(FILE *f, struct list *properties, unsigned int prop_mask) 37 | { 38 | struct label_property *ptr; 39 | 40 | list_for_each(ptr, *properties) 41 | if (label_prop_match_mask(ptr->type, prop_mask)) 42 | fprintf(f, "\\n%s: %s", ptr->key, ptr->value); 43 | } 44 | 45 | static void output_addresses(FILE *f, struct list *addresses) 46 | { 47 | struct if_addr *addr; 48 | 49 | list_for_each(addr, *addresses) { 50 | fprintf(f, "\\n%s", addr->addr.formatted); 51 | if (addr->peer.formatted) 52 | fprintf(f, " peer %s", addr->peer.formatted); 53 | } 54 | } 55 | 56 | static void output_mtu(FILE *f, struct if_entry *ptr) 57 | { 58 | if (ptr->mtu && ptr->mtu != 1500 && !(ptr->flags & IF_LOOPBACK)) 59 | fprintf(f, "\\nMTU %d", ptr->mtu); 60 | } 61 | 62 | static void output_xdp(FILE *f, struct list *xdp_list) 63 | { 64 | struct if_xdp *xdp; 65 | 66 | list_for_each(xdp, *xdp_list) 67 | fprintf(f, "\\nxdp-%s (id %u)", xdp->mode, xdp->prog_id); 68 | } 69 | 70 | static int output_ifaces_pass1(FILE *f, struct list *list, unsigned int prop_mask) 71 | { 72 | int need_init_net = 0; 73 | struct if_entry *ptr; 74 | 75 | list_for_each(ptr, *list) { 76 | fprintf(f, "\"%s\" [label=\"%s", ifid(ptr), ptr->if_name); 77 | if (ptr->driver) 78 | fprintf(f, " (%s)", ifdrv(ptr)); 79 | output_label_properties(f, &ptr->properties, prop_mask); 80 | output_mtu(f, ptr); 81 | output_xdp(f, &ptr->xdp); 82 | if (label_prop_match_mask(IF_PROP_CONFIG, prop_mask)) { 83 | output_addresses(f, &ptr->addr); 84 | if ((ptr->flags & IF_LOOPBACK) == 0 && ptr->mac_addr.formatted) 85 | fprintf(f, "\\nmac %s", ptr->mac_addr.formatted); 86 | } 87 | fprintf(f, "\""); 88 | 89 | if (label_prop_match_mask(IF_PROP_STATE, prop_mask)) { 90 | if (ptr->flags & IF_INTERNAL) 91 | fprintf(f, ",style=dotted"); 92 | else if (!(ptr->flags & IF_UP)) 93 | fprintf(f, ",style=filled,fillcolor=\"grey\""); 94 | else if (!(ptr->flags & IF_HAS_LINK)) 95 | fprintf(f, ",style=filled,fillcolor=\"pink\""); 96 | else 97 | fprintf(f, ",style=filled,fillcolor=\"darkolivegreen1\""); 98 | } 99 | if (ptr->warnings) 100 | fprintf(f, ",color=\"red\""); 101 | fprintf(f, "]\n"); 102 | 103 | if (ptr->link_net && !ptr->link_net->name) 104 | need_init_net = 1; 105 | } 106 | 107 | return need_init_net; 108 | } 109 | 110 | static void output_ifaces_pass2(FILE *f, struct list *list) 111 | { 112 | struct if_entry *ptr; 113 | 114 | list_for_each(ptr, *list) { 115 | if (ptr->master) { 116 | fprintf(f, "\"%s\" -> ", ifid(ptr)); 117 | fprintf(f, "\"%s\" [style=%s", ifid(ptr->master), 118 | ptr->flags & IF_PASSIVE_SLAVE ? "dashed" : "solid"); 119 | if (ptr->edge_label && !ptr->link) 120 | fprintf(f, ",label=\"%s\"", ptr->edge_label); 121 | fprintf(f, "]\n"); 122 | } 123 | if (ptr->physfn) { 124 | fprintf(f, "\"%s\" -> ", ifid(ptr)); 125 | fprintf(f, "\"%s\" [style=dotted,taillabel=\"PF\"]\n", ifid(ptr->physfn)); 126 | } 127 | if (ptr->link) { 128 | fprintf(f, "\"%s\" -> ", ifid(ptr->link)); 129 | fprintf(f, "\"%s\" [style=%s", ifid(ptr), 130 | ptr->flags & IF_LINK_WEAK ? "dashed" : "solid"); 131 | if (ptr->edge_label) 132 | fprintf(f, ",label=\"%s\"", ptr->edge_label); 133 | fprintf(f, "]\n"); 134 | } else if (ptr->link_net) { 135 | if (ptr->link_net->name) { 136 | /* hack: we can't create an arrow directly 137 | * to the cluster, only to a node. lhead 138 | * modifies the arrow so that it actually 139 | * points to the cluster. 140 | */ 141 | fprintf(f, "\"%s\" -> ", ifid(ptr)); 142 | fprintf(f, "\"%s/lo\" ", nsid(ptr->link_net)); 143 | fprintf(f, "[lhead=\"cluster/%s\", style=\"dashed\"]\n", nsid(ptr->link_net)); 144 | } else 145 | fprintf(f, "\"%s\" -> \"cluster//\" [style=\"dashed\"]\n", ifid(ptr)); 146 | } 147 | if (ptr->peer && (size_t) ptr > (size_t) ptr->peer) { 148 | fprintf(f, "\"%s\" -> ", ifid(ptr)); 149 | fprintf(f, "\"%s\" [dir=none]\n", ifid(ptr->peer)); 150 | } 151 | } 152 | } 153 | 154 | static void output_warnings(FILE *f, struct list *netns_list) 155 | { 156 | struct netns_entry *ns; 157 | int was_label = 0; 158 | 159 | list_for_each(ns, *netns_list) { 160 | if (!list_empty(ns->warnings)) { 161 | if (!was_label) 162 | fprintf(f, "label=\""); 163 | was_label = 1; 164 | output_label(f, &ns->warnings); 165 | } 166 | } 167 | if (was_label) { 168 | fprintf(f, "\"\n"); 169 | fprintf(f, "fontcolor=\"red\"\n"); 170 | } 171 | } 172 | 173 | static void dot_output(FILE *f, struct list *netns_list, struct output_entry *output_entry) 174 | { 175 | struct netns_entry *ns; 176 | int need_init_net = 0; 177 | time_t cur; 178 | 179 | time(&cur); 180 | fprintf(f, "// generated by plotnetcfg " VERSION " on %s", ctime(&cur)); 181 | fprintf(f, "digraph {\ncompound=true;\nnode [shape=box]\n"); 182 | list_for_each(ns, *netns_list) { 183 | if (ns->name) { 184 | fprintf(f, "subgraph \"cluster/%s\" {\n", nsid(ns)); 185 | fprintf(f, "label=\"%s\"\n", ns->name); 186 | fprintf(f, "fontcolor=\"black\"\n"); 187 | } 188 | need_init_net += output_ifaces_pass1(f, &ns->ifaces, output_entry->print_mask); 189 | if (ns->name) 190 | fprintf(f, "}\n"); 191 | } 192 | 193 | /* output a fake cluster to which tunnel interfaces can point */ 194 | if (need_init_net) 195 | fprintf(f, "\"cluster//\"[label=\"root netns\",penwidth=0];\n"); 196 | 197 | list_for_each(ns, *netns_list) 198 | output_ifaces_pass2(f, &ns->ifaces); 199 | output_warnings(f, netns_list); 200 | fprintf(f, "}\n"); 201 | } 202 | 203 | static struct frontend fe_dot = { 204 | .format = "dot", 205 | .desc = "dot language (suitable for graphviz)", 206 | .output = dot_output, 207 | }; 208 | 209 | void frontend_dot_register(void) 210 | { 211 | frontend_register(&fe_dot); 212 | } 213 | -------------------------------------------------------------------------------- /frontends/dot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _FRONTENDS_DOT_H 17 | #define _FRONTENDS_DOT_H 18 | 19 | void frontend_dot_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /frontends/json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "json.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "../addr.h" 23 | #include "../frontend.h" 24 | #include "../if.h" 25 | #include "../label.h" 26 | #include "../netns.h" 27 | #include "../route.h" 28 | #include "../utils.h" 29 | #include "../version.h" 30 | 31 | static json_t *label_to_array(struct list *labels) 32 | { 33 | json_t *arr; 34 | struct label *entry; 35 | 36 | arr = json_array(); 37 | list_for_each(entry, *labels) 38 | json_array_append_new(arr, json_string(entry->text)); 39 | return arr; 40 | } 41 | 42 | static json_t *label_properties_to_object(struct list *properties, unsigned int prop_mask) 43 | { 44 | json_t *jobj; 45 | struct label_property *prop; 46 | 47 | jobj = json_object(); 48 | list_for_each(prop, *properties) 49 | if (label_prop_match_mask(prop->type, prop_mask)) 50 | json_object_set_new(jobj, prop->key, json_string(prop->value)); 51 | return jobj; 52 | } 53 | 54 | static json_t *address_family(int family) 55 | { 56 | switch (family) { 57 | case AF_INET: return json_string("INET"); 58 | case AF_INET6: return json_string("INET6"); 59 | } 60 | /* should not happen */ 61 | return json_string("unknown"); 62 | } 63 | 64 | static json_t *address_to_obj(struct addr *addr) 65 | { 66 | json_t *obj = json_object(); 67 | json_object_set_new(obj, "family", address_family(addr->family)); 68 | json_object_set_new(obj, "address", json_string(addr->formatted)); 69 | return obj; 70 | } 71 | 72 | static json_t *addresses_to_array(struct list *addresses) 73 | { 74 | json_t *arr, *addr; 75 | struct if_addr *entry; 76 | 77 | arr = json_array(); 78 | list_for_each(entry, *addresses) { 79 | addr = address_to_obj(&entry->addr); 80 | if (entry->peer.formatted) 81 | json_object_set_new(addr, "peer", address_to_obj(&entry->peer)); 82 | json_array_append_new(arr, addr); 83 | } 84 | return arr; 85 | } 86 | 87 | static json_t *xdp_to_array(struct list *xdp_list) 88 | { 89 | json_t *arr, *obj; 90 | struct if_xdp *entry; 91 | 92 | arr = json_array(); 93 | list_for_each(entry, *xdp_list) { 94 | obj = json_object(); 95 | json_object_set_new(obj, "mode", json_string(entry->mode)); 96 | json_object_set_new(obj, "prog_id", json_integer(entry->prog_id)); 97 | json_array_append_new(arr, obj); 98 | } 99 | return arr; 100 | } 101 | 102 | static json_t *connection(struct if_entry *target, char *edge_label) 103 | { 104 | json_t *obj, *arr; 105 | 106 | obj = json_object(); 107 | json_object_set_new(obj, "target", json_string(ifid(target))); 108 | 109 | arr = json_array(); 110 | if (edge_label) 111 | json_array_append_new(arr, json_string(edge_label)); 112 | json_object_set_new(obj, "info", arr); 113 | return obj; 114 | } 115 | 116 | static json_t *link_netns(struct netns_entry *target) 117 | { 118 | json_t *obj, *arr; 119 | 120 | obj = json_object(); 121 | json_object_set_new(obj, "target", json_string(nsid(target))); 122 | 123 | arr = json_array(); 124 | json_object_set_new(obj, "info", arr); 125 | return obj; 126 | } 127 | 128 | static json_t *interfaces_to_array(struct list *list, struct output_entry *output_entry) 129 | { 130 | struct if_entry *entry, *link, *slave; 131 | json_t *ifarr, *ifobj, *children, *parents, *jconn; 132 | char *s; 133 | 134 | ifarr = json_object(); 135 | list_for_each(entry, *list) { 136 | ifobj = json_object(); 137 | json_object_set_new(ifobj, "id", json_string(ifid(entry))); 138 | json_object_set_new(ifobj, "namespace", json_string(nsid(entry->ns))); 139 | json_object_set_new(ifobj, "name", json_string(entry->if_name)); 140 | json_object_set_new(ifobj, "driver", json_string(ifdrv(entry))); 141 | json_object_set_new(ifobj, "info", label_properties_to_object(&entry->properties, output_entry->print_mask)); 142 | if (label_prop_match_mask(IF_PROP_CONFIG, output_entry->print_mask)) { 143 | json_object_set_new(ifobj, "addresses", addresses_to_array(&entry->addr)); 144 | json_object_set_new(ifobj, "mtu", json_integer(entry->mtu)); 145 | if ((entry->flags & IF_LOOPBACK) == 0 && entry->mac_addr.formatted) 146 | json_object_set_new(ifobj, "mac", json_string(entry->mac_addr.formatted)); 147 | } 148 | if (!entry->link_index && entry->link_net) 149 | json_object_set_new(ifobj, "link-netns", link_netns(entry->link_net)); 150 | json_object_set_new(ifobj, "type", json_string(entry->flags & IF_INTERNAL ? 151 | "internal" : 152 | "device")); 153 | if (entry->flags & IF_INTERNAL) 154 | s = "none"; 155 | else if (!(entry->flags & IF_UP)) 156 | s = "down"; 157 | else if (!(entry->flags & IF_HAS_LINK)) 158 | s = "up_no_link"; 159 | else 160 | s = "up"; 161 | if (label_prop_match_mask(IF_PROP_STATE, output_entry->print_mask)) 162 | json_object_set_new(ifobj, "state", json_string(s)); 163 | if (!list_empty(entry->xdp)) 164 | json_object_set_new(ifobj, "xdp", xdp_to_array(&entry->xdp)); 165 | if (entry->warnings) 166 | json_object_set_new(ifobj, "warning", json_true()); 167 | 168 | parents = json_object(); 169 | if (entry->master) { 170 | jconn = connection(entry->master, 171 | entry->link ? NULL : entry->edge_label); 172 | json_object_set_new(parents, ifid(entry->master), jconn); 173 | } else 174 | list_for_each_member(link, entry->rev_link, rev_link_node) { 175 | jconn = connection(link, link->edge_label); 176 | json_object_set_new(parents, ifid(link), jconn); 177 | } 178 | if (json_object_size(parents)) 179 | json_object_set(ifobj, "parents", parents); 180 | json_decref(parents); 181 | 182 | children = json_object(); 183 | if (entry->link) { 184 | jconn = connection(entry->link, entry->edge_label); 185 | json_object_set_new(children, ifid(entry->link), jconn); 186 | } else 187 | list_for_each_member(slave, entry->rev_master, rev_master_node) { 188 | jconn = connection(slave, slave->link ? NULL : slave->edge_label); 189 | json_object_set_new(children, ifid(slave), jconn); 190 | } 191 | if (json_object_size(children)) 192 | json_object_set(ifobj, "children", children); 193 | json_decref(children); 194 | 195 | if (entry->peer) 196 | json_object_set(ifobj, "peer", connection(entry->peer, NULL)); 197 | 198 | json_object_set_new(ifarr, ifid(entry), ifobj); 199 | } 200 | return ifarr; 201 | } 202 | 203 | static json_t *routes_to_array(struct list *routes) 204 | { 205 | json_t *ifarr, *ifobj; 206 | struct rtmetric *rtm; 207 | struct route *rte; 208 | 209 | ifarr = json_array(); 210 | list_for_each(rte, *routes) { 211 | ifobj = json_object(); 212 | if (rte->dst.family) 213 | json_object_set_new(ifobj, "destination", json_string(rte->dst.formatted)); 214 | json_object_set_new(ifobj, "family", address_family(rte->family)); 215 | if (rte->gw.family) 216 | json_object_set_new(ifobj, "gateway", json_string(rte->gw.formatted)); 217 | if (rte->iif) 218 | json_object_set_new(ifobj, "iif", json_string(ifid(rte->iif))); 219 | if (!list_empty(rte->metrics)) { 220 | json_t *rtmetrics = json_object(); 221 | 222 | list_for_each(rtm, rte->metrics) 223 | json_object_set_new(rtmetrics, route_metric(rtm->type), json_integer(rtm->value)); 224 | 225 | json_object_set_new(ifobj, "metrics", rtmetrics); 226 | } 227 | if (rte->oif) 228 | json_object_set_new(ifobj, "oif", json_string(ifid(rte->oif))); 229 | json_object_set_new(ifobj, "priority", json_integer(rte->priority)); 230 | json_object_set_new(ifobj, "protocol", json_string(route_protocol(rte->protocol))); 231 | json_object_set_new(ifobj, "scope", json_string(route_scope(rte->scope))); 232 | if (rte->src.family) 233 | json_object_set_new(ifobj, "source", json_string(rte->src.formatted)); 234 | if (rte->prefsrc.family) 235 | json_object_set_new(ifobj, "preferred-source", json_string(rte->prefsrc.formatted)); 236 | json_object_set_new(ifobj, "tos", json_integer(rte->tos)); 237 | json_object_set_new(ifobj, "type", json_string(route_type(rte->type))); 238 | 239 | json_array_append_new(ifarr, ifobj); 240 | } 241 | 242 | return ifarr; 243 | } 244 | 245 | static json_t *rtables_to_array(struct list *tables) 246 | { 247 | struct rtable *rt; 248 | json_t *ifarr, *ifobj; 249 | 250 | ifarr = json_object(); 251 | list_for_each(rt, *tables) { 252 | ifobj = json_object(); 253 | json_object_set_new(ifobj, "name", json_string(route_table(rt->id))); 254 | json_object_set_new(ifobj, "routes", routes_to_array(&rt->routes)); 255 | json_object_set_new(ifarr, rtid(rt), ifobj); 256 | } 257 | 258 | return ifarr; 259 | } 260 | 261 | static void json_output(FILE *f, struct list *netns_list, struct output_entry *output_entry) 262 | { 263 | struct netns_entry *entry, *root; 264 | json_t *output, *ns_list, *ns; 265 | time_t cur; 266 | 267 | root = list_head(*netns_list); 268 | 269 | time(&cur); 270 | output = json_object(); 271 | json_object_set_new(output, "format", json_integer(2)); 272 | json_object_set_new(output, "version", json_string(VERSION)); 273 | json_object_set_new(output, "date", json_string(ctime(&cur))); 274 | json_object_set_new(output, "root", json_string(nsid(root))); 275 | ns_list = json_object(); 276 | list_for_each(entry, *netns_list) { 277 | ns = json_object(); 278 | json_object_set_new(ns, "id", json_string(nsid(entry))); 279 | json_object_set_new(ns, "name", json_string(entry->name ? entry->name : "")); 280 | json_object_set_new(ns, "interfaces", interfaces_to_array(&entry->ifaces, output_entry)); 281 | json_object_set_new(ns, "routes", rtables_to_array(&entry->rtables)); 282 | if (!list_empty(entry->warnings)) 283 | json_object_set_new(ns, "warnings", label_to_array(&entry->warnings)); 284 | json_object_set_new(ns_list, nsid(entry), ns); 285 | } 286 | json_object_set_new(output, "namespaces", ns_list); 287 | json_dumpf(output, f, JSON_SORT_KEYS | JSON_COMPACT); 288 | json_decref(output); 289 | } 290 | 291 | static struct frontend fe_json = { 292 | .format = "json", 293 | .desc = "JSON, see plotnetcfg-json(5)", 294 | .output = json_output, 295 | }; 296 | 297 | void frontend_json_register(void) 298 | { 299 | frontend_register(&fe_json); 300 | } 301 | -------------------------------------------------------------------------------- /frontends/json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _FRONTENDS_JSON_H 17 | #define _FRONTENDS_JSON_H 18 | 19 | void frontend_json_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handler.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "handler.h" 17 | #include 18 | #include 19 | #include 20 | #include "if.h" 21 | #include "netns.h" 22 | 23 | 24 | static DECLARE_LIST(if_handlers); 25 | static DECLARE_LIST(netns_handlers); 26 | static DECLARE_LIST(global_handlers); 27 | 28 | void if_handler_register(struct if_handler *h) 29 | { 30 | list_append(&if_handlers, node(h)); 31 | } 32 | 33 | void netns_handler_register(struct netns_handler *h) 34 | { 35 | list_append(&netns_handlers, node(h)); 36 | } 37 | 38 | void global_handler_register(struct global_handler *h) 39 | { 40 | list_append(&global_handlers, node(h)); 41 | } 42 | 43 | static int driver_match(struct if_handler *h, struct if_entry *e) 44 | { 45 | return !h->driver || (e->driver && !strcmp(h->driver, e->driver)); 46 | } 47 | 48 | #define handler_callback(handler, callback, ...) \ 49 | ((handler)->callback ? (handler)->callback(__VA_ARGS__) : 0) 50 | 51 | #define if_handler_callback(handler, callback, entry, ...) \ 52 | (driver_match(handler, entry) ? handler_callback(handler, callback, entry, ##__VA_ARGS__) : 0) 53 | 54 | int if_handler_init(struct if_entry *entry) 55 | { 56 | struct if_handler *h; 57 | 58 | list_for_each(h, if_handlers) { 59 | if (!h->driver || strcmp(h->driver, entry->driver)) 60 | continue; 61 | 62 | if (h->private_size) { 63 | entry->handler_private = calloc(1, h->private_size); 64 | if (!entry->handler_private) 65 | return ENOMEM; 66 | } 67 | 68 | break; 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | int if_handler_netlink(struct if_entry *entry, struct nlattr **linkinfo) 75 | { 76 | struct if_handler *h; 77 | int err; 78 | 79 | list_for_each(h, if_handlers) 80 | if ((err = if_handler_callback(h, netlink, entry, linkinfo))) 81 | return err; 82 | 83 | return 0; 84 | } 85 | 86 | int if_handler_scan(struct if_entry *entry) 87 | { 88 | struct if_handler *h; 89 | int err; 90 | 91 | list_for_each(h, if_handlers) 92 | if ((err = if_handler_callback(h, scan, entry))) 93 | return err; 94 | 95 | return 0; 96 | } 97 | 98 | int if_handler_post(struct list *netns_list) 99 | { 100 | struct netns_entry *ns; 101 | struct if_entry *entry; 102 | struct if_handler *h; 103 | int err; 104 | 105 | list_for_each(ns, *netns_list) 106 | list_for_each(entry, ns->ifaces) 107 | list_for_each(h, if_handlers) 108 | if ((err = if_handler_callback(h, post, entry, netns_list))) 109 | return err; 110 | 111 | return 0; 112 | } 113 | 114 | void if_handler_cleanup(struct if_entry *entry) 115 | { 116 | struct if_handler *h; 117 | 118 | list_for_each(h, if_handlers) 119 | if_handler_callback(h, cleanup, entry); 120 | 121 | if (entry->handler_private) 122 | free(entry->handler_private); 123 | } 124 | 125 | int netns_handler_scan(struct netns_entry *entry) 126 | { 127 | struct netns_handler *h; 128 | int err; 129 | 130 | list_for_each(h, netns_handlers) 131 | if ((err = handler_callback(h, scan, entry))) 132 | return err; 133 | 134 | return 0; 135 | } 136 | 137 | void netns_handler_cleanup(struct netns_entry *entry) 138 | { 139 | struct netns_handler *h; 140 | 141 | list_for_each(h, netns_handlers) 142 | handler_callback(h, cleanup, entry); 143 | } 144 | 145 | int global_handler_init(void) 146 | { 147 | struct global_handler *h; 148 | int err; 149 | 150 | list_for_each(h, global_handlers) 151 | if ((err = handler_callback(h, init))) 152 | return err; 153 | 154 | return 0; 155 | } 156 | 157 | int global_handler_post(struct list *netns_list) 158 | { 159 | struct global_handler *h; 160 | int err; 161 | 162 | list_for_each(h, global_handlers) 163 | if ((err = handler_callback(h, post, netns_list))) 164 | return err; 165 | 166 | return 0; 167 | } 168 | 169 | void global_handler_cleanup(struct list *netns_list) 170 | { 171 | struct global_handler *h; 172 | 173 | list_for_each(h, global_handlers) 174 | handler_callback(h, cleanup, netns_list); 175 | } 176 | -------------------------------------------------------------------------------- /handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLER_H 17 | #define _HANDLER_H 18 | 19 | #include 20 | #include "list.h" 21 | 22 | struct if_entry; 23 | struct netns_entry; 24 | struct nlattr; 25 | 26 | /* Only one handler for each driver allowed. 27 | * Generic handlers called for every interface are supported and are created 28 | * by setting driver to NULL. Generic handlers are not allowed to use 29 | * handler_private field in struct if_entry. 30 | * 31 | * If you want to use handler_private, private_size bytes will be allocated 32 | * before any callback is called. 33 | * 34 | * Callbacks are called in this order: 35 | * 1. netlink - while reading interface data from netlink 36 | * 2. scan - while scanning interfaces, sysfs is mounted 37 | * 3. post - all interfaces are scanned, use this for inter-interface 38 | * scanning 39 | * 4. cleanup - destroy private structure. handler_private itself will be 40 | * freed automatically. 41 | */ 42 | struct if_handler { 43 | struct node n; 44 | const char *driver; 45 | size_t private_size; 46 | int (*netlink)(struct if_entry *entry, struct nlattr **linkinfo); 47 | int (*scan)(struct if_entry *entry); 48 | int (*post)(struct if_entry *entry, struct list *netns_list); 49 | void (*cleanup)(struct if_entry *entry); 50 | }; 51 | 52 | void if_handler_register(struct if_handler *h); 53 | int if_handler_init(struct if_entry *entry); 54 | int if_handler_netlink(struct if_entry *entry, struct nlattr **linkinfo); 55 | int if_handler_scan(struct if_entry *entry); 56 | int if_handler_post(struct list *netns_list); 57 | void if_handler_cleanup(struct if_entry *entry); 58 | 59 | struct netns_handler { 60 | struct node n; 61 | int (*scan)(struct netns_entry *entry); 62 | void (*cleanup)(struct netns_entry *entry); 63 | }; 64 | 65 | void netns_handler_register(struct netns_handler *h); 66 | int netns_handler_scan(struct netns_entry *entry); 67 | void netns_handler_cleanup(struct netns_entry *entry); 68 | 69 | struct global_handler { 70 | struct node n; 71 | int (*init)(void); 72 | int (*post)(struct list *netns_list); 73 | void (*cleanup)(struct list *netns_list); 74 | }; 75 | 76 | void global_handler_register(struct global_handler *h); 77 | int global_handler_init(void); 78 | int global_handler_post(struct list *netns_list); 79 | void global_handler_cleanup(struct list *netns_list); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /handlers/bond.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "bond.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "../handler.h" 24 | #include "../if.h" 25 | #include "../netlink.h" 26 | #include "../sysfs.h" 27 | #include "../utils.h" 28 | 29 | #include "../compat.h" 30 | 31 | static const char *bond_mode_name[] = { 32 | "", 33 | "balance-rr", 34 | "active-backup", 35 | "balance-xor", 36 | "broadcast", 37 | "802.3ad", 38 | "balance-tlb", 39 | "balance-alb", 40 | }; 41 | 42 | struct bond_private { 43 | uint8_t mode; 44 | unsigned int active_slave_index; 45 | char *active_slave_name; 46 | }; 47 | 48 | static int bond_netlink(struct if_entry *entry, struct nlattr **linkinfo); 49 | static int bond_scan(struct if_entry *entry); 50 | static int bond_post(struct if_entry *entry, struct list *netns_list); 51 | static void bond_cleanup(struct if_entry *entry); 52 | 53 | static struct if_handler h_bond = { 54 | .driver = "bonding", 55 | .private_size = sizeof(struct bond_private), 56 | .netlink = bond_netlink, 57 | .scan = bond_scan, 58 | .post = bond_post, 59 | .cleanup = bond_cleanup, 60 | }; 61 | 62 | void handler_bond_register(void) 63 | { 64 | if_handler_register(&h_bond); 65 | } 66 | 67 | static int bond_netlink(struct if_entry *entry, struct nlattr **linkinfo) 68 | { 69 | struct bond_private *priv = entry->handler_private; 70 | struct nlattr **bondinfo; 71 | 72 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 73 | return ENOENT; 74 | bondinfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_BOND_MAX); 75 | if (!bondinfo) 76 | return ENOMEM; 77 | 78 | if (bondinfo[IFLA_BOND_MODE]) { 79 | priv->mode = nla_read_u8(bondinfo[IFLA_BOND_MODE]) + 1; 80 | if (priv->mode >= ARRAY_SIZE(bond_mode_name)) 81 | priv->mode = 0; 82 | } 83 | 84 | if (bondinfo[IFLA_BOND_ACTIVE_SLAVE]) { 85 | priv->active_slave_index = nla_read_u32(bondinfo[IFLA_BOND_ACTIVE_SLAVE]); 86 | } 87 | 88 | free(bondinfo); 89 | return 0; 90 | } 91 | 92 | static ssize_t bond_get_sysfs(char **dest, struct if_entry *entry, const char *prop) 93 | { 94 | char *path; 95 | ssize_t len; 96 | 97 | *dest = NULL; 98 | if (asprintf(&path, "class/net/%s/bonding/%s", entry->if_name, prop) < 0) 99 | return -errno; 100 | 101 | len = sysfs_readfile(dest, path); 102 | free(path); 103 | if (len < 0) { 104 | if (len == -ENOENT) 105 | return 0; 106 | return len; 107 | } 108 | 109 | if (len == 0) 110 | free(dest); 111 | 112 | return len; 113 | } 114 | 115 | static int match_active_slave(struct if_entry *entry, struct if_entry *master) 116 | { 117 | struct bond_private *priv = master->handler_private; 118 | 119 | if (priv->active_slave_index && entry->if_index != priv->active_slave_index) 120 | return 0; 121 | if (priv->active_slave_name && strcmp(entry->if_name, priv->active_slave_name)) 122 | return 0; 123 | return 1; 124 | } 125 | 126 | static int bond_scan(struct if_entry *entry) 127 | { 128 | char *dest, *tmp; 129 | struct bond_private *priv = entry->handler_private; 130 | 131 | if (!priv->mode) { 132 | if (bond_get_sysfs(&dest, entry, "mode") > 0) { 133 | tmp = index(dest, ' '); 134 | if (tmp) 135 | priv->mode = atoi(tmp + 1) + 1; 136 | free(dest); 137 | } 138 | if (priv->mode >= ARRAY_SIZE(bond_mode_name)) 139 | priv->mode = 0; 140 | } 141 | 142 | if (!priv->active_slave_index) 143 | bond_get_sysfs(&priv->active_slave_name, entry, "active_slave"); 144 | 145 | return 0; 146 | } 147 | static int bond_post(struct if_entry *entry, _unused struct list *netns_list) 148 | { 149 | struct bond_private *priv = entry->handler_private; 150 | struct if_entry *slave; 151 | 152 | if (priv->mode && *bond_mode_name[priv->mode]) 153 | if_add_config(entry, "mode", "%s", bond_mode_name[priv->mode]); 154 | 155 | if (priv->active_slave_index || priv->active_slave_name) { 156 | list_for_each_member(slave, entry->rev_master, rev_master_node) { 157 | if (match_active_slave(slave, entry)) { 158 | entry->active_slave = slave; 159 | if_add_state(entry, "active slave", "%s", slave->if_name); 160 | } else { 161 | slave->flags |= IF_PASSIVE_SLAVE; 162 | } 163 | } 164 | } 165 | return 0; 166 | } 167 | 168 | static void bond_cleanup(struct if_entry *entry) 169 | { 170 | struct bond_private *priv = entry->handler_private; 171 | 172 | if (priv->active_slave_name) 173 | free(priv->active_slave_name); 174 | } 175 | -------------------------------------------------------------------------------- /handlers/bond.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_BOND_H 17 | #define _HANDLERS_BOND_H 18 | 19 | void handler_bond_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/bridge.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "bridge.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../handler.h" 22 | #include "../if.h" 23 | #include "../sysfs.h" 24 | 25 | static int bridge_scan(struct if_entry *entry); 26 | 27 | static struct if_handler h_bridge = { 28 | .scan = bridge_scan, 29 | }; 30 | 31 | void handler_bridge_register(void) 32 | { 33 | if_handler_register(&h_bridge); 34 | } 35 | 36 | static int bridge_scan(struct if_entry *entry) 37 | { 38 | int len; 39 | char path [33 + IFNAMSIZ + 1]; 40 | char *ifindex; 41 | 42 | if (entry->master_index) 43 | return 0; 44 | 45 | snprintf(path, sizeof(path), "/class/net/%s/brport/bridge/ifindex", entry->if_name); 46 | 47 | len = sysfs_readfile(&ifindex, path); 48 | if (len < 0) { 49 | if (len == -ENOENT) 50 | return 0; 51 | return -len; 52 | } 53 | 54 | entry->master_index = atoi(ifindex); 55 | free(ifindex); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /handlers/bridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_BRIDGE_H 17 | #define _HANDLERS_BRIDGE_H 18 | 19 | void handler_bridge_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/geneve.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "geneve.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../addr.h" 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../master.h" 25 | #include "../netlink.h" 26 | #include "../tunnel.h" 27 | #include "../utils.h" 28 | 29 | #include "../compat.h" 30 | 31 | #define GENEVE_DEFAULT_PORT 6081 32 | 33 | struct geneve_priv { 34 | int flags; 35 | }; 36 | 37 | static int geneve_netlink(struct if_entry *entry, struct nlattr **linkinfo); 38 | 39 | static struct if_handler h_geneve = { 40 | .driver = "geneve", 41 | .private_size = sizeof(struct geneve_priv), 42 | .netlink = geneve_netlink, 43 | }; 44 | 45 | void handler_geneve_register(void) 46 | { 47 | if_handler_register(&h_geneve); 48 | } 49 | 50 | static int geneve_netlink(struct if_entry *entry, struct nlattr **linkinfo) 51 | { 52 | struct nlattr **geneveinfo; 53 | uint16_t port; 54 | struct geneve_priv *priv; 55 | int err; 56 | 57 | priv = calloc(1, sizeof(struct geneve_priv)); 58 | if (!priv) { 59 | err = ENOMEM; 60 | goto err; 61 | } 62 | entry->handler_private = priv; 63 | 64 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) { 65 | err = ENOENT; 66 | goto err_priv; 67 | } 68 | geneveinfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_GENEVE_MAX); 69 | if (!geneveinfo) { 70 | err = ENOMEM; 71 | goto err_priv; 72 | } 73 | 74 | if (geneveinfo[IFLA_GENEVE_ID]) 75 | if_add_config(entry, "VNI", "%u", nla_read_u32(geneveinfo[IFLA_GENEVE_ID])); 76 | 77 | if (geneveinfo[IFLA_GENEVE_PORT]) { 78 | port = nla_read_be16(geneveinfo[IFLA_GENEVE_PORT]); 79 | if (port != GENEVE_DEFAULT_PORT) 80 | if_add_config(entry, "port", "%u", port); 81 | } 82 | 83 | if (geneveinfo[IFLA_GENEVE_REMOTE]) { 84 | struct addr addr; 85 | if ((err = addr_init(&addr, AF_INET, -1, nla_read(geneveinfo[IFLA_GENEVE_REMOTE])))) 86 | goto err_attrs; 87 | if (!addr_is_zero(&addr)) 88 | if_add_config(entry, "remote", "%s", addr.formatted); 89 | addr_destruct(&addr); 90 | } 91 | 92 | if (geneveinfo[IFLA_GENEVE_REMOTE6]) { 93 | struct addr addr; 94 | if ((err = addr_init(&addr, AF_INET6, -1, nla_read(geneveinfo[IFLA_GENEVE_REMOTE6])))) 95 | goto err_attrs; 96 | if (!addr_is_zero(&addr)) 97 | if_add_config(entry, "remote6", "%s", addr.formatted); 98 | addr_destruct(&addr); 99 | } 100 | 101 | if (geneveinfo[IFLA_GENEVE_COLLECT_METADATA]) 102 | if_add_config(entry, "mode", "external"); 103 | 104 | free(geneveinfo); 105 | return 0; 106 | 107 | err_attrs: 108 | free(geneveinfo); 109 | err_priv: 110 | free(priv); 111 | err: 112 | return err; 113 | } 114 | -------------------------------------------------------------------------------- /handlers/geneve.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_GENEVE_H 17 | #define _HANDLERS_GENEVE_H 18 | 19 | void handler_geneve_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/gre.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2017 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "gre.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "../addr.h" 23 | #include "../handler.h" 24 | #include "../if.h" 25 | #include "../master.h" 26 | #include "../netlink.h" 27 | #include "../tunnel.h" 28 | #include "../netns.h" 29 | 30 | static int gre_netlink(struct if_entry *entry, struct nlattr **linkinfo); 31 | static int gre6_netlink(struct if_entry *entry, struct nlattr **linkinfo); 32 | static int gre_post(struct if_entry *entry, struct list *netns_list); 33 | 34 | struct gre_priv { 35 | struct addr local; 36 | }; 37 | 38 | static struct if_handler h_gre = { 39 | .driver = "gre", 40 | .private_size = sizeof(struct gre_priv), 41 | .netlink = gre_netlink, 42 | .post = gre_post, 43 | }; 44 | 45 | static struct if_handler h_gretap = { 46 | .driver = "gretap", 47 | .private_size = sizeof(struct gre_priv), 48 | .netlink = gre_netlink, 49 | .post = gre_post, 50 | }; 51 | 52 | static struct if_handler h_erspan = { 53 | .driver = "erspan", 54 | .private_size = sizeof(struct gre_priv), 55 | .netlink = gre_netlink, 56 | .post = gre_post, 57 | }; 58 | 59 | static struct if_handler h_ip6gre = { 60 | .driver = "ip6gre", 61 | .private_size = sizeof(struct gre_priv), 62 | .netlink = gre6_netlink, 63 | .post = gre_post, 64 | }; 65 | 66 | static struct if_handler h_ip6gretap = { 67 | .driver = "ip6gretap", 68 | .private_size = sizeof(struct gre_priv), 69 | .netlink = gre6_netlink, 70 | .post = gre_post, 71 | }; 72 | 73 | static struct if_handler h_ip6erspan = { 74 | .driver = "ip6erspan", 75 | .private_size = sizeof(struct gre_priv), 76 | .netlink = gre6_netlink, 77 | .post = gre_post, 78 | }; 79 | 80 | void handler_gre_register(void) 81 | { 82 | if_handler_register(&h_gre); 83 | if_handler_register(&h_gretap); 84 | if_handler_register(&h_erspan); 85 | if_handler_register(&h_ip6gre); 86 | if_handler_register(&h_ip6gretap); 87 | if_handler_register(&h_ip6erspan); 88 | } 89 | 90 | static int gre_common_netlink(int family, struct if_entry *entry, struct nlattr **linkinfo) 91 | { 92 | struct nlattr **greinfo; 93 | struct gre_priv *priv; 94 | int err, key; 95 | 96 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 97 | return ENOENT; 98 | 99 | priv = calloc(1, sizeof(struct gre_priv)); 100 | if (!priv) 101 | return ENOMEM; 102 | entry->handler_private = priv; 103 | 104 | greinfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_GRE_MAX); 105 | if (!greinfo) { 106 | err = ENOMEM; 107 | goto err_priv; 108 | } 109 | 110 | priv->local.family = -1; 111 | if (greinfo[IFLA_GRE_LOCAL]) { 112 | if ((err = addr_init(&priv->local, family, -1, nla_read(greinfo[IFLA_GRE_LOCAL])))) 113 | goto err_attrs; 114 | if (!addr_is_zero(&priv->local)) 115 | if_add_config(entry, "local", "%s", priv->local.formatted); 116 | } 117 | 118 | if (greinfo[IFLA_GRE_REMOTE]) { 119 | struct addr addr; 120 | if ((err = addr_init(&addr, family, -1, nla_read(greinfo[IFLA_GRE_REMOTE])))) 121 | goto err_attrs; 122 | if (!addr_is_zero(&addr)) 123 | if_add_config(entry, "remote", "%s", addr.formatted); 124 | addr_destruct(&addr); 125 | } 126 | 127 | if (greinfo[IFLA_GRE_LINK]) 128 | entry->link_index = nla_read_u8(greinfo[IFLA_GRE_LINK]); 129 | 130 | if (greinfo[IFLA_GRE_IKEY]) { 131 | if ((key = nla_read_u32(greinfo[IFLA_GRE_IKEY]))) 132 | if_add_config(entry, "ikey", "%u", ntohl(key)); 133 | } 134 | 135 | if (greinfo[IFLA_GRE_OKEY]) 136 | if ((key = nla_read_u32(greinfo[IFLA_GRE_OKEY]))) 137 | if_add_config(entry, "okey", "%u", ntohl(key)); 138 | 139 | free(greinfo); 140 | return 0; 141 | 142 | err_attrs: 143 | free(greinfo); 144 | err_priv: 145 | free(priv); 146 | return err; 147 | } 148 | 149 | static int gre_netlink(struct if_entry *entry, struct nlattr **linkinfo) 150 | { 151 | return gre_common_netlink(AF_INET, entry, linkinfo); 152 | } 153 | 154 | static int gre6_netlink(struct if_entry *entry, struct nlattr **linkinfo) 155 | { 156 | return gre_common_netlink(AF_INET6, entry, linkinfo); 157 | } 158 | 159 | static int gre_post(struct if_entry *entry, _unused struct list *netns_list) 160 | { 161 | struct gre_priv *priv; 162 | struct if_entry *ife; 163 | 164 | priv = (struct gre_priv *) entry->handler_private; 165 | if (priv->local.family >= 0) { 166 | struct netns_entry *ns = entry->link_net ? : entry->ns; 167 | 168 | if ((ife = tunnel_find_addr(ns, &priv->local))) { 169 | link_set(ife, entry); 170 | entry->flags |= IF_LINK_WEAK; 171 | } 172 | } 173 | 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /handlers/gre.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2017 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_GRE_H 17 | #define _HANDLERS_GRE_H 18 | 19 | void handler_gre_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/iov.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "iov.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../handler.h" 22 | #include "../if.h" 23 | #include "../match.h" 24 | #include "../sysfs.h" 25 | 26 | struct netns_entry; 27 | 28 | static int iov_scan(struct if_entry *entry); 29 | static int iov_post(struct if_entry *entry, struct list *netns_list); 30 | static void iov_cleanup(struct if_entry *entry); 31 | 32 | static struct if_handler h_iov = { 33 | .scan = iov_scan, 34 | .post = iov_post, 35 | .cleanup = iov_cleanup, 36 | }; 37 | 38 | void handler_iov_register(void) 39 | { 40 | if_handler_register(&h_iov); 41 | } 42 | 43 | static int iov_scan(struct if_entry *entry) 44 | { 45 | char *resolved; 46 | char *path; 47 | 48 | if (asprintf(&path, "class/net/%s/device", entry->if_name) < 0) 49 | return errno; 50 | 51 | resolved = sysfs_realpath(path); 52 | free(path); 53 | 54 | if (resolved) { 55 | entry->pci_path = resolved; 56 | } else { 57 | if (errno == ENOENT) 58 | return 0; /* this is not a PCI device */ 59 | return errno; 60 | } 61 | 62 | if (asprintf(&path, "class/net/%s/device/physfn", entry->if_name) < 0) 63 | return errno; 64 | 65 | resolved = sysfs_realpath(path); 66 | free(path); 67 | 68 | if (resolved) { 69 | entry->pci_physfn_path = resolved; 70 | } else { 71 | if (errno == ENOENT) 72 | return 0; /* this is not a VF */ 73 | return errno; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | static int match_physfn(struct if_entry *physfn, void *arg) 80 | { 81 | struct if_entry *entry = arg; 82 | 83 | if (!physfn->pci_path) 84 | return 0; 85 | 86 | if (!strcmp(physfn->pci_path, entry->pci_physfn_path)) 87 | return 1; 88 | 89 | return 0; 90 | } 91 | 92 | static int iov_post(struct if_entry *entry, struct list *netns_list) 93 | { 94 | struct match_desc match; 95 | int err = 0; 96 | 97 | if (entry->physfn || !entry->pci_physfn_path) 98 | return 0; 99 | 100 | match_init(&match); 101 | match.mode = MM_FIRST; 102 | match.netns_list = netns_list; 103 | match.exclude = entry; 104 | if ((err = match_if(&match, match_physfn, entry))) 105 | return err; 106 | if (!(entry->physfn = match_found(match))) 107 | return if_add_warning(entry, "failed to find the iov physfn"); 108 | return 0; 109 | } 110 | 111 | static void iov_cleanup(struct if_entry *entry) 112 | { 113 | if (entry->pci_path) 114 | sysfs_free(entry->pci_path); 115 | 116 | if (entry->pci_physfn_path) 117 | sysfs_free(entry->pci_physfn_path); 118 | } 119 | -------------------------------------------------------------------------------- /handlers/iov.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_IOV_H 17 | #define _HANDLERS_IOV_H 18 | 19 | void handler_iov_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/ipxipy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "ipxipy.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "../addr.h" 23 | #include "../handler.h" 24 | #include "../if.h" 25 | #include "../master.h" 26 | #include "../netlink.h" 27 | #include "../tunnel.h" 28 | #include "../netns.h" 29 | 30 | static int ipip_netlink(struct if_entry *entry, struct nlattr **linkinfo); 31 | static int ipxip6_netlink(struct if_entry *entry, struct nlattr **linkinfo); 32 | static int sit_netlink(struct if_entry *entry, struct nlattr **linkinfo); 33 | static int ipxipy_post(struct if_entry *entry, struct list *netns_list); 34 | 35 | struct ipxipy_priv { 36 | struct addr local; 37 | }; 38 | 39 | static struct if_handler h_ipip = { 40 | .driver = "ipip", 41 | .private_size = sizeof(struct ipxipy_priv), 42 | .netlink = ipip_netlink, 43 | .post = ipxipy_post, 44 | }; 45 | 46 | static struct if_handler h_sit = { 47 | .driver = "sit", 48 | .private_size = sizeof(struct ipxipy_priv), 49 | .netlink = sit_netlink, 50 | .post = ipxipy_post, 51 | }; 52 | 53 | static struct if_handler h_ip6tnl = { 54 | .driver = "ip6tnl", 55 | .private_size = sizeof(struct ipxipy_priv), 56 | .netlink = ipxip6_netlink, 57 | .post = ipxipy_post, 58 | }; 59 | 60 | void handler_ipxipy_register(void) 61 | { 62 | if_handler_register(&h_ipip); 63 | if_handler_register(&h_sit); 64 | if_handler_register(&h_ip6tnl); 65 | } 66 | 67 | static int ipxipy_netlink(int family, struct if_entry *entry, struct nlattr **linkinfo) 68 | { 69 | struct nlattr **info; 70 | struct ipxipy_priv *priv; 71 | int err; 72 | 73 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 74 | return ENOENT; 75 | 76 | priv = calloc(1, sizeof(struct ipxipy_priv)); 77 | if (!priv) 78 | return ENOMEM; 79 | entry->handler_private = priv; 80 | 81 | info = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_IPTUN_MAX); 82 | if (!info) { 83 | err = ENOMEM; 84 | goto err_priv; 85 | } 86 | 87 | priv->local.family = -1; 88 | if (info[IFLA_IPTUN_LOCAL]) { 89 | if ((err = addr_init(&priv->local, family, -1, nla_read(info[IFLA_IPTUN_LOCAL])))) 90 | goto err_attrs; 91 | if (!addr_is_zero(&priv->local)) 92 | if_add_config(entry, "local", "%s", priv->local.formatted); 93 | } 94 | 95 | if (info[IFLA_IPTUN_REMOTE]) { 96 | struct addr addr; 97 | if ((err = addr_init(&addr, family, -1, nla_read(info[IFLA_IPTUN_REMOTE])))) 98 | goto err_attrs; 99 | if (!addr_is_zero(&addr)) 100 | if_add_config(entry, "remote", "%s", addr.formatted); 101 | addr_destruct(&addr); 102 | } 103 | 104 | if (info[IFLA_IPTUN_LINK]) 105 | entry->link_index = nla_read_u8(info[IFLA_IPTUN_LINK]); 106 | 107 | if (family == AF_INET6 && info[IFLA_IPTUN_PROTO]) { 108 | switch (nla_read_u8(info[IFLA_IPTUN_PROTO])) { 109 | case IPPROTO_IPIP: 110 | if_add_config(entry, "proto", "ipip6"); 111 | break; 112 | case IPPROTO_IPV6: 113 | if_add_config(entry, "proto", "ip6ip6"); 114 | break; 115 | case 0: 116 | if_add_config(entry, "proto", "any"); 117 | break; 118 | } 119 | } 120 | 121 | free(info); 122 | return 0; 123 | 124 | err_attrs: 125 | free(info); 126 | err_priv: 127 | free(priv); 128 | return err; 129 | } 130 | 131 | static int ipip_netlink(struct if_entry *entry, struct nlattr **linkinfo) 132 | { 133 | return ipxipy_netlink(AF_INET, entry, linkinfo); 134 | } 135 | 136 | static int sit_netlink(struct if_entry *entry, struct nlattr **linkinfo) 137 | { 138 | return ipxipy_netlink(AF_INET, entry, linkinfo); 139 | } 140 | 141 | static int ipxip6_netlink(struct if_entry *entry, struct nlattr **linkinfo) 142 | { 143 | return ipxipy_netlink(AF_INET6, entry, linkinfo); 144 | } 145 | 146 | static int ipxipy_post(struct if_entry *entry, _unused struct list *netns_list) 147 | { 148 | struct ipxipy_priv *priv; 149 | struct if_entry *ife; 150 | 151 | priv = (struct ipxipy_priv *) entry->handler_private; 152 | if (priv->local.family >= 0) { 153 | struct netns_entry *ns = entry->link_net ? : entry->ns; 154 | 155 | if ((ife = tunnel_find_addr(ns, &priv->local))) { 156 | link_set(ife, entry); 157 | entry->flags |= IF_LINK_WEAK; 158 | } 159 | } 160 | 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /handlers/ipxipy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_IPXIPY_H 17 | #define _HANDLERS_IPXIPY_H 18 | 19 | void handler_ipxipy_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/macsec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "macsec.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../netlink.h" 25 | 26 | static int macsec_netlink(struct if_entry *entry, struct nlattr **linkinfo); 27 | 28 | static struct if_handler h_macsec = { 29 | .driver = "macsec", 30 | .netlink = macsec_netlink, 31 | }; 32 | 33 | void handler_macsec_register(void) 34 | { 35 | if_handler_register(&h_macsec); 36 | } 37 | 38 | static int macsec_netlink(struct if_entry *entry, struct nlattr **linkinfo) 39 | { 40 | struct nlattr **macsecinfo; 41 | int err = 0; 42 | uint64_t sci; 43 | 44 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 45 | return ENOENT; 46 | 47 | macsecinfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_MACSEC_MAX); 48 | if (!macsecinfo) 49 | return ENOMEM; 50 | 51 | if (!macsecinfo[IFLA_MACSEC_SCI]) { 52 | err = ENOENT; 53 | goto err_attrs; 54 | } 55 | 56 | sci = nla_read_be64(macsecinfo[IFLA_MACSEC_SCI]); 57 | if (asprintf(&entry->edge_label, "sci %" PRIx64, sci) < 0) { 58 | err = ENOMEM; 59 | goto err_attrs; 60 | } 61 | 62 | err_attrs: 63 | free(macsecinfo); 64 | return err; 65 | } 66 | -------------------------------------------------------------------------------- /handlers/macsec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_MACSEC_H 17 | #define _HANDLERS_MACSEC_H 18 | 19 | void handler_macsec_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/openvswitch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_OPENVSWITCH_H 17 | #define _HANDLERS_OPENVSWITCH_H 18 | 19 | void handler_openvswitch_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/route.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "route.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../handler.h" 22 | #include "../if.h" 23 | #include "../label.h" 24 | #include "../list.h" 25 | #include "../netlink.h" 26 | #include "../netns.h" 27 | #include "../route.h" 28 | 29 | #include "../compat.h" 30 | 31 | static int route_scan(struct netns_entry *entry); 32 | static void route_cleanup(struct netns_entry *entry); 33 | 34 | static struct netns_handler h_route = { 35 | .scan = route_scan, 36 | .cleanup = route_cleanup, 37 | }; 38 | 39 | void handler_route_register(void) 40 | { 41 | netns_handler_register(&h_route); 42 | } 43 | 44 | static int route_parse_metrics(struct list *metrics, struct nlattr *mxrta) 45 | { 46 | struct rtmetric *rtm; 47 | 48 | for_each_nla_nested(a, mxrta) { 49 | if (a->nla_type >= RTAX_CC_ALGO) 50 | continue; 51 | 52 | rtm = calloc(1, sizeof(struct rtmetric)); 53 | if (!rtm) 54 | return ENOMEM; 55 | 56 | rtm->type = a->nla_type; 57 | rtm->value = nla_read_u32(a); 58 | list_append(metrics, node(rtm)); 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | int route_create_netlink(struct route **rte, struct nlmsg *msg) 65 | { 66 | struct rtmsg *rtmsg; 67 | struct nlattr **tb; 68 | struct route *r; 69 | int err; 70 | 71 | *rte = NULL; 72 | 73 | if (nlmsg_get_hdr(msg)->nlmsg_type != RTM_NEWROUTE) 74 | return ENOENT; 75 | 76 | rtmsg = nlmsg_get(msg, sizeof(*rtmsg)); 77 | if (!rtmsg) 78 | return ENOENT; 79 | 80 | r = calloc(1, sizeof(struct route)); 81 | if (!r) 82 | return ENOMEM; 83 | 84 | r->family = rtmsg->rtm_family; 85 | r->protocol = rtmsg->rtm_protocol; 86 | r->scope = rtmsg->rtm_scope; 87 | r->tos = rtmsg->rtm_tos; 88 | r->type = rtmsg->rtm_type; 89 | 90 | tb = nlmsg_attrs(msg, RTA_MAX); 91 | if (!tb) { 92 | err = ENOMEM; 93 | goto err_rte; 94 | } 95 | 96 | if (tb[RTA_TABLE]) 97 | r->table_id = nla_read_u32(tb[RTA_TABLE]); 98 | else 99 | r->table_id = rtmsg->rtm_table; 100 | 101 | if (tb[RTA_SRC]) 102 | addr_init(&r->src, r->family, rtmsg->rtm_src_len, 103 | nla_read(tb[RTA_SRC])); 104 | if (tb[RTA_DST]) 105 | addr_init(&r->dst, r->family, rtmsg->rtm_dst_len, 106 | nla_read(tb[RTA_DST])); 107 | if (tb[RTA_GATEWAY]) 108 | addr_init(&r->gw, r->family, -1, 109 | nla_read(tb[RTA_GATEWAY])); 110 | if (tb[RTA_PREFSRC]) 111 | addr_init(&r->prefsrc, r->family, -1, 112 | nla_read(tb[RTA_PREFSRC])); 113 | 114 | if (tb[RTA_OIF]) 115 | r->oifindex = nla_read_u32(tb[RTA_OIF]); 116 | if (tb[RTA_IIF]) 117 | r->iifindex = nla_read_u32(tb[RTA_IIF]); 118 | if (tb[RTA_PRIORITY]) 119 | r->priority = nla_read_u32(tb[RTA_PRIORITY]); 120 | 121 | 122 | list_init(&r->metrics); 123 | if (tb[RTA_METRICS]) 124 | if ((err = route_parse_metrics(&r->metrics, tb[RTA_METRICS]))) 125 | goto err_tb; 126 | 127 | *rte = r; 128 | free(tb); 129 | return 0; 130 | 131 | err_tb: 132 | free(tb); 133 | err_rte: 134 | free(r); 135 | return err; 136 | } 137 | 138 | static int rtable_create(struct rtable **rtd, int id) 139 | { 140 | struct rtable *rt; 141 | 142 | rt = calloc(1, sizeof(struct rtable)); 143 | if (!rt) 144 | return ENOMEM; 145 | 146 | rt->id = id; 147 | list_init(&rt->routes); 148 | 149 | *rtd = rt; 150 | return 0; 151 | } 152 | 153 | static struct if_entry *find_if_by_ifindex(struct list *list, unsigned int ifindex) 154 | { 155 | struct if_entry *entry; 156 | 157 | list_for_each(entry, *list) 158 | if (entry->if_index == ifindex) 159 | return entry; 160 | return NULL; 161 | } 162 | 163 | 164 | int route_scan(struct netns_entry *ns) 165 | { 166 | struct nl_handle hnd; 167 | struct nlmsg *req, *resp; 168 | struct rtmsg msg = { 169 | .rtm_table = RT_TABLE_UNSPEC, 170 | .rtm_protocol = RTPROT_UNSPEC, 171 | }; 172 | struct rtable *tables [256]; 173 | struct route *r = NULL; 174 | int err, i; 175 | 176 | memset(tables, 0, sizeof(tables)); 177 | list_init(&ns->rtables); 178 | 179 | if ((err = rtnl_open(&hnd))) 180 | return err; 181 | 182 | req = nlmsg_new(RTM_GETROUTE, NLM_F_DUMP); 183 | if (!req) { 184 | err = ENOMEM; 185 | goto err_handle; 186 | } 187 | err = nlmsg_put(req, &msg, sizeof(msg)); 188 | if (err) 189 | goto err_req; 190 | 191 | err = nl_exchange(&hnd, req, &resp); 192 | if (err) 193 | goto err_req; 194 | 195 | for_each_nlmsg(nle, resp) { 196 | if ((err = route_create_netlink(&r, nle))) 197 | goto err_resp; 198 | 199 | r->oif = find_if_by_ifindex(&ns->ifaces, r->oifindex); 200 | r->iif = find_if_by_ifindex(&ns->ifaces, r->iifindex); 201 | 202 | if (!tables[r->table_id]) 203 | if ((err = rtable_create(&tables[r->table_id], r->table_id))) 204 | goto err_route; 205 | 206 | list_append(&tables[r->table_id]->routes, node(r)); 207 | r = NULL; 208 | } 209 | 210 | for (i = 255; i >= 0; i--) { 211 | if (tables[i]) 212 | list_append(&ns->rtables, node(tables[i])); 213 | } 214 | 215 | err_route: 216 | if (r) 217 | free(r); 218 | err_resp: 219 | nlmsg_free(resp); 220 | err_req: 221 | nlmsg_free(req); 222 | err_handle: 223 | nl_close(&hnd); 224 | return err; 225 | } 226 | 227 | static void route_destruct(struct route *r) 228 | { 229 | list_free(&r->metrics, NULL); 230 | } 231 | 232 | static void rtable_free(struct rtable *rt) 233 | { 234 | list_free(&rt->routes, (destruct_f) route_destruct); 235 | } 236 | 237 | static void route_cleanup(struct netns_entry *entry) 238 | { 239 | list_free(&entry->rtables, (destruct_f) rtable_free); 240 | } 241 | -------------------------------------------------------------------------------- /handlers/route.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_ROUTE_H 17 | #define _HANDLERS_ROUTE_H 18 | 19 | void handler_route_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/team.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "team.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "../handler.h" 30 | #include "../if.h" 31 | #include "../utils.h" 32 | 33 | #include "../compat.h" 34 | 35 | struct netns_entry; 36 | 37 | #define TEAMD_REQUEST_PREFIX "REQUEST" 38 | #define TEAMD_ERR_PREFIX "REPLY_ERROR" 39 | #define TEAMD_SUCC_PREFIX "REPLY_SUCCESS" 40 | #define TEAMD_SOCK_PATH "/var/run/teamd/" 41 | 42 | #define TEAMD_REPLY_TIMEOUT 5000 43 | #define TEAMD_REQ TEAMD_REQUEST_PREFIX "\nStateDump\n" 44 | 45 | struct team_priv { 46 | json_t *active_port_name; 47 | }; 48 | 49 | static int team_scan(struct if_entry *entry); 50 | static int team_post(struct if_entry *entry, struct list *netns_list); 51 | static void team_cleanup(struct if_entry *entry); 52 | 53 | static struct if_handler h_team = { 54 | .driver = "team", 55 | .private_size = sizeof(struct team_priv), 56 | .scan = team_scan, 57 | .post = team_post, 58 | .cleanup = team_cleanup 59 | }; 60 | 61 | 62 | void handler_team_register(void) 63 | { 64 | if_handler_register(&h_team); 65 | } 66 | 67 | static int team_connect(struct if_entry *entry) 68 | { 69 | int fd, fl; 70 | struct sockaddr_un addr; 71 | 72 | fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 73 | if (fd < 0) 74 | return -errno; 75 | 76 | memset(&addr, 0, sizeof(addr)); 77 | addr.sun_family = AF_UNIX; 78 | snprintf(addr.sun_path, UNIX_PATH_MAX, TEAMD_SOCK_PATH"%s.sock", entry->if_name); 79 | 80 | fl = fcntl(fd, F_GETFL); 81 | fl |= O_NONBLOCK; 82 | if (fcntl(fd, F_SETFL, fl) == -1) 83 | goto err; 84 | 85 | if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) 86 | goto err; 87 | 88 | return fd; 89 | err: 90 | close(fd); 91 | return -errno; 92 | } 93 | 94 | static int team_wait(int fd) 95 | { 96 | fd_set rfds; 97 | struct timeval tv; 98 | int ret; 99 | 100 | tv.tv_sec = TEAMD_REPLY_TIMEOUT / 1000; 101 | tv.tv_usec = TEAMD_REPLY_TIMEOUT % 1000 * 1000; 102 | FD_ZERO(&rfds); 103 | FD_SET(fd, &rfds); 104 | 105 | ret = select(fd + 1, &rfds, NULL, NULL, &tv); 106 | if (ret < 0) 107 | return errno; 108 | 109 | if (!FD_ISSET(fd, &rfds)) 110 | return ETIMEDOUT; 111 | 112 | return 0; 113 | } 114 | 115 | static char *team_recv(int fd) 116 | { 117 | int size, len, r; 118 | char *buf, *newbuf; 119 | 120 | size = 4096; 121 | len = 0; 122 | buf = malloc(size); 123 | if (!buf) 124 | return NULL; 125 | 126 | while ((r = read(fd, buf + len, size - len))) { 127 | if (r < 0) { 128 | free(buf); 129 | return NULL; 130 | } 131 | 132 | len += r; 133 | if (len < size) 134 | break; 135 | 136 | size *= 2; 137 | newbuf = realloc(buf, size); 138 | if (!newbuf) { 139 | free(buf); 140 | return NULL; 141 | } 142 | buf = newbuf; 143 | } 144 | 145 | buf[len] = '\0'; 146 | return buf; 147 | } 148 | 149 | static char *team_getline(char **reply) 150 | { 151 | char *nl, *ret; 152 | 153 | nl = strchr(*reply, '\n'); 154 | if (nl == NULL) { 155 | ret = *reply; 156 | *reply = ""; 157 | return ret; 158 | } 159 | 160 | *nl = '\0'; 161 | ret = *reply; 162 | *reply = nl + 1; 163 | return ret; 164 | } 165 | 166 | int team_check_if(json_t *jdev, struct if_entry *entry, json_error_t *jerr) 167 | { 168 | char *ifname; 169 | unsigned int ifindex; 170 | 171 | if (json_unpack_ex(jdev, jerr, 0, "{s:{s:i,s:s}}", "ifinfo", "ifindex", &ifindex, "ifname", &ifname)) 172 | return -1; 173 | 174 | return ifindex != entry->if_index || strcmp(ifname, entry->if_name); 175 | } 176 | 177 | int team_parse_runner(json_t *jrunner, struct if_entry *entry, _unused json_error_t *jerr) 178 | { 179 | struct team_priv *priv = entry->handler_private; 180 | json_t *jport; 181 | 182 | if (jrunner == NULL) 183 | return 0; 184 | 185 | jport = json_object_get(jrunner, "active_port"); 186 | if (jport) { 187 | priv->active_port_name = jport; 188 | json_incref(jport); 189 | } 190 | 191 | return 0; 192 | } 193 | 194 | int team_parse_setup(json_t *jsetup, struct if_entry *entry, json_error_t *jerr) 195 | { 196 | char *runner_name; 197 | 198 | if (json_unpack_ex(jsetup, jerr, 0, "{s:s}", "runner_name", &runner_name)) 199 | return -1; 200 | 201 | if_add_config(entry, "runner", "%s", runner_name); 202 | return 0; 203 | } 204 | 205 | 206 | static void team_parse_json(char *json, struct if_entry *entry) 207 | { 208 | json_error_t jerr; 209 | json_t *jroot, *jports, *jsetup, *jdev, *jrunner; 210 | char *errstr; 211 | 212 | errstr = NULL; 213 | 214 | jroot = json_loads(json, 0, &jerr); 215 | if (!jroot) { 216 | errstr = "Failed to parse JSON."; 217 | goto error_load; 218 | } 219 | 220 | jrunner = jports = NULL; 221 | if (json_unpack_ex(jroot, &jerr, 0, "{s:o,s:o,s?:o,s?:o}", "setup", &jsetup, "team_device", &jdev, "runner", &jrunner)) { 222 | errstr = jerr.text; 223 | goto error_jroot; 224 | } 225 | 226 | if (team_check_if(jdev, entry, &jerr)) { 227 | errstr = "Daemon controling different device"; 228 | goto error_jroot; 229 | } 230 | 231 | if (team_parse_setup(jsetup, entry, &jerr)) { 232 | errstr = jerr.text; 233 | goto error_jroot; 234 | } 235 | 236 | if (team_parse_runner(jrunner, entry, &jerr)) { 237 | errstr = jerr.text; 238 | goto error_jroot; 239 | } 240 | 241 | error_jroot: 242 | json_decref(jroot); 243 | error_load: 244 | if (errstr) 245 | if_add_warning(entry, "Team: Error parsing reply (%s)", errstr); 246 | 247 | return; 248 | } 249 | 250 | static void team_parse_reply(char *reply, struct if_entry *entry) 251 | { 252 | char *rest, *line; 253 | 254 | rest = reply; 255 | line = team_getline(&rest); 256 | if (strncmp(line, TEAMD_SUCC_PREFIX, sizeof(TEAMD_SUCC_PREFIX)) == 0) { 257 | team_parse_json(rest, entry); 258 | return; 259 | } 260 | if (strncmp(line, TEAMD_ERR_PREFIX, sizeof(TEAMD_ERR_PREFIX)) == 0) { 261 | line = team_getline(&rest); 262 | if_add_warning(entry, "Team: Received error reply (%s: %s)", line, rest); 263 | return; 264 | } 265 | if_add_warning(entry, "Team: Cannot parse reply"); 266 | } 267 | 268 | static int team_scan(struct if_entry *entry) 269 | { 270 | int fd, err; 271 | char *reply; 272 | 273 | fd = team_connect(entry); 274 | if (fd < 0) { 275 | if_add_warning(entry, "Team: Failed to connect to teamd (%s)", strerror(-fd)); 276 | return 0; 277 | } 278 | 279 | if (write(fd, TEAMD_REQ, sizeof(TEAMD_REQ)) < (ssize_t) sizeof(TEAMD_REQ)) { 280 | if_add_warning(entry, "Team: Failed to send request (%s)", strerror(errno)); 281 | goto error_conn; 282 | } 283 | 284 | if ((err = team_wait(fd))) { 285 | if_add_warning(entry, "Team: Failed to get status (%s)", strerror(err)); 286 | goto error_conn; 287 | } 288 | 289 | reply = team_recv(fd); 290 | if (!reply) { 291 | err = errno; 292 | if_add_warning(entry, "Team: Failed to receive reply (%s)", strerror(errno)); 293 | goto error_conn; 294 | } 295 | 296 | team_parse_reply(reply, entry); 297 | free(reply); 298 | 299 | error_conn: 300 | close(fd); 301 | return 0; 302 | } 303 | 304 | static int team_post(struct if_entry *master, _unused struct list *netns_list) 305 | { 306 | struct team_priv *priv = master->handler_private; 307 | struct if_entry *slave; 308 | const char *active_name; 309 | 310 | if (!master->active_slave && priv->active_port_name) { 311 | active_name = json_string_value(priv->active_port_name); 312 | list_for_each_member(slave, master->rev_master, rev_master_node) { 313 | if (strcmp(slave->if_name, active_name)) { 314 | slave->flags |= IF_PASSIVE_SLAVE; 315 | } else { 316 | master->active_slave = slave; 317 | if_add_state(master, "active port", "%s", slave->if_name); 318 | } 319 | } 320 | } 321 | return 0; 322 | } 323 | 324 | static void team_cleanup(struct if_entry *entry) 325 | { 326 | struct team_priv *priv = entry->handler_private; 327 | 328 | if (priv->active_port_name) 329 | json_decref(priv->active_port_name); 330 | } 331 | -------------------------------------------------------------------------------- /handlers/team.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_TEAM_H 17 | #define _HANDLERS_TEAM_H 18 | 19 | void handler_team_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/veth.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "veth.h" 17 | #include 18 | #include 19 | #include "../ethtool.h" 20 | #include "../handler.h" 21 | #include "../if.h" 22 | #include "../master.h" 23 | #include "../match.h" 24 | 25 | struct netns_entry; 26 | 27 | static int veth_scan(struct if_entry *entry); 28 | static int veth_post(struct if_entry *entry, struct list *netns_list); 29 | 30 | static struct if_handler h_veth = { 31 | .driver = "veth", 32 | .scan = veth_scan, 33 | .post = veth_post, 34 | }; 35 | 36 | void handler_veth_register(void) 37 | { 38 | if_handler_register(&h_veth); 39 | } 40 | 41 | static int veth_scan(struct if_entry *entry) 42 | { 43 | if (entry->link_index) { 44 | entry->peer_index = entry->link_index; 45 | entry->peer_netnsid = entry->link_netnsid; 46 | entry->link_index = 0; 47 | entry->link_netnsid = -1; 48 | } else { 49 | entry->peer_index = ethtool_veth_peer(entry->if_name); 50 | } 51 | return 0; 52 | } 53 | 54 | static int match_peer(struct if_entry *entry, void *arg) 55 | { 56 | struct if_entry *link = arg; 57 | 58 | if (entry->if_index != link->peer_index || 59 | entry->peer_index != link->if_index || 60 | strcmp(entry->driver, "veth")) 61 | return 0; 62 | if (entry->peer && entry->peer != link) 63 | return 0; 64 | if (entry->ns == link->ns) 65 | return 2; 66 | return 1; 67 | } 68 | 69 | static int veth_post(struct if_entry *entry, struct list *netns_list) 70 | { 71 | int err; 72 | struct match_desc match; 73 | 74 | if (entry->peer) 75 | return 0; 76 | if (!entry->peer_index) 77 | return ENOENT; 78 | 79 | match_init(&match); 80 | match.netns_list = netns_list; 81 | match.exclude = entry; 82 | 83 | if ((err = match_if(&match, match_peer, entry))) 84 | return err; 85 | if (match_ambiguous(match)) 86 | return if_add_warning(entry, "failed to find the veth peer reliably"); 87 | if (!match_found(match)) 88 | return if_add_warning(entry, "failed to find the veth peer"); 89 | peer_set(entry, match_found(match)); 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /handlers/veth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_VETH_H 17 | #define _HANDLERS_VETH_H 18 | 19 | void handler_veth_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/vlan.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "vlan.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../handler.h" 22 | #include "../if.h" 23 | #include "../netlink.h" 24 | 25 | struct vlan_private { 26 | unsigned int tag; 27 | }; 28 | 29 | static int vlan_netlink(struct if_entry *entry, struct nlattr **linkinfo); 30 | 31 | static struct if_handler h_vlan = { 32 | .driver = "802.1Q VLAN Support", 33 | .private_size = sizeof(struct vlan_private), 34 | .netlink = vlan_netlink, 35 | }; 36 | 37 | void handler_vlan_register(void) 38 | { 39 | if_handler_register(&h_vlan); 40 | } 41 | 42 | static int vlan_netlink(struct if_entry *entry, struct nlattr **linkinfo) 43 | { 44 | struct vlan_private *priv = entry->handler_private; 45 | struct nlattr **vlanattr; 46 | int err = 0; 47 | 48 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 49 | return ENOENT; 50 | vlanattr = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_VLAN_MAX); 51 | if (!vlanattr) 52 | return ENOMEM; 53 | if (!vlanattr[IFLA_VLAN_ID]) { 54 | err = ENOENT; 55 | goto out; 56 | } 57 | priv->tag = nla_read_u16(vlanattr[IFLA_VLAN_ID]); 58 | if (asprintf(&entry->edge_label, "tag %d", priv->tag) < 0) { 59 | err = ENOMEM; 60 | goto out; 61 | } 62 | out: 63 | free(vlanattr); 64 | return err; 65 | } 66 | -------------------------------------------------------------------------------- /handlers/vlan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_VLAN_H 17 | #define _HANDLERS_VLAN_H 18 | 19 | void handler_vlan_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/vti.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "vti.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../addr.h" 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../master.h" 25 | #include "../netlink.h" 26 | #include "../tunnel.h" 27 | #include "../utils.h" 28 | 29 | #include "../compat.h" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | static int vti4_netlink(struct if_entry *entry, struct nlattr **linkinfo); 36 | static int vti6_netlink(struct if_entry *entry, struct nlattr **linkinfo); 37 | static int vti_post(struct if_entry *entry, struct list *netns_list); 38 | 39 | struct vti_priv { 40 | struct addr local; 41 | }; 42 | 43 | static struct if_handler h_vti4 = { 44 | .driver = "vti", 45 | .netlink = vti4_netlink, 46 | .post = vti_post, 47 | }; 48 | 49 | static struct if_handler h_vti6 = { 50 | .driver = "vti6", 51 | .netlink = vti6_netlink, 52 | .post = vti_post, 53 | }; 54 | 55 | void handler_vti_register(void) 56 | { 57 | if_handler_register(&h_vti4); 58 | if_handler_register(&h_vti6); 59 | } 60 | 61 | static int vti_netlink(int family, struct if_entry *entry, struct nlattr **linkinfo) 62 | { 63 | struct nlattr **vtiinfo; 64 | struct vti_priv *priv; 65 | int err, key; 66 | 67 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 68 | return ENOENT; 69 | 70 | priv = calloc(1, sizeof(struct vti_priv)); 71 | if (!priv) 72 | return ENOMEM; 73 | entry->handler_private = priv; 74 | 75 | vtiinfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_VTI_MAX); 76 | if (!vtiinfo) { 77 | err = ENOMEM; 78 | goto err_priv; 79 | } 80 | 81 | if (vtiinfo[IFLA_VTI_REMOTE]) { 82 | struct addr addr; 83 | if ((err = addr_init(&addr, family, -1, nla_read(vtiinfo[IFLA_VTI_REMOTE])))) 84 | goto err_attrs; 85 | if (!addr_is_zero(&addr)) 86 | if_add_config(entry, "remote", "%s", addr.formatted); 87 | addr_destruct(&addr); 88 | } 89 | 90 | priv->local.family = -1; 91 | if (vtiinfo[IFLA_VTI_LOCAL]) { 92 | if ((err = addr_init(&priv->local, family, -1, nla_read(vtiinfo[IFLA_VTI_LOCAL])))) 93 | goto err_attrs; 94 | if (!addr_is_zero(&priv->local)) 95 | if_add_config(entry, "local", "%s", priv->local.formatted); 96 | } 97 | 98 | if (vtiinfo[IFLA_VTI_IKEY]) { 99 | if ((key = nla_read_be32(vtiinfo[IFLA_VTI_IKEY]))) 100 | if_add_config(entry, "ikey", "%u", key); 101 | } 102 | 103 | if (vtiinfo[IFLA_VTI_OKEY]) { 104 | if ((key = nla_read_be32(vtiinfo[IFLA_VTI_OKEY]))) 105 | if_add_config(entry, "okey", "%u", key); 106 | } 107 | 108 | free(vtiinfo); 109 | return 0; 110 | 111 | err_attrs: 112 | free(vtiinfo); 113 | err_priv: 114 | free(priv); 115 | return err; 116 | } 117 | 118 | static int vti4_netlink(struct if_entry *entry, struct nlattr **linkinfo) 119 | { 120 | return vti_netlink(AF_INET, entry, linkinfo); 121 | } 122 | 123 | static int vti6_netlink(struct if_entry *entry, struct nlattr **linkinfo) 124 | { 125 | return vti_netlink(AF_INET6, entry, linkinfo); 126 | } 127 | 128 | static int vti_post(struct if_entry *entry, _unused struct list *netns_list) 129 | { 130 | struct vti_priv *priv; 131 | struct if_entry *ife; 132 | 133 | priv = (struct vti_priv *) entry->handler_private; 134 | if (priv->local.family >= 0) { 135 | struct netns_entry *ns = entry->link_net ? : entry->ns; 136 | 137 | if ((ife = tunnel_find_addr(ns, &priv->local))) { 138 | link_set(ife, entry); 139 | entry->flags |= IF_LINK_WEAK; 140 | } 141 | } 142 | 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /handlers/vti.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_VTI_H 17 | #define _HANDLERS_VTI_H 18 | 19 | void handler_vti_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/vxlan.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "vxlan.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../addr.h" 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../master.h" 25 | #include "../netlink.h" 26 | #include "../tunnel.h" 27 | #include "../utils.h" 28 | 29 | #include "../compat.h" 30 | 31 | #define VXLAN_DEFAULT_PORT 4789 32 | 33 | struct vxlan_priv { 34 | struct addr *local; 35 | struct addr *group; 36 | int flags; 37 | }; 38 | 39 | static int vxlan_netlink(struct if_entry *entry, struct nlattr **linkinfo); 40 | static int vxlan_post(struct if_entry *entry, struct list *netns_list); 41 | 42 | static struct if_handler h_vxlan = { 43 | .driver = "vxlan", 44 | .private_size = sizeof(struct vxlan_priv), 45 | .netlink = vxlan_netlink, 46 | .post = vxlan_post, 47 | }; 48 | 49 | #define VXLAN_COLLECT_METADATA 1 50 | 51 | void handler_vxlan_register(void) 52 | { 53 | if_handler_register(&h_vxlan); 54 | } 55 | 56 | static int vxlan_fill_addr(struct addr **addr, int ai_family, struct nlattr *attr) 57 | { 58 | int err; 59 | 60 | if (!attr || *addr) 61 | return 0; 62 | 63 | *addr = calloc(1, sizeof(struct addr)); 64 | if (!*addr) 65 | return ENOMEM; 66 | 67 | if ((err = addr_init(*addr, ai_family, addr_max_prefix_len(ai_family), nla_read(attr)))) { 68 | free(*addr); 69 | *addr = NULL; 70 | return err; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | static int vxlan_netlink(struct if_entry *entry, struct nlattr **linkinfo) 77 | { 78 | struct nlattr **vxlaninfo; 79 | uint16_t port; 80 | struct vxlan_priv *priv; 81 | int err; 82 | 83 | priv = calloc(1, sizeof(struct vxlan_priv)); 84 | if (!priv) { 85 | err = ENOMEM; 86 | goto err; 87 | } 88 | entry->handler_private = priv; 89 | 90 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) { 91 | err = ENOENT; 92 | goto err_priv; 93 | } 94 | vxlaninfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_VXLAN_MAX); 95 | if (!vxlaninfo) { 96 | err = ENOMEM; 97 | goto err_priv; 98 | } 99 | 100 | if (vxlaninfo[IFLA_VXLAN_ID]) 101 | if_add_config(entry, "VNI", "%u", nla_read_u32(vxlaninfo[IFLA_VXLAN_ID])); 102 | 103 | if (vxlaninfo[IFLA_VXLAN_PORT]) { 104 | port = nla_read_be16(vxlaninfo[IFLA_VXLAN_PORT]); 105 | if (port != VXLAN_DEFAULT_PORT) 106 | if_add_config(entry, "port", "%u", port); 107 | } 108 | 109 | if (vxlaninfo[IFLA_VXLAN_COLLECT_METADATA]) { 110 | priv->flags |= nla_read_u8(vxlaninfo[IFLA_VXLAN_COLLECT_METADATA]) ? VXLAN_COLLECT_METADATA : 0; 111 | } 112 | 113 | if (priv->flags & VXLAN_COLLECT_METADATA) { 114 | if_add_config(entry, "mode", "external"); 115 | } else { 116 | /* These can be set in COLLECT_METADATA, but are ignored by kernel */ 117 | if ((err = vxlan_fill_addr(&priv->group, AF_INET, vxlaninfo[IFLA_VXLAN_GROUP]))) 118 | goto err_attrs; 119 | if ((err = vxlan_fill_addr(&priv->group, AF_INET6, vxlaninfo[IFLA_VXLAN_GROUP6]))) 120 | goto err_attrs; 121 | if ((err = vxlan_fill_addr(&priv->local, AF_INET, vxlaninfo[IFLA_VXLAN_LOCAL]))) 122 | goto err_attrs; 123 | if ((err = vxlan_fill_addr(&priv->local, AF_INET6, vxlaninfo[IFLA_VXLAN_LOCAL6]))) 124 | goto err_attrs; 125 | } 126 | 127 | free(vxlaninfo); 128 | return 0; 129 | 130 | err_attrs: 131 | free(vxlaninfo); 132 | err_priv: 133 | free(priv); 134 | err: 135 | return err; 136 | } 137 | 138 | static int vxlan_post(struct if_entry *entry, _unused struct list *netns_list) 139 | { 140 | struct vxlan_priv *priv; 141 | struct if_entry *ife; 142 | 143 | priv = (struct vxlan_priv *) entry->handler_private; 144 | if (priv->local) { 145 | struct netns_entry *ns = entry->link_net ? : entry->ns; 146 | 147 | if_add_config(entry, "from", "%s", priv->local->formatted); 148 | if ((ife = tunnel_find_addr(ns, priv->local))) { 149 | link_set(ife, entry); 150 | entry->flags |= IF_LINK_WEAK; 151 | } 152 | } 153 | if (priv->group) 154 | if_add_config(entry, "to", "%s", priv->group->formatted); 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /handlers/vxlan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_VXLAN_H 17 | #define _HANDLERS_VXLAN_H 18 | 19 | void handler_vxlan_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /handlers/xfrm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "xfrm.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "../addr.h" 22 | #include "../handler.h" 23 | #include "../if.h" 24 | #include "../master.h" 25 | #include "../netlink.h" 26 | #include "../tunnel.h" 27 | #include "../utils.h" 28 | 29 | #include "../compat.h" 30 | 31 | static int xfrm_netlink(struct if_entry *entry, struct nlattr **linkinfo); 32 | 33 | static struct if_handler h_xfrm = { 34 | .driver = "xfrm", 35 | .netlink = xfrm_netlink, 36 | }; 37 | 38 | void handler_xfrm_register(void) 39 | { 40 | if_handler_register(&h_xfrm); 41 | } 42 | 43 | static int xfrm_netlink(struct if_entry *entry, struct nlattr **linkinfo) 44 | { 45 | struct nlattr **xfrminfo; 46 | int if_id; 47 | 48 | if (!linkinfo || !linkinfo[IFLA_INFO_DATA]) 49 | return ENOENT; 50 | 51 | xfrminfo = nla_nested_attrs(linkinfo[IFLA_INFO_DATA], IFLA_XFRM_MAX); 52 | if (!xfrminfo) 53 | return ENOMEM; 54 | 55 | if (xfrminfo[IFLA_XFRM_IF_ID]) { 56 | if_id = nla_read_u32(xfrminfo[IFLA_XFRM_IF_ID]); 57 | if_add_config(entry, "if_id", "0x%x", if_id); 58 | } 59 | 60 | free(xfrminfo); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /handlers/xfrm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2020 Red Hat, Inc. -- Sabrina Dubroca 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _HANDLERS_XFRM_H 17 | #define _HANDLERS_XFRM_H 18 | 19 | void handler_xfrm_register(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /if.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "if.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "ethtool.h" 27 | #include "handler.h" 28 | #include "label.h" 29 | #include "list.h" 30 | #include "netlink.h" 31 | #include "netns.h" 32 | #include "utils.h" 33 | 34 | #include "compat.h" 35 | 36 | static const char *xdp_mode_name[] = { 37 | "", 38 | "driver", 39 | "generic", 40 | "offloaded", 41 | }; 42 | 43 | static int fill_if_xdp_prog(struct list *xdp_list, unsigned int mode, 44 | const struct nlattr *prog_id_nla) 45 | { 46 | struct if_xdp *entry; 47 | unsigned int prog_id = 0; 48 | 49 | if (prog_id_nla) 50 | prog_id = nla_read_u32(prog_id_nla); 51 | if (!prog_id) 52 | return 0; 53 | 54 | entry = calloc(1, sizeof(struct if_xdp)); 55 | if (!entry) 56 | return ENOMEM; 57 | entry->prog_id = prog_id; 58 | if (mode < ARRAY_SIZE(xdp_mode_name)) 59 | snprintf(entry->mode, sizeof(entry->mode), "%s", xdp_mode_name[mode]); 60 | else 61 | snprintf(entry->mode, sizeof(entry->mode), "[%u]", mode); 62 | list_append(xdp_list, node(entry)); 63 | return 0; 64 | } 65 | 66 | static int fill_if_xdp(struct list *xdp_list, struct nlattr *xdp_nla) 67 | { 68 | struct nlattr **tb; 69 | unsigned int mode = XDP_ATTACHED_NONE; 70 | int err = 0; 71 | 72 | if (!xdp_nla) 73 | /* XDP not supported */ 74 | return 0; 75 | tb = nla_nested_attrs(xdp_nla, IFLA_XDP_MAX); 76 | if (!tb) 77 | return ENOMEM; 78 | if (tb[IFLA_XDP_ATTACHED]) 79 | mode = nla_read_u8(tb[IFLA_XDP_ATTACHED]); 80 | if (mode == XDP_ATTACHED_NONE) 81 | goto out; 82 | if (mode == XDP_ATTACHED_MULTI) { 83 | if ((err = fill_if_xdp_prog(xdp_list, XDP_ATTACHED_DRV, tb[IFLA_XDP_DRV_PROG_ID])) || 84 | (err = fill_if_xdp_prog(xdp_list, XDP_ATTACHED_SKB, tb[IFLA_XDP_SKB_PROG_ID])) || 85 | (err = fill_if_xdp_prog(xdp_list, XDP_ATTACHED_HW, tb[IFLA_XDP_HW_PROG_ID]))) 86 | goto out; 87 | } else { 88 | if ((err = fill_if_xdp_prog(xdp_list, mode, tb[IFLA_XDP_PROG_ID]))) 89 | goto out; 90 | } 91 | out: 92 | free(tb); 93 | return err; 94 | } 95 | 96 | static int fill_if_link(struct if_entry *dest, struct nlmsg *msg) 97 | { 98 | struct ifinfomsg *ifi; 99 | struct nlattr **tb, **linkinfo = NULL; 100 | int err; 101 | 102 | if (nlmsg_get_hdr(msg)->nlmsg_type != RTM_NEWLINK) 103 | return ENOENT; 104 | ifi = nlmsg_get(msg, sizeof(*ifi)); 105 | if (!ifi) 106 | return ENOENT; 107 | tb = nlmsg_attrs(msg, IFLA_MAX); 108 | if (!tb) 109 | return ENOMEM; 110 | if (!tb[IFLA_IFNAME]) { 111 | err = ENOENT; 112 | goto out; 113 | } 114 | dest->if_index = ifi->ifi_index; 115 | dest->if_name = strdup(nla_read_str(tb[IFLA_IFNAME])); 116 | if (!dest->if_name) { 117 | err = ENOMEM; 118 | goto err_ifname; 119 | } 120 | if (ifi->ifi_flags & IFF_UP) { 121 | dest->flags |= IF_UP; 122 | if (ifi->ifi_flags & IFF_RUNNING) 123 | dest->flags |= IF_HAS_LINK; 124 | } 125 | if (tb[IFLA_MASTER]) 126 | dest->master_index = nla_read_u32(tb[IFLA_MASTER]); 127 | if (tb[IFLA_LINK]) 128 | dest->link_index = nla_read_u32(tb[IFLA_LINK]); 129 | if (tb[IFLA_LINK_NETNSID]) 130 | dest->link_netnsid = nla_read_s32(tb[IFLA_LINK_NETNSID]); 131 | if (tb[IFLA_MTU]) 132 | dest->mtu = nla_read_u32(tb[IFLA_MTU]); 133 | if (tb[IFLA_LINKINFO]) { 134 | linkinfo = nla_nested_attrs(tb[IFLA_LINKINFO], IFLA_INFO_MAX); 135 | if (!linkinfo) { 136 | err = ENOMEM; 137 | goto err_ifname; 138 | } 139 | } 140 | 141 | if (tb[IFLA_ADDRESS]) { 142 | err = mac_addr_fill_netlink(&dest->mac_addr, tb[IFLA_ADDRESS]); 143 | if (err) 144 | goto err_ifname; 145 | } 146 | 147 | if (ifi->ifi_flags & IFF_LOOPBACK) { 148 | dest->driver = strdup("loopback"); 149 | dest->flags |= IF_LOOPBACK; 150 | } else 151 | dest->driver = ethtool_driver(dest->if_name); 152 | if (!dest->driver) { 153 | /* No ethtool ops available, try IFLA_INFO_KIND */ 154 | if (tb[IFLA_LINKINFO] && linkinfo && linkinfo[IFLA_INFO_KIND]) 155 | dest->driver = strdup(RTA_DATA(linkinfo[IFLA_INFO_KIND])); 156 | } 157 | if (!dest->driver) { 158 | /* Allow the program to continue at least with generic stuff 159 | * as there may be interfaces that do not implement any of 160 | * the mechanisms for driver detection that we use */ 161 | dest->driver = strdup("unknown driver, please report a bug"); 162 | } 163 | 164 | if ((err = fill_if_xdp(&dest->xdp, tb[IFLA_XDP]))) 165 | goto err_driver; 166 | 167 | if ((err = if_handler_init(dest))) 168 | goto err_driver; 169 | 170 | if ((err = if_handler_netlink(dest, linkinfo))) 171 | if (err != ENOENT) 172 | goto err_driver; 173 | 174 | err = 0; 175 | goto out; 176 | 177 | err_driver: 178 | free(dest->driver); 179 | dest->driver = NULL; 180 | err_ifname: 181 | free(dest->if_name); 182 | dest->if_name = NULL; 183 | out: 184 | free(tb); 185 | free(linkinfo); 186 | return err; 187 | } 188 | 189 | static int fill_if_addr(struct if_entry *dest, struct nlmsg *alist) 190 | { 191 | struct if_addr *entry; 192 | struct ifaddrmsg *ifa; 193 | struct nlattr **rta_tb; 194 | int err; 195 | 196 | for_each_nlmsg(ainfo, alist) { 197 | rta_tb = NULL; 198 | err = 0; 199 | ifa = nlmsg_get(ainfo, sizeof(*ifa)); 200 | if (!ifa) 201 | return ENOENT; 202 | if (ifa->ifa_index != dest->if_index) 203 | goto skip; 204 | if (nlmsg_get_hdr(ainfo)->nlmsg_type != RTM_NEWADDR) 205 | goto skip; 206 | if (ifa->ifa_family != AF_INET && 207 | ifa->ifa_family != AF_INET6) 208 | /* only IP addresses supported (at least for now) */ 209 | goto skip; 210 | rta_tb = nlmsg_attrs(ainfo, IFA_MAX); 211 | if (!rta_tb) { 212 | err = ENOMEM; 213 | goto skip; 214 | } 215 | if (!rta_tb[IFA_LOCAL] && !rta_tb[IFA_ADDRESS]) 216 | /* don't care about broadcast and anycast adresses */ 217 | goto skip; 218 | 219 | entry = calloc(1, sizeof(struct if_addr)); 220 | if (!entry) { 221 | err = ENOMEM; 222 | goto skip; 223 | } 224 | 225 | list_append(&dest->addr, node(entry)); 226 | 227 | if (!rta_tb[IFA_LOCAL]) { 228 | rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; 229 | rta_tb[IFA_ADDRESS] = NULL; 230 | } 231 | if ((err = addr_init_netlink(&entry->addr, ifa, rta_tb[IFA_LOCAL]))) 232 | goto skip; 233 | if (rta_tb[IFA_ADDRESS] && 234 | memcmp(nla_read(rta_tb[IFA_ADDRESS]), nla_read(rta_tb[IFA_LOCAL]), 235 | ifa->ifa_family == AF_INET ? 4 : 16)) { 236 | if ((err = addr_init_netlink(&entry->peer, ifa, rta_tb[IFA_ADDRESS]))) 237 | goto skip; 238 | } 239 | skip: 240 | nlmsg_unget(ainfo, sizeof(*ifa)); 241 | if (rta_tb) 242 | free(rta_tb); 243 | if (err) 244 | return err; 245 | } 246 | return 0; 247 | } 248 | 249 | struct if_entry *if_create(void) 250 | { 251 | struct if_entry *entry; 252 | 253 | entry = calloc(1, sizeof(struct if_entry)); 254 | if (!entry) 255 | return NULL; 256 | 257 | list_init(&entry->addr); 258 | list_init(&entry->xdp); 259 | list_init(&entry->rev_master); 260 | list_init(&entry->rev_link); 261 | list_init(&entry->properties); 262 | entry->link_netnsid = -1; 263 | entry->peer_netnsid = -1; 264 | mac_addr_init(&entry->mac_addr); 265 | return entry; 266 | } 267 | 268 | int if_list(struct list *result, struct netns_entry *ns) 269 | { 270 | struct nl_handle hnd; 271 | struct nlmsg *linfo, *ainfo; 272 | struct if_entry *entry; 273 | int err; 274 | 275 | list_init(result); 276 | 277 | if ((err = rtnl_open(&hnd))) 278 | return err; 279 | err = rtnl_ifi_dump(&hnd, RTM_GETLINK, AF_UNSPEC, &linfo); 280 | if (err) 281 | goto out_close; 282 | err = rtnl_ifi_dump(&hnd, RTM_GETADDR, AF_UNSPEC, &ainfo); 283 | if (err) 284 | goto out_linfo; 285 | 286 | for_each_nlmsg(l, linfo) { 287 | entry = if_create(); 288 | if (!entry) { 289 | err = ENOMEM; 290 | goto out_ainfo; 291 | } 292 | list_append(result, node(entry)); 293 | entry->ns = ns; 294 | if ((err = fill_if_link(entry, l))) 295 | goto out_ainfo; 296 | if ((err = fill_if_addr(entry, ainfo))) 297 | goto out_ainfo; 298 | if ((err = if_handler_scan(entry))) 299 | goto out_ainfo; 300 | } 301 | err = 0; 302 | 303 | out_ainfo: 304 | nlmsg_free(ainfo); 305 | out_linfo: 306 | nlmsg_free(linfo); 307 | out_close: 308 | nl_close(&hnd); 309 | return err; 310 | } 311 | 312 | static void if_addr_destruct(struct if_addr *entry) 313 | { 314 | addr_destruct(&entry->addr); 315 | addr_destruct(&entry->peer); 316 | } 317 | 318 | static void if_list_destruct(struct if_entry *entry) 319 | { 320 | if_handler_cleanup(entry); 321 | free(entry->internal_ns); 322 | free(entry->if_name); 323 | free(entry->edge_label); 324 | free(entry->driver); 325 | free(entry->sub_driver); 326 | mac_addr_destruct(&entry->mac_addr); 327 | label_free_property(&entry->properties); 328 | list_free(&entry->xdp, NULL); 329 | list_free(&entry->addr, (destruct_f) if_addr_destruct); 330 | } 331 | 332 | void if_list_free(struct list *list) 333 | { 334 | list_free(list, (destruct_f) if_list_destruct); 335 | } 336 | 337 | int if_add_warning(struct if_entry *entry, char *fmt, ...) 338 | { 339 | va_list ap; 340 | char *warn; 341 | int err = ENOMEM; 342 | 343 | va_start(ap, fmt); 344 | entry->warnings++; 345 | if (vasprintf(&warn, fmt, ap) < 0) 346 | goto out; 347 | err = label_add(&entry->ns->warnings, "%s: %s", ifstr(entry), warn); 348 | free(warn); 349 | out: 350 | va_end(ap); 351 | return err; 352 | } 353 | -------------------------------------------------------------------------------- /if.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _IF_H 17 | #define _IF_H 18 | 19 | #include "addr.h" 20 | #include "label.h" 21 | #include "list.h" 22 | 23 | struct netns_entry; 24 | 25 | struct if_addr { 26 | struct node n; 27 | struct addr addr; 28 | struct addr peer; 29 | }; 30 | 31 | struct if_xdp { 32 | struct node n; 33 | char mode[16]; 34 | unsigned int prog_id; 35 | }; 36 | 37 | struct if_entry { 38 | struct node n; /* in netns->ifaces */ 39 | struct node rev_master_node; /* in if_entry->rev_master */ 40 | struct node rev_link_node; /* in if_entry->rev_link */ 41 | 42 | /* basic fields */ 43 | struct netns_entry *ns; 44 | char *internal_ns; 45 | unsigned int if_index; 46 | unsigned int flags; 47 | int mtu; 48 | char *if_name; 49 | char *driver; 50 | char *sub_driver; 51 | struct list properties; 52 | struct mac_addr mac_addr; 53 | struct list addr; 54 | struct list xdp; 55 | 56 | /* master relation */ 57 | unsigned int master_index; 58 | struct if_entry *master; 59 | 60 | /* peer and child relations */ 61 | struct if_entry *active_slave; 62 | unsigned int link_index; 63 | int link_netnsid; 64 | struct if_entry *link; 65 | unsigned int peer_index; 66 | int peer_netnsid; 67 | struct if_entry *peer; 68 | 69 | /* netns relation without peer/child */ 70 | struct netns_entry *link_net; 71 | 72 | /* IOV fields */ 73 | char *pci_path; 74 | char *pci_physfn_path; 75 | struct if_entry *physfn; 76 | 77 | /* handler fields */ 78 | char *edge_label; 79 | void *handler_private; 80 | int warnings; 81 | 82 | /* reverse fields needed by some frontends */ 83 | struct list rev_master; 84 | struct list rev_link; 85 | }; 86 | 87 | #define IF_LOOPBACK 1 88 | #define IF_UP 2 89 | #define IF_HAS_LINK 4 90 | #define IF_INTERNAL 8 91 | #define IF_LINK_WEAK 16 92 | #define IF_PASSIVE_SLAVE 32 93 | 94 | int if_list(struct list *result, struct netns_entry *ns); 95 | void if_list_free(struct list *list); 96 | struct if_entry *if_create(void); 97 | 98 | int if_add_warning(struct if_entry *entry, char *fmt, ...); 99 | 100 | #define IF_PROP_STATE 1 101 | #define IF_PROP_CONFIG 2 102 | 103 | #define if_add_state(entry, key, fmt, ...) label_add_property(&(entry)->properties, IF_PROP_STATE, key, fmt, ##__VA_ARGS__) 104 | #define if_add_config(entry, key, fmt, ...) label_add_property(&(entry)->properties, IF_PROP_CONFIG, key, fmt, ##__VA_ARGS__) 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /label.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "label.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "list.h" 23 | 24 | int label_add(struct list *labels, char *fmt, ...) 25 | { 26 | va_list ap; 27 | struct label *new; 28 | int err = ENOMEM; 29 | 30 | va_start(ap, fmt); 31 | new = calloc(1, sizeof(*new)); 32 | if (!new) 33 | goto out; 34 | if (vasprintf(&new->text, fmt, ap) < 0) { 35 | free(new); 36 | goto out; 37 | } 38 | 39 | err = 0; 40 | list_append(labels, node(new)); 41 | out: 42 | va_end(ap); 43 | return err; 44 | } 45 | 46 | static void label_destruct(struct label *item) 47 | { 48 | free(item->text); 49 | } 50 | 51 | void label_free(struct list *labels) 52 | { 53 | list_free(labels, (destruct_f)label_destruct); 54 | } 55 | 56 | int label_add_property(struct list *properties, int type, 57 | const char *key, const char *fmt, ...) 58 | { 59 | va_list ap; 60 | struct label_property *new; 61 | int err = ENOMEM; 62 | 63 | va_start(ap, fmt); 64 | new = calloc(1, sizeof(*new)); 65 | if (!new) 66 | goto out; 67 | 68 | new->key = strdup(key); 69 | if (!new->key) 70 | goto out_new; 71 | 72 | if (vasprintf(&new->value, fmt, ap) < 0) { 73 | goto out_key; 74 | } 75 | 76 | new->type = type; 77 | list_append(properties, node(new)); 78 | return 0; 79 | 80 | out_key: 81 | free(new->key); 82 | out_new: 83 | free(new); 84 | out: 85 | va_end(ap); 86 | return err; 87 | } 88 | 89 | static void label_property_destruct(struct label_property *prop) 90 | { 91 | free(prop->key); 92 | free(prop->value); 93 | } 94 | 95 | void label_free_property(struct list *properties) 96 | { 97 | list_free(properties, (destruct_f)label_property_destruct); 98 | } 99 | -------------------------------------------------------------------------------- /label.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _LABEL_H 17 | #define _LABEL_H 18 | 19 | #include "list.h" 20 | 21 | struct label { 22 | struct node n; 23 | char *text; 24 | }; 25 | 26 | struct label_property { 27 | struct node n; 28 | int type; 29 | char *key, *value; 30 | }; 31 | 32 | int label_add(struct list *labels, char *fmt, ...); 33 | void label_free(struct list *labels); 34 | 35 | int label_add_property(struct list *properties, int type, 36 | const char *key, const char *fmt, ...); 37 | void label_free_property(struct list *properties); 38 | #define label_prop_match_mask(type, mask) (((type) & (mask)) > 0) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _LIST_H 17 | #define _LIST_H 18 | 19 | #include 20 | #include "utils.h" 21 | 22 | /* 23 | * Simply insert the node as a member of a structure to insert it into lists. 24 | */ 25 | struct node { 26 | struct node *next, *prev; 27 | }; 28 | 29 | /* 30 | * List is made from two virtual nodes in between all the other nodes are 31 | * inserted. This makes all corner cases base cases at the cost of two 32 | * always-NULL pointers extra. 33 | */ 34 | struct list { 35 | struct node head, tail; 36 | }; 37 | 38 | #define node(n) ((struct node *) (n)) 39 | #define node_next(n) ((void *)((node(n))->next)) 40 | #define node_valid(n) ((node(n))->next) 41 | 42 | #define list_head(list) ((void *)((list).head.next)) 43 | #define list_tail(list) ((void *)((list).tail.prev)) 44 | #define list_empty(list) (!(list).head.next->next) 45 | 46 | #define LIST_INITIALIZER(var) { .head = { &(var).tail, NULL }, .tail = { NULL, &(var).head } } 47 | #define DECLARE_LIST(var) struct list var = LIST_INITIALIZER(var) 48 | 49 | /* 50 | * When 'struct node' is embedded at the beginning of a structure, 51 | * the following macro can be used to iterate over the list. 52 | * 53 | * The node() macro allows easy access to the embedded node. 54 | */ 55 | #define list_for_each(n, list) for (n = list_head(list); node_valid(n); n = node_next(n)) 56 | 57 | /* 58 | * When its not possible to make the node first, lists can still be used and 59 | * iterated over, but the following macros must be used. 60 | */ 61 | #define NODE_CONTAINER(ptr, type, member) \ 62 | ((type *) SKIP_BACK(type, member, ptr)) 63 | 64 | #define list_for_each_member(n, list, member) \ 65 | for ((n) = NODE_CONTAINER(list_head(list), __typeof__(*n), member); \ 66 | node_valid(&(n)->member); \ 67 | n = NODE_CONTAINER(node_next(&(n)->member), __typeof__(*n), member)) 68 | 69 | static inline void list_init(struct list *l) 70 | { 71 | l->head.next = &l->tail; 72 | l->tail.prev = &l->head; 73 | l->head.prev = l->tail.next = NULL; 74 | } 75 | 76 | /* 77 | * *-------* *-----* 78 | * | after | \ / | z | 79 | * *-------* \ *-----* / *-----* 80 | * | n | 81 | * *-----* 82 | */ 83 | static inline void list_insert_after(struct node *after, struct node *n) 84 | { 85 | struct node *z = after->next; 86 | 87 | after->next = n; 88 | n->prev = after; 89 | n->next = z; 90 | z->prev = n; 91 | } 92 | 93 | /* 94 | * *-----* *--------* 95 | * | a | \ / | before | 96 | * *-----* \ *-----* / *--------* 97 | * | n | 98 | * *-----* 99 | */ 100 | static inline void list_insert_before(struct node *before, struct node *n) 101 | { 102 | struct node *a = before->prev; 103 | 104 | before->prev = n; 105 | n->next = before; 106 | n->prev = a; 107 | a->next = n; 108 | } 109 | 110 | static inline void list_append(struct list *l, struct node *n) 111 | { 112 | list_insert_before(&l->tail, n); 113 | } 114 | 115 | static inline void list_prepend(struct list *l, struct node *n) 116 | { 117 | list_insert_after(&l->head, n); 118 | } 119 | 120 | /* 121 | * returns void * to inhibit type-checking 122 | */ 123 | static inline void *node_remove(struct node *n) 124 | { 125 | struct node *z = n->prev; 126 | struct node *a = n->next; 127 | 128 | z->next = a; 129 | a->prev = z; 130 | n->next = NULL; 131 | n->prev = NULL; 132 | 133 | return n; 134 | } 135 | 136 | static inline void *list_pop(struct list *l) 137 | { 138 | return list_empty(*l) ? NULL : node_remove(list_tail(*l)); 139 | } 140 | 141 | /* 142 | * Provided for compatibility, until all code is transformed to lists. 143 | */ 144 | typedef void (*destruct_f)(void *); 145 | 146 | static inline void slist_free(void *list, destruct_f destruct) 147 | { 148 | struct generic_list { 149 | struct generic_list *next; 150 | } *cur, *next = list; 151 | 152 | while ((cur = next)) { 153 | next = cur->next; 154 | if (destruct) 155 | destruct(cur); 156 | free(cur); 157 | } 158 | } 159 | 160 | static inline void list_free(void *list, destruct_f destruct) 161 | { 162 | struct node *n; 163 | 164 | while ((n = list_pop(list))) { 165 | if (destruct) 166 | destruct(n); 167 | free(n); 168 | } 169 | } 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "args.h" 24 | #include "netns.h" 25 | #include "utils.h" 26 | #include "version.h" 27 | 28 | #include "frontend.h" 29 | #include "frontends/dot.h" 30 | #include "frontends/json.h" 31 | 32 | #include "handler.h" 33 | #include "handlers/bond.h" 34 | #include "handlers/bridge.h" 35 | #include "handlers/geneve.h" 36 | #include "handlers/gre.h" 37 | #include "handlers/iov.h" 38 | #include "handlers/ipxipy.h" 39 | #include "handlers/macsec.h" 40 | #include "handlers/openvswitch.h" 41 | #include "handlers/team.h" 42 | #include "handlers/veth.h" 43 | #include "handlers/vlan.h" 44 | #include "handlers/vti.h" 45 | #include "handlers/vxlan.h" 46 | #include "handlers/xfrm.h" 47 | #include "handlers/route.h" 48 | 49 | static void register_frontends(void) 50 | { 51 | frontend_init(); 52 | frontend_dot_register(); 53 | frontend_json_register(); 54 | } 55 | 56 | static void register_handlers(void) 57 | { 58 | handler_bond_register(); 59 | handler_bridge_register(); 60 | handler_geneve_register(); 61 | handler_gre_register(); 62 | handler_iov_register(); 63 | handler_ipxipy_register(); 64 | handler_macsec_register(); 65 | handler_openvswitch_register(); 66 | handler_team_register(); 67 | handler_veth_register(); 68 | handler_vlan_register(); 69 | handler_vti_register(); 70 | handler_vxlan_register(); 71 | handler_xfrm_register(); 72 | handler_route_register(); 73 | } 74 | 75 | static int print_help(_unused char *arg) 76 | { 77 | printf("Usage: plotnetcfg [OPTION]...\n\n"); 78 | arg_get_help((arg_help_handler_t)puts); 79 | return 1; 80 | } 81 | 82 | static int print_version(_unused char *arg) 83 | { 84 | printf("%s\n", VERSION); 85 | return 1; 86 | } 87 | 88 | static struct arg_option options[] = { 89 | { .long_name = "help", .short_name = 'h', 90 | .type = ARG_CALLBACK, .action.callback = print_help, 91 | .help = "print help and exit", 92 | }, 93 | { .long_name = "version", .short_name = '\0', 94 | .type = ARG_CALLBACK, .action.callback = print_version, 95 | .help = "print version and exit", 96 | }, 97 | }; 98 | 99 | static int check_caps(void) 100 | { 101 | struct __user_cap_header_struct caps_hdr; 102 | struct __user_cap_data_struct caps[2]; 103 | int err; 104 | 105 | caps_hdr.version = _LINUX_CAPABILITY_VERSION_3; 106 | caps_hdr.pid = 0; 107 | err = syscall(__NR_capget, &caps_hdr, caps); 108 | if (err == -EINVAL) { 109 | caps_hdr.version = _LINUX_CAPABILITY_VERSION_1; 110 | caps_hdr.pid = 0; 111 | err = syscall(__NR_capget, &caps_hdr, caps); 112 | } 113 | if (err < 0) 114 | return 0; 115 | /* The capabilities we're interested in fall into the first 32 bits, 116 | * thus we can just check the first array member irrespective of 117 | * whether we used v3 or v1 of the syscall. */ 118 | if (!(caps[0].effective & (1U << CAP_SYS_ADMIN)) || 119 | !(caps[0].effective & (1U << CAP_NET_ADMIN))) 120 | return 0; 121 | return 1; 122 | } 123 | 124 | int main(int argc, char **argv) 125 | { 126 | struct list netns_list; 127 | int netns_ok, err; 128 | 129 | arg_register_batch(options, ARRAY_SIZE(options)); 130 | register_frontends(); 131 | register_handlers(); 132 | if ((err = arg_parse(argc, argv))) 133 | exit(err); 134 | 135 | if (!check_caps()) { 136 | fprintf(stderr, "Must be run under root (or with enough capabilities).\n"); 137 | exit(1); 138 | } 139 | netns_ok = netns_switch_root(); 140 | if (netns_ok > 0) { 141 | fprintf(stderr, "Cannot change to the root name space: %s\n", strerror(netns_ok)); 142 | exit(1); 143 | } 144 | 145 | if ((err = global_handler_init())) { 146 | fprintf(stderr, "Initialization failed: %s\n", strerror(err)); 147 | exit(1); 148 | } 149 | if ((err = netns_fill_list(&netns_list, netns_ok == 0))) { 150 | fprintf(stderr, "ERROR: %s\n", strerror(err)); 151 | exit(1); 152 | } 153 | if ((err = frontend_output(&netns_list))) { 154 | fprintf(stderr, "Invalid output format specified.\n"); 155 | exit(1); 156 | } 157 | global_handler_cleanup(&netns_list); 158 | netns_list_free(&netns_list); 159 | frontend_cleanup(); 160 | 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /master.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Jiri Benc , 4 | * Ondrej Hlavaty 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2 9 | * of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | /* Resolves master_index to the actual interface. */ 18 | 19 | #include "if.h" 20 | #include 21 | #include 22 | #include 23 | #include "master.h" 24 | #include "match.h" 25 | #include "netns.h" 26 | 27 | int master_set(struct if_entry *master, struct if_entry *master_of) 28 | { 29 | if (master_of->master != NULL) 30 | node_remove(&master_of->rev_master_node); 31 | master_of->master = master; 32 | if (master_of->master != NULL) 33 | list_append(&master_of->master->rev_master, &master_of->rev_master_node); 34 | 35 | return 0; 36 | } 37 | 38 | int link_set(struct if_entry *link, struct if_entry *entry) 39 | { 40 | if (!entry) 41 | return 0; 42 | 43 | if (entry->link != NULL) 44 | node_remove(&entry->rev_link_node); 45 | entry->link = link; 46 | if (entry->link != NULL) 47 | list_append(&entry->link->rev_link, &entry->rev_link_node); 48 | 49 | return 0; 50 | } 51 | 52 | int peer_set(struct if_entry *first, struct if_entry *second) 53 | { 54 | if (first) { 55 | if (first->peer) 56 | first->peer->peer = NULL; 57 | first->peer = second; 58 | } 59 | 60 | if (second) { 61 | if (second->peer) 62 | second->peer->peer = NULL; 63 | second->peer = first; 64 | } 65 | return 0; 66 | } 67 | 68 | static int match_master(struct if_entry *entry, void *arg) 69 | { 70 | struct if_entry *slave = arg; 71 | 72 | if (entry->if_index != slave->master_index) 73 | return 0; 74 | if (entry->ns == slave->ns) 75 | return 2; 76 | return 1; 77 | } 78 | 79 | static int match_link(struct if_entry *entry, void *arg) 80 | { 81 | struct if_entry *slave = arg; 82 | 83 | if (entry->if_index != slave->link_index) 84 | return 0; 85 | if (entry->ns == slave->ns) 86 | return 2; 87 | return 1; 88 | } 89 | 90 | static int err_msg(struct match_desc *match, const char *type, struct if_entry *entry) 91 | { 92 | if (match_ambiguous(*match)) 93 | return if_add_warning(entry, "has a %s but failed to find it reliably", type); 94 | if (!match_found(*match)) 95 | return if_add_warning(entry, "has a %s but failed to find it", type); 96 | return 0; 97 | } 98 | 99 | static int process(struct if_entry *entry, struct list *netns_list) 100 | { 101 | int err; 102 | struct match_desc match; 103 | 104 | if (!entry->master && entry->master_index) { 105 | match_init(&match); 106 | match.netns_list = netns_list; 107 | match.exclude = entry; 108 | 109 | if ((err = match_if(&match, match_master, entry))) 110 | return err; 111 | if ((err = err_msg(&match, "master", entry))) 112 | return err; 113 | if ((err = master_set(match_found(match), entry))) 114 | return err; 115 | } 116 | if (!entry->link && entry->link_index) { 117 | match_init(&match); 118 | match.netns_list = netns_list; 119 | match.exclude = entry; 120 | 121 | if ((err = match_if(&match, match_link, entry))) 122 | return err; 123 | if ((err = err_msg(&match, "link", entry))) 124 | return err; 125 | if ((err = link_set(match_found(match), entry))) 126 | return err; 127 | } 128 | return 0; 129 | } 130 | 131 | int master_resolve(struct list *netns_list) 132 | { 133 | struct netns_entry *ns; 134 | struct if_entry *entry; 135 | int err; 136 | 137 | list_for_each(ns, *netns_list) { 138 | list_for_each(entry, ns->ifaces) { 139 | err = process(entry, netns_list); 140 | if (err) 141 | return err; 142 | } 143 | } 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /master.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Jiri Benc , 4 | * Ondrej Hlavaty 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2 9 | * of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #ifndef _MASTER_H 18 | #define _MASTER_H 19 | 20 | struct if_entry; 21 | struct netns_entry; 22 | 23 | /* 24 | * Resolves entry->master_index to entry->master. 25 | */ 26 | int master_resolve(struct list *netns_list); 27 | 28 | /* 29 | * Updates slave->master and master->rev_master appropriately. 30 | */ 31 | int master_set(struct if_entry *master, struct if_entry *slave); 32 | 33 | /* 34 | * Sets entry->link to link and adds link->rev_link entry of entry 35 | */ 36 | int link_set(struct if_entry *link, struct if_entry *entry); 37 | 38 | /* 39 | * Sets both entries peer to each other. Previous peers will have their peer set to NULL. 40 | */ 41 | int peer_set(struct if_entry *link, struct if_entry *entry); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /match.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014-2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "match.h" 17 | #include 18 | #include "if.h" 19 | #include "master.h" 20 | #include "netns.h" 21 | 22 | /* Matches only on desc->ns */ 23 | static int match_if_ns(struct match_desc *desc, match_callback_f callback, void *arg) 24 | { 25 | struct if_entry *entry; 26 | int res; 27 | 28 | list_for_each(entry, desc->ns->ifaces) { 29 | if (entry == desc->exclude) 30 | continue; 31 | res = callback(entry, arg); 32 | if (res < 0) 33 | return -res; 34 | if (res > desc->best) { 35 | desc->found = entry; 36 | desc->best = res; 37 | desc->count = 1; 38 | if (desc->mode == MM_FIRST) 39 | return 0; 40 | } else if (res == desc->best) 41 | desc->count++; 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | int match_if(struct match_desc *desc, match_callback_f callback, void *arg) 48 | { 49 | struct netns_entry *ns; 50 | int err; 51 | 52 | if (desc->netns_list) { 53 | list_for_each(ns, *desc->netns_list) { 54 | desc->ns = ns; 55 | if ((err = match_if_ns(desc, callback, arg))) 56 | return err; 57 | } 58 | desc->ns = NULL; 59 | return 0; 60 | } 61 | 62 | return match_if_ns(desc, callback, arg); 63 | } 64 | 65 | struct netns_entry *match_netnsid(int netnsid, struct netns_entry *current) 66 | { 67 | struct netns_id *ptr; 68 | 69 | list_for_each(ptr, current->ids) { 70 | if (ptr->id == netnsid) 71 | return ptr->ns; 72 | } 73 | return NULL; 74 | } 75 | 76 | struct if_entry *match_if_netnsid(unsigned int ifindex, int netnsid, 77 | struct netns_entry *current) 78 | { 79 | struct netns_entry *ptr = match_netnsid(netnsid, current); 80 | struct if_entry *entry; 81 | 82 | if (!ptr) 83 | return NULL; 84 | 85 | list_for_each(entry, ptr->ifaces) { 86 | if (entry->if_index == ifindex) 87 | return entry; 88 | } 89 | 90 | return NULL; 91 | } 92 | 93 | void match_all_netnsid(struct list *netns_list) 94 | { 95 | struct netns_entry *ns; 96 | struct if_entry *entry; 97 | 98 | list_for_each(ns, *netns_list) { 99 | list_for_each(entry, ns->ifaces) { 100 | if (entry->link_netnsid >= 0) { 101 | if (entry->link_index) 102 | link_set(match_if_netnsid(entry->link_index, 103 | entry->link_netnsid, 104 | ns), entry); 105 | else 106 | entry->link_net = match_netnsid(entry->link_netnsid, ns); 107 | } 108 | if (entry->peer_netnsid >= 0) 109 | peer_set(entry, match_if_netnsid(entry->peer_index, 110 | entry->peer_netnsid, 111 | ns)); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /match.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014-2015 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _MATCH_H 17 | #define _MATCH_H 18 | 19 | #include 20 | #include "list.h" 21 | 22 | struct if_entry; 23 | struct netns_entry; 24 | 25 | typedef int (*match_callback_f)(struct if_entry *, void *); 26 | 27 | #define MM_HEURISTIC 0 28 | #define MM_FIRST 1 29 | 30 | struct match_desc { 31 | /* Mode of searching, heuristic by default */ 32 | int mode; /* MM_* */ 33 | 34 | /* Exclude one entry from result (e.g. self when finding peer) */ 35 | struct if_entry *exclude; 36 | 37 | /* Search in netns list or one ns */ 38 | struct list *netns_list; 39 | struct netns_entry *ns; 40 | 41 | /* Internal. Use match_found and match_ambiguous functions. */ 42 | struct if_entry *found; 43 | int best, count; 44 | }; 45 | 46 | /* Find interface with various criteria. See match_desc for description. 47 | * 48 | * Compare callback: 49 | * Returns > 0 as a priority of the match, 50 | * 0 to ignore (not matched), 51 | * < 0 for error. 52 | * 53 | * The highest priority match is returned if exactly one highest priority 54 | * interface matches. 55 | * Returns 0 for success or error code > 0. 56 | * Use match_found and match_is_ambiguous macros to retrieve result. 57 | */ 58 | int match_if(struct match_desc *desc, match_callback_f callback, void *arg); 59 | 60 | #define match_found(d) ((d).best > 0 ? (d).found : NULL) 61 | #define match_ambiguous(d) ((d).best > 0 && (d).count > 1) 62 | 63 | static inline void match_init(struct match_desc *desc) 64 | { 65 | memset(desc, 0, sizeof(struct match_desc)); 66 | } 67 | 68 | /* Find interface using netnsid. */ 69 | struct if_entry *match_if_netnsid(unsigned int ifindex, int netnsid, 70 | struct netns_entry *current); 71 | 72 | void match_all_netnsid(struct list *netns_list); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015-2017 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "netlink.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "list.h" 30 | #include "utils.h" 31 | 32 | #define NLMSG_BASIC_SIZE 16384 33 | 34 | #define NL_TIMEOUT_MS 500 35 | #define NL_RETRY_COUNT 16 36 | 37 | int nl_open(struct nl_handle *hnd, int family) 38 | { 39 | int bufsize; 40 | int err; 41 | struct sockaddr_nl sa; 42 | socklen_t sa_len; 43 | 44 | hnd->fd = socket(AF_NETLINK, SOCK_RAW, family); 45 | if (hnd->fd < 0) 46 | return -errno; 47 | hnd->seq = 0; 48 | bufsize = 32768; 49 | if (setsockopt(hnd->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) < 0) 50 | goto err_out; 51 | bufsize = 1048576; 52 | if (setsockopt(hnd->fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0) 53 | goto err_out; 54 | 55 | memset(&sa, 0, sizeof(sa)); 56 | sa.nl_family = AF_NETLINK; 57 | if (bind(hnd->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) 58 | goto err_out; 59 | sa_len = sizeof(sa); 60 | if (getsockname(hnd->fd, (struct sockaddr *)&sa, &sa_len) < 0) 61 | goto err_out; 62 | hnd->pid = sa.nl_pid; 63 | return 0; 64 | 65 | err_out: 66 | err = -errno; 67 | close(hnd->fd); 68 | return err; 69 | } 70 | 71 | void nl_close(struct nl_handle *hnd) 72 | { 73 | close(hnd->fd); 74 | } 75 | 76 | static struct nlmsg *nlmsg_alloc(unsigned int size) 77 | { 78 | struct nlmsg *msg; 79 | 80 | msg = calloc(1, sizeof(*msg)); 81 | if (!msg) 82 | return NULL; 83 | msg->buf = malloc(size); 84 | if (!msg->buf) 85 | goto err_out; 86 | msg->allocated = size; 87 | return msg; 88 | 89 | err_out: 90 | free(msg); 91 | return NULL; 92 | } 93 | 94 | static int nlmsg_realloc(struct nlmsg *msg, unsigned int min_len) 95 | { 96 | unsigned int new_alloc = msg->allocated; 97 | void *new_buf; 98 | 99 | if (new_alloc >= min_len) 100 | return 0; 101 | while (new_alloc < min_len) 102 | new_alloc *= 2; 103 | new_buf = realloc(msg->buf, new_alloc); 104 | if (!new_buf) 105 | return ENOMEM; 106 | msg->buf = new_buf; 107 | msg->allocated = new_alloc; 108 | return 0; 109 | } 110 | 111 | void nlmsg_free(struct nlmsg *msg) 112 | { 113 | struct nlmsg *next; 114 | 115 | while (msg) { 116 | next = msg->next; 117 | free(msg->buf); 118 | free(msg); 119 | msg = next; 120 | } 121 | } 122 | 123 | static int nlmsg_put_raw(struct nlmsg *msg, const void *data, int len, int padding) 124 | { 125 | unsigned int new_len; 126 | int err; 127 | 128 | new_len = NLMSG_ALIGN(msg->len) + len + padding; 129 | err = nlmsg_realloc(msg, new_len); 130 | if (err) 131 | return err; 132 | /* zero out the alignment padding */ 133 | memset(msg->buf + msg->len, 0, NLMSG_ALIGN(msg->len) - msg->len); 134 | /* put the new value */ 135 | memcpy(msg->buf + NLMSG_ALIGN(msg->len), data, len); 136 | if (padding) 137 | memset(msg->buf + NLMSG_ALIGN(msg->len) + len, 0, padding); 138 | msg->len = new_len; 139 | return 0; 140 | } 141 | 142 | int nlmsg_put(struct nlmsg *msg, const void *data, int len) 143 | { 144 | int err; 145 | 146 | err = nlmsg_put_raw(msg, data, len, 0); 147 | if (err) 148 | return err; 149 | nlmsg_get_hdr(msg)->nlmsg_len = msg->len; 150 | return 0; 151 | } 152 | 153 | static int nlmsg_put_padded(struct nlmsg *msg, const void *data, int len, int size) 154 | { 155 | int err; 156 | 157 | err = nlmsg_put_raw(msg, data, len, size - len); 158 | if (err) 159 | return err; 160 | nlmsg_get_hdr(msg)->nlmsg_len = msg->len; 161 | return 0; 162 | } 163 | 164 | void *nlmsg_get(struct nlmsg *msg, int len) 165 | { 166 | void *res; 167 | 168 | if (msg->len - msg->start < len) 169 | return NULL; 170 | res = msg->buf + msg->start; 171 | msg->start += NLMSG_ALIGN(len); 172 | return res; 173 | } 174 | 175 | void nlmsg_unget(struct nlmsg *msg, int len) 176 | { 177 | len = NLMSG_ALIGN(len); 178 | assert(msg->start >= len); 179 | msg->start -= len; 180 | } 181 | 182 | struct nlmsg *nlmsg_new(int type, int flags) 183 | { 184 | struct nlmsghdr hdr = { .nlmsg_type = type, .nlmsg_flags = flags }; 185 | struct nlmsg *msg; 186 | 187 | hdr.nlmsg_flags |= NLM_F_REQUEST; 188 | msg = nlmsg_alloc(NLMSG_BASIC_SIZE); 189 | if (!msg) 190 | return NULL; 191 | if (nlmsg_put(msg, &hdr, sizeof(hdr))) { 192 | nlmsg_free(msg); 193 | return NULL; 194 | } 195 | return msg; 196 | } 197 | 198 | static void nlmsg_reset_start(struct nlmsg *msg) 199 | { 200 | msg->start = NLMSG_ALIGN(sizeof(struct nlmsghdr)); 201 | } 202 | 203 | struct nlattr **nlmsg_attrs(struct nlmsg *msg, int max) 204 | { 205 | struct nlattr **tb; 206 | 207 | tb = calloc(max + 1, sizeof(struct nlattr *)); 208 | if (!tb) 209 | return NULL; 210 | for_each_nla(a, msg) { 211 | if (a->nla_type <= max) 212 | tb[a->nla_type] = a; 213 | } 214 | return tb; 215 | } 216 | 217 | struct nlattr **nla_nested_attrs(struct nlattr *nla, int max) 218 | { 219 | struct nlattr **tb; 220 | 221 | tb = calloc(max + 1, sizeof(struct nlattr *)); 222 | if (!tb) 223 | return NULL; 224 | for_each_nla_nested(a, nla) { 225 | if (a->nla_type <= max) 226 | tb[a->nla_type] = a; 227 | } 228 | return tb; 229 | } 230 | 231 | int nla_put(struct nlmsg *msg, int type, const void *data, int len) 232 | { 233 | struct nlattr a = { .nla_len = len + sizeof(struct nlattr), 234 | .nla_type = type }; 235 | 236 | if (nlmsg_put(msg, &a, sizeof(a)) || 237 | nlmsg_put(msg, data, len)) 238 | return ENOMEM; 239 | return 0; 240 | } 241 | 242 | static int nl_send(struct nl_handle *hnd, struct iovec *iov, int iovlen) 243 | { 244 | struct sockaddr_nl sa = { 245 | .nl_family = AF_NETLINK, 246 | }; 247 | struct msghdr msg = { 248 | .msg_name = &sa, 249 | .msg_namelen = sizeof(sa), 250 | .msg_iov = iov, 251 | .msg_iovlen = iovlen, 252 | }; 253 | struct nlmsghdr *src = iov->iov_base; 254 | 255 | src->nlmsg_seq = ++hnd->seq; 256 | if (sendmsg(hnd->fd, &msg, 0) < 0) 257 | return errno; 258 | return 0; 259 | } 260 | 261 | static int nl_recv(struct nl_handle *hnd, struct nlmsg **dest, int is_dump) 262 | { 263 | struct sockaddr_nl sa = { 264 | .nl_family = AF_NETLINK, 265 | }; 266 | struct iovec iov; 267 | struct msghdr msg = { 268 | .msg_name = &sa, 269 | .msg_namelen = sizeof(sa), 270 | .msg_iov = &iov, 271 | .msg_iovlen = 1, 272 | }; 273 | char buf[16384]; 274 | int len, err; 275 | struct nlmsghdr *n; 276 | struct nlmsg *ptr = NULL; /* GCC false positive */ 277 | struct nlmsg *entry; 278 | struct pollfd pfd; 279 | 280 | *dest = NULL; 281 | pfd.fd = hnd->fd; 282 | pfd.events = POLLIN; 283 | while (1) { 284 | iov.iov_base = buf; 285 | iov.iov_len = sizeof(buf); 286 | err = poll(&pfd, 1, NL_TIMEOUT_MS); 287 | if (err < 0) { 288 | err = errno; 289 | goto err_out; 290 | } 291 | if (err == 0 || !(pfd.revents & POLLIN)) { 292 | err = ETIME; 293 | goto err_out; 294 | } 295 | len = recvmsg(hnd->fd, &msg, 0); 296 | if (len < 0) { 297 | err = errno; 298 | goto err_out; 299 | } 300 | if (!len) { 301 | err = EPIPE; 302 | goto err_out; 303 | } 304 | if (sa.nl_pid) { 305 | /* not from the kernel */ 306 | continue; 307 | } 308 | for (n = (struct nlmsghdr *)buf; NLMSG_OK(n, len); n = NLMSG_NEXT(n, len)) { 309 | if (n->nlmsg_pid != hnd->pid || n->nlmsg_seq != hnd->seq) 310 | continue; 311 | if (is_dump && n->nlmsg_type == NLMSG_DONE) 312 | return 0; 313 | if (n->nlmsg_type == NLMSG_ERROR) { 314 | struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(n); 315 | 316 | err = -nlerr->error; 317 | goto err_out; 318 | } 319 | entry = nlmsg_alloc(n->nlmsg_len); 320 | if (!entry) { 321 | err = ENOMEM; 322 | goto err_out; 323 | } 324 | if (!*dest) 325 | *dest = entry; 326 | else 327 | ptr->next = entry; 328 | ptr = entry; 329 | 330 | err = nlmsg_put_raw(entry, n, n->nlmsg_len, 0); 331 | if (err) 332 | goto err_out; 333 | nlmsg_reset_start(entry); 334 | 335 | if (!is_dump) 336 | return 0; 337 | } 338 | } 339 | err_out: 340 | nlmsg_free(*dest); 341 | *dest = NULL; 342 | return err; 343 | } 344 | 345 | static int nl_check_interrupted_dump(struct nlmsg *entry) 346 | { 347 | for (; entry; entry = entry->next) 348 | if (nlmsg_get_hdr(entry)->nlmsg_flags & NLM_F_DUMP_INTR) 349 | return 1; 350 | return 0; 351 | } 352 | 353 | int nl_exchange(struct nl_handle *hnd, struct nlmsg *src, struct nlmsg **dest) 354 | { 355 | struct iovec iov = { 356 | .iov_base = src->buf, 357 | .iov_len = src->len, 358 | }; 359 | int is_dump; 360 | int err; 361 | int retry = NL_RETRY_COUNT; 362 | 363 | is_dump = !!(nlmsg_get_hdr(src)->nlmsg_flags & NLM_F_DUMP); 364 | while (1) { 365 | if (!retry--) 366 | return EINTR; 367 | 368 | err = nl_send(hnd, &iov, 1); 369 | if (err) 370 | return err; 371 | err = nl_recv(hnd, dest, is_dump); 372 | if (err == ETIME || err == EAGAIN || err == EINTR) 373 | continue; 374 | if (err) 375 | return err; 376 | 377 | if (is_dump && nl_check_interrupted_dump(*dest)) { 378 | nlmsg_free(*dest); 379 | *dest = NULL; 380 | continue; 381 | } 382 | 383 | return 0; 384 | } 385 | } 386 | 387 | int rtnl_open(struct nl_handle *hnd) 388 | { 389 | return nl_open(hnd, NETLINK_ROUTE); 390 | } 391 | 392 | struct nlmsg *rtnlmsg_new(int type, int family, int flags, int size) 393 | { 394 | struct rtgenmsg g = { .rtgen_family = family }; 395 | struct nlmsg *res; 396 | 397 | res = nlmsg_new(type, flags); 398 | if (!res) 399 | return NULL; 400 | if (nlmsg_put_padded(res, &g, sizeof(g), size)) { 401 | nlmsg_free(res); 402 | return NULL; 403 | } 404 | return res; 405 | } 406 | 407 | int rtnl_ifi_dump(struct nl_handle *hnd, int type, int family, struct nlmsg **dest) 408 | { 409 | struct nlmsg *req; 410 | int err; 411 | 412 | req = rtnlmsg_new(type, family, NLM_F_DUMP, sizeof(struct ifinfomsg)); 413 | if (!req) 414 | return ENOMEM; 415 | err = nl_exchange(hnd, req, dest); 416 | nlmsg_free(req); 417 | return err; 418 | } 419 | 420 | int genl_open(struct nl_handle *hnd) 421 | { 422 | return nl_open(hnd, NETLINK_GENERIC); 423 | } 424 | 425 | struct nlmsg *genlmsg_new(int type, int cmd, int flags) 426 | { 427 | struct genlmsghdr g = { .cmd = cmd, .version = 1 }; 428 | struct nlmsg *res; 429 | 430 | res = nlmsg_new(type, flags); 431 | if (!res) 432 | return NULL; 433 | if (nlmsg_put(res, &g, sizeof(g))) { 434 | nlmsg_free(res); 435 | return NULL; 436 | } 437 | return res; 438 | } 439 | 440 | unsigned int genl_family_id(struct nl_handle *hnd, const char *name) 441 | { 442 | struct nlmsg *req, *resp; 443 | int res = 0; 444 | 445 | req = genlmsg_new(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0); 446 | if (!req) 447 | return 0; 448 | if (nla_put_str(req, CTRL_ATTR_FAMILY_NAME, name)) 449 | goto out_req; 450 | if (nl_exchange(hnd, req, &resp)) 451 | goto out_req; 452 | if (!nlmsg_get(resp, sizeof(struct genlmsghdr))) 453 | goto out_resp; 454 | for_each_nla(a, resp) { 455 | if (a->nla_type == CTRL_ATTR_FAMILY_ID) { 456 | res = nla_read_u16(a); 457 | break; 458 | } 459 | } 460 | 461 | out_resp: 462 | nlmsg_free(resp); 463 | out_req: 464 | nlmsg_free(req); 465 | return res; 466 | } 467 | -------------------------------------------------------------------------------- /netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015-2017 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _NETLINK_H 17 | #define _NETLINK_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | struct nl_handle { 27 | int fd; 28 | unsigned int pid; 29 | unsigned int seq; 30 | }; 31 | 32 | struct nlmsg { 33 | struct nlmsg *next; 34 | void *buf; 35 | int start; 36 | int len; 37 | int allocated; 38 | }; 39 | 40 | #define nlmsg_get_hdr(n) ((struct nlmsghdr *)(n)->buf) 41 | 42 | /* all netlink families */ 43 | 44 | int nl_open(struct nl_handle *hnd, int family); 45 | void nl_close(struct nl_handle *hnd); 46 | int nl_exchange(struct nl_handle *hnd, struct nlmsg *src, struct nlmsg **dest); 47 | 48 | struct nlmsg *nlmsg_new(int type, int flags); 49 | void nlmsg_free(struct nlmsg *msg); 50 | int nlmsg_put(struct nlmsg *msg, const void *data, int len); 51 | void *nlmsg_get(struct nlmsg *msg, int len); 52 | void nlmsg_unget(struct nlmsg *msg, int len); 53 | struct nlattr **nlmsg_attrs(struct nlmsg *msg, int max); 54 | struct nlattr **nla_nested_attrs(struct nlattr *nla, int max); 55 | 56 | #define for_each_nlmsg(iter, msg) \ 57 | for (struct nlmsg *iter = (msg); iter; iter = iter->next) 58 | 59 | int nla_put(struct nlmsg *msg, int type, const void *data, int len); 60 | 61 | static inline int nla_put_u8(struct nlmsg *msg, int type, uint8_t data) 62 | { 63 | return nla_put(msg, type, &data, sizeof(data)); 64 | } 65 | 66 | static inline int nla_put_u16(struct nlmsg *msg, int type, uint16_t data) 67 | { 68 | return nla_put(msg, type, &data, sizeof(data)); 69 | } 70 | 71 | static inline int nla_put_u32(struct nlmsg *msg, int type, uint32_t data) 72 | { 73 | return nla_put(msg, type, &data, sizeof(data)); 74 | } 75 | 76 | static inline int nla_put_s32(struct nlmsg *msg, int type, int32_t data) 77 | { 78 | return nla_put(msg, type, &data, sizeof(data)); 79 | } 80 | 81 | static inline int nla_put_str(struct nlmsg *msg, int type, const char *str) 82 | { 83 | return nla_put(msg, type, str, strlen(str) + 1); 84 | } 85 | 86 | static inline unsigned int nla_len(const struct nlattr *nla) 87 | { 88 | return nla->nla_len - sizeof(struct nlattr); 89 | } 90 | 91 | static inline const void *nla_read(const struct nlattr *nla) 92 | { 93 | return nla + 1; 94 | } 95 | 96 | static inline uint8_t nla_read_u8(const struct nlattr *nla) 97 | { 98 | return *(uint8_t *)(nla + 1); 99 | } 100 | 101 | static inline uint16_t nla_read_u16(const struct nlattr *nla) 102 | { 103 | return *(uint16_t *)(nla + 1); 104 | } 105 | 106 | static inline uint32_t nla_read_u32(const struct nlattr *nla) 107 | { 108 | return *(uint32_t *)(nla + 1); 109 | } 110 | 111 | static inline uint16_t nla_read_be16(const struct nlattr *nla) 112 | { 113 | return ntohs(*(uint16_t *)(nla + 1)); 114 | } 115 | 116 | static inline uint32_t nla_read_be32(const struct nlattr *nla) 117 | { 118 | return ntohl(*(uint32_t *)(nla + 1)); 119 | } 120 | 121 | #define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) 122 | static inline uint64_t nla_read_be64(const struct nlattr *nla) 123 | { 124 | return ntohll(*(uint64_t *)(nla + 1)); 125 | } 126 | 127 | static inline int32_t nla_read_s32(const struct nlattr *nla) 128 | { 129 | return *(int32_t *)(nla + 1); 130 | } 131 | 132 | static inline const char *nla_read_str(const struct nlattr *nla) 133 | { 134 | return (const char *)(nla + 1); 135 | } 136 | 137 | #define __nla_buf_remaining(iter, buf, len) \ 138 | ((int)(len) - ((void *)(iter) - (void *)(buf))) 139 | 140 | #define for_each_nla_buf(iter, buf, len) \ 141 | for (struct nlattr *iter = (struct nlattr *)(buf); \ 142 | __nla_buf_remaining(iter, buf, len) >= \ 143 | (int)sizeof(struct nlattr) && \ 144 | iter->nla_len >= sizeof(struct nlattr) && \ 145 | iter->nla_len <= __nla_buf_remaining(iter, buf, len); \ 146 | iter = (void *)iter + NLMSG_ALIGN(iter->nla_len)) 147 | 148 | #define for_each_nla(iter, msg) \ 149 | for_each_nla_buf(iter, (msg)->buf + (msg)->start, (msg)->len - (msg)->start) 150 | 151 | #define for_each_nla_nested(iter, nla) \ 152 | for_each_nla_buf(iter, nla_read(nla), nla_len(nla)) 153 | 154 | /* rtnetlink */ 155 | 156 | int rtnl_open(struct nl_handle *hnd); 157 | struct nlmsg *rtnlmsg_new(int type, int family, int flags, int size); 158 | int rtnl_ifi_dump(struct nl_handle *hnd, int type, int family, struct nlmsg **dest); 159 | 160 | /* genetlink */ 161 | 162 | int genl_open(struct nl_handle *hnd); 163 | struct nlmsg *genlmsg_new(int type, int cmd, int flags); 164 | unsigned int genl_family_id(struct nl_handle *hnd, const char *name); 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /netns.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "netns.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "handler.h" 31 | #include "if.h" 32 | #include "list.h" 33 | #include "master.h" 34 | #include "match.h" 35 | #include "netlink.h" 36 | #include "sysfs.h" 37 | 38 | #include "compat.h" 39 | 40 | #define NETNS_RUN_DIR "/var/run/netns" 41 | 42 | static long netns_get_kernel_id(const char *path) 43 | { 44 | char dest[PATH_MAX], *s, *endptr; 45 | ssize_t len; 46 | long result; 47 | 48 | len = readlink(path, dest, sizeof(dest)); 49 | if (len < 0) 50 | return -errno; 51 | dest[len] = 0; 52 | if (strncmp("net:[", dest, 5)) 53 | return -EINVAL; 54 | s = strrchr(dest, ']'); 55 | if (!s) 56 | return -EINVAL; 57 | *s = '\0'; 58 | s = dest + 5; 59 | result = strtol(s, &endptr, 10); 60 | if (!*s || *endptr) 61 | return -EINVAL; 62 | return result; 63 | } 64 | 65 | static struct netns_entry *netns_check_duplicate(struct list *netns_list, 66 | long int kernel_id) 67 | { 68 | struct netns_entry *ns; 69 | 70 | list_for_each(ns, *netns_list) 71 | if (ns->kernel_id == kernel_id) 72 | return ns; 73 | return NULL; 74 | } 75 | 76 | static struct netns_entry *netns_create() 77 | { 78 | struct netns_entry *ns; 79 | 80 | ns = calloc(1, sizeof(struct netns_entry)); 81 | if (!ns) 82 | return NULL; 83 | 84 | list_init(&ns->ifaces); 85 | list_init(&ns->warnings); 86 | list_init(&ns->ids); 87 | 88 | return ns; 89 | } 90 | 91 | static int netns_get_var_entry(struct netns_entry **result, 92 | struct list *netns_list, 93 | const char *name, 94 | struct list *warnings) 95 | { 96 | struct netns_entry *entry; 97 | char path[PATH_MAX]; 98 | long kernel_id; 99 | int err; 100 | 101 | *result = entry = netns_create(); 102 | if (!entry) 103 | return ENOMEM; 104 | 105 | snprintf(path, sizeof(path), "%s/%s", NETNS_RUN_DIR, name); 106 | entry->fd = open(path, O_RDONLY); 107 | if (entry->fd < 0) { 108 | label_add(warnings, "Wrong %s: %s", path, strerror(errno)); 109 | return -1; 110 | } 111 | /* to get the kernel_id, we need to switch to that ns and examine 112 | * /proc/self */ 113 | err = netns_switch(entry); 114 | if (err) { 115 | label_add(warnings, "Wrong %s: %s", path, strerror(err)); 116 | return -1; 117 | } 118 | kernel_id = netns_get_kernel_id("/proc/self/ns/net"); 119 | if (kernel_id < 0) 120 | return -kernel_id; 121 | if (netns_check_duplicate(netns_list, kernel_id)) { 122 | close(entry->fd); 123 | free(entry); 124 | return -1; 125 | } 126 | entry->kernel_id = kernel_id; 127 | entry->name = strdup(name); 128 | if (!entry->name) 129 | return ENOMEM; 130 | return netns_switch_root(); 131 | } 132 | 133 | static void netns_proc_entry_set_name(struct netns_entry *entry, 134 | const char *spid) 135 | { 136 | char path[PATH_MAX], buf[PATH_MAX]; 137 | ssize_t len; 138 | int commfd; 139 | 140 | snprintf(path, sizeof(path), "/proc/%s/comm", spid); 141 | commfd = open(path, O_RDONLY); 142 | len = -1; 143 | if (commfd >= 0) { 144 | len = read(commfd, path, sizeof(path)); 145 | if (len >= 0) { 146 | path[sizeof(path) - 1] = '\0'; 147 | if (path[len - 1] == '\n') 148 | path[len - 1] = '\0'; 149 | } 150 | close(commfd); 151 | } 152 | if (len >= 0) 153 | snprintf(buf, sizeof(buf), "PID %s (%s)", spid, path); 154 | else 155 | snprintf(buf, sizeof(buf), "PID %s", spid); 156 | entry->name = strdup(buf); 157 | if (!entry->name) 158 | entry->name = "?"; 159 | } 160 | 161 | static int netns_get_proc_entry(struct netns_entry **result, 162 | struct list *netns_list, 163 | const char *spid) 164 | { 165 | struct netns_entry *entry, *dup; 166 | char path[PATH_MAX]; 167 | pid_t pid; 168 | long kernel_id; 169 | 170 | snprintf(path, sizeof(path), "/proc/%s/ns/net", spid); 171 | kernel_id = netns_get_kernel_id(path); 172 | if (kernel_id < 0) { 173 | /* ignore entries that cannot be read */ 174 | return -1; 175 | } 176 | pid = atol(spid); 177 | dup = netns_check_duplicate(netns_list, kernel_id); 178 | if (dup) { 179 | if (dup->pid && (dup->pid > pid)) { 180 | dup->pid = pid; 181 | netns_proc_entry_set_name(dup, spid); 182 | } 183 | return -1; 184 | } 185 | 186 | *result = entry = netns_create(); 187 | if (!entry) 188 | return ENOMEM; 189 | 190 | entry->kernel_id = kernel_id; 191 | entry->pid = pid; 192 | entry->fd = open(path, O_RDONLY); 193 | if (entry->fd < 0) { 194 | /* ignore entries that cannot be read */ 195 | free(entry); 196 | return -1; 197 | } 198 | netns_proc_entry_set_name(entry, spid); 199 | return 0; 200 | } 201 | 202 | static int netns_new_list(struct list *result, int supported) 203 | { 204 | struct netns_entry *root; 205 | 206 | root = netns_create(); 207 | if (!root) 208 | return ENOMEM; 209 | if (supported) { 210 | root->kernel_id = netns_get_kernel_id("/proc/1/ns/net"); 211 | if (root->kernel_id < 0) 212 | return -root->kernel_id; 213 | root->fd = open("/proc/1/ns/net", O_RDONLY); 214 | if (root->fd < 0) 215 | return errno; 216 | } 217 | 218 | list_init(result); 219 | list_append(result, node(root)); 220 | return 0; 221 | } 222 | 223 | static int netns_add_var_list(struct list *netns_list, struct list *warnings) 224 | { 225 | struct netns_entry *entry; 226 | struct dirent *de; 227 | DIR *dir; 228 | int err; 229 | 230 | dir = opendir(NETNS_RUN_DIR); 231 | if (!dir) 232 | return 0; 233 | 234 | while ((de = readdir(dir)) != NULL) { 235 | if (!strcmp(de->d_name, ".") || 236 | !strcmp(de->d_name, "..")) 237 | continue; 238 | err = netns_get_var_entry(&entry, netns_list, de->d_name, warnings); 239 | if (err < 0) { 240 | /* duplicate entry */ 241 | continue; 242 | } 243 | if (err) 244 | return err; 245 | list_append(netns_list, node(entry)); 246 | } 247 | closedir(dir); 248 | 249 | return 0; 250 | } 251 | 252 | static int netns_add_proc_list(struct list *netns_list) 253 | { 254 | struct netns_entry *entry; 255 | struct dirent *de; 256 | DIR *dir; 257 | int err; 258 | 259 | dir = opendir("/proc"); 260 | if (!dir) 261 | return 0; 262 | 263 | while ((de = readdir(dir)) != NULL) { 264 | if (!strcmp(de->d_name, ".") || 265 | !strcmp(de->d_name, "..")) 266 | continue; 267 | if (de->d_name[0] < '0' || de->d_name[0] > '9') 268 | continue; 269 | err = netns_get_proc_entry(&entry, netns_list, de->d_name); 270 | if (err < 0) { 271 | /* duplicate entry */ 272 | continue; 273 | } 274 | if (err) 275 | return err; 276 | list_append(netns_list, node(entry)); 277 | } 278 | closedir(dir); 279 | 280 | return 0; 281 | } 282 | 283 | /* Returns -1 if netnsids are not supported. */ 284 | static int netns_get_id(struct nl_handle *hnd, struct netns_entry *entry) 285 | { 286 | struct nlmsg *req, *resp; 287 | int res = -1; 288 | 289 | req = rtnlmsg_new(RTM_GETNSID, AF_UNSPEC, 0, sizeof(struct rtgenmsg)); 290 | if (!req) 291 | return -1; 292 | if (nla_put_u32(req, NETNSA_FD, entry->fd)) 293 | goto out_req; 294 | if (nl_exchange(hnd, req, &resp)) 295 | goto out_req; 296 | if (!nlmsg_get(resp, sizeof(struct rtgenmsg))) 297 | goto out_resp; 298 | for_each_nla(a, resp) { 299 | if (a->nla_type == NETNSA_NSID) { 300 | res = nla_read_s32(a); 301 | break; 302 | } 303 | } 304 | 305 | out_resp: 306 | nlmsg_free(resp); 307 | out_req: 308 | nlmsg_free(req); 309 | return res; 310 | } 311 | 312 | /* This is best effort only, if anything fails (e.g. netnsids are not 313 | * supported by kernel), we fail back to heuristics. */ 314 | static void netns_get_all_ids(struct netns_entry *current, struct list *netns_list) 315 | { 316 | struct nl_handle hnd; 317 | struct netns_entry *entry; 318 | struct netns_id *nsid; 319 | int id; 320 | 321 | if (netns_switch(current)) 322 | return; 323 | if (rtnl_open(&hnd) < 0) 324 | return; 325 | 326 | list_for_each(entry, *netns_list) { 327 | id = netns_get_id(&hnd, entry); 328 | if (id < 0) 329 | continue; 330 | nsid = malloc(sizeof(*nsid)); 331 | if (!nsid) 332 | break; 333 | nsid->ns = entry; 334 | nsid->id = id; 335 | list_append(¤t->ids, node(nsid)); 336 | } 337 | nl_close(&hnd); 338 | } 339 | 340 | int netns_fill_list(struct list *result, int supported) 341 | { 342 | struct netns_entry *entry; 343 | int err; 344 | 345 | err = netns_new_list(result, supported); 346 | if (err) 347 | return err; 348 | if (supported) { 349 | struct netns_entry *root = list_head(*result); 350 | 351 | err = netns_add_var_list(result, &root->warnings); 352 | if (err) 353 | return err; 354 | err = netns_add_proc_list(result); 355 | if (err) 356 | return err; 357 | } 358 | 359 | if ((err = sysfs_init())) 360 | return err; 361 | 362 | list_for_each(entry, *result) { 363 | if (entry->name) { 364 | /* Do not try to switch to the root netns, as we're 365 | * already there when processing the first entry, 366 | * and netns_switch fails hard if there's no netns 367 | * support available. */ 368 | if ((err = netns_switch(entry))) 369 | return err; 370 | } 371 | if ((err = sysfs_mount(entry->name))) 372 | return err; 373 | if ((err = if_list(&entry->ifaces, entry))) 374 | return err; 375 | if ((err = netns_handler_scan(entry))) 376 | return err; 377 | sysfs_umount(); 378 | } 379 | /* Walk all net name spaces again and gather all kernel assigned 380 | * netnsids. We don't assign netnsids ourselves to prevent assigning 381 | * them needlessly - the kernel assigns only those that are really 382 | * needed while doing netlinks dumps. Note also that netnsids are 383 | * per name space and this is O(n^2). */ 384 | list_for_each(entry, *result) 385 | netns_get_all_ids(entry, result); 386 | /* And finally, resolve netnsid+ifindex to the if_entry pointers. */ 387 | match_all_netnsid(result); 388 | 389 | if ((err = master_resolve(result))) 390 | return err; 391 | if ((err = global_handler_post(result))) 392 | return err; 393 | if ((err = if_handler_post(result))) 394 | return err; 395 | return 0; 396 | } 397 | 398 | static int do_netns_switch(int fd) 399 | { 400 | if (syscall(__NR_setns, fd, CLONE_NEWNET) < 0) 401 | return errno; 402 | return 0; 403 | } 404 | 405 | int netns_switch(struct netns_entry *dest) 406 | { 407 | return do_netns_switch(dest->fd); 408 | } 409 | 410 | /* Used also to detect whether netns support is available. 411 | * Returns 0 if everything went okay, -1 if there's no netns support, 412 | * positive error code in case of an error. */ 413 | int netns_switch_root(void) 414 | { 415 | int fd, res; 416 | 417 | fd = open("/proc/1/ns/net", O_RDONLY); 418 | if (fd < 0) { 419 | res = errno; 420 | if (res == ENOENT) 421 | res = -1; 422 | return res; 423 | } 424 | res = do_netns_switch(fd); 425 | close(fd); 426 | if (res == ENOENT) 427 | return -1; 428 | return res; 429 | } 430 | 431 | static void netns_list_destruct(struct netns_entry *entry) 432 | { 433 | netns_handler_cleanup(entry); 434 | list_free(&entry->ids, NULL); 435 | if_list_free(&entry->ifaces); 436 | free(entry->name); 437 | } 438 | 439 | void netns_list_free(struct list *netns_list) 440 | { 441 | list_free(netns_list, (destruct_f)netns_list_destruct); 442 | } 443 | -------------------------------------------------------------------------------- /netns.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _NETNS_H 17 | #define _NETNS_H 18 | 19 | #include 20 | #include "if.h" 21 | #include "list.h" 22 | 23 | struct label; 24 | struct netns_entry; 25 | struct route; 26 | 27 | struct netns_id { 28 | struct node n; 29 | struct netns_entry *ns; 30 | int id; 31 | }; 32 | 33 | struct netns_entry { 34 | struct node n; 35 | struct list ifaces; 36 | struct list warnings; 37 | long kernel_id; 38 | /* name is NULL for root name space, for other name spaces it 39 | * contains human recognizable identifier */ 40 | char *name; 41 | pid_t pid; 42 | int fd; 43 | struct list ids; 44 | struct list rtables; 45 | }; 46 | 47 | int netns_fill_list(struct list *result, int supported); 48 | void netns_list_free(struct list *list); 49 | int netns_switch(struct netns_entry *dest); 50 | int netns_switch_root(void); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /plotnetcfg-json.5: -------------------------------------------------------------------------------- 1 | .TH plotnetcfg-json 5 "10 June 2015" 2 | .SH NAME 3 | plotnetcfg \- json output format 4 | .SH DESCRIPTION 5 | 6 | .SS Root object fields 7 | 8 | .TP 9 | format 10 | .I (number) 11 | Currently 2. Will be increased if incompatible changes are introduced. 12 | A tool parsing the json output should refuse any format it's not aware of. 13 | Note that adding of new fields is not considered to be an incompatible 14 | change. 15 | 16 | .TP 17 | version 18 | .I (string) 19 | Plotnetcfg version. 20 | 21 | .TP 22 | date 23 | .I (string) 24 | Time and date when the data were gathered, in 25 | .BR ctime (3) 26 | format. 27 | 28 | .TP 29 | namespaces 30 | .I (object) 31 | Associative array of name space objects. Key is the namespace id. 32 | 33 | .TP 34 | root 35 | .I (object) 36 | The id and key of root namespace. 37 | 38 | .SS Name space object fields 39 | 40 | .TP 41 | id 42 | .I (string) 43 | An arbitrary identifier, unique among other namespaces. 44 | 45 | .TP 46 | name 47 | .I (string) 48 | Name of the name space suitable for user consumption. This in general cannot 49 | be used for machine consumption, e.g. switching to the name space. The root 50 | name space has an empty name. 51 | 52 | .TP 53 | interfaces 54 | .I (object) 55 | Associative array of interface objects. Key is the interface id. 56 | 57 | .TP 58 | warnings 59 | .I (array) 60 | If present, an array of strings. Contains error messages encountered when 61 | gathering data in the given name space. 62 | 63 | .TP 64 | routes 65 | .I (array) 66 | An array of existing routing tables. 67 | 68 | .SS Interface object fields 69 | 70 | .TP 71 | id 72 | .I (string) 73 | Unique identifier of the interface. This is an arbitrary opaque string and 74 | the consumer should not make any assumption of its contents (apart of not 75 | containing null characters). It should not be displayed to the user, the 76 | sole purpose of this field is linking to other interfaces. The identifier is 77 | globally unique, it is safe to assume that interfaces with the same name in 78 | different name spaces have a different id. 79 | 80 | .TP 81 | name 82 | .I (string) 83 | User visible name of the interface. Usually (but not always) the name of the 84 | corresponding Linux interface. This is not unique between name spaces. 85 | 86 | .TP 87 | driver 88 | .I (string) 89 | The kernel module (driver) behind the interface. May be empty in some 90 | specific cases. 91 | 92 | .TP 93 | info 94 | .I (object) 95 | Contains additional information about the interface. Values are formatted 96 | strings, e.g. tunnel endpoints. The exact content is dependent on the 97 | type of the interface. 98 | 99 | .TP 100 | addresses 101 | .I (array) 102 | Array of address objects. 103 | 104 | .TP 105 | mac 106 | .I (string) 107 | Link-layer address of the interface. 108 | 109 | .TP 110 | mtu 111 | .I (number) 112 | Interface MTU. 113 | 114 | .TP 115 | link-netns 116 | .I (object) 117 | .BR id 118 | The namespace of the link for the interface. Only present for 119 | cross-net interfaces that don't have a peer or parent/child 120 | relationship. 121 | 122 | 123 | .TP 124 | type 125 | .I (string) 126 | .RS 127 | "device": normal interface. Most interfaces are of this type. 128 | .P 129 | "internal": this interface is not backed up by a Linux interface. Can be 130 | often found with Open vSwitch. 131 | .P 132 | Further types are possible with future plotnetcfg versions. Adding them will 133 | not be considered a format change. 134 | .RE 135 | 136 | .TP 137 | state 138 | .I (string) 139 | .RS 140 | "down": the interface is administratively disabled. 141 | .P 142 | "up": the interface is up and operating. 143 | .P 144 | "up_no_link": the interface is up but has no link. 145 | .P 146 | "none": state cannot be determined or is not applicable to this kind of 147 | interface. 148 | .P 149 | More states are possible to be added in future plotnetcfg versions. Adding 150 | them will not be considered a format change. 151 | .RE 152 | 153 | .TP 154 | xdp 155 | .I (array) 156 | Array of XDP programs, as xdp objects. Not present if there are no XDP 157 | programs loaded. 158 | 159 | .TP 160 | warning 161 | .I (bool) 162 | There was a problem gathering data about this interface. Details are in the 163 | name space warnings field. The purpose of this flag is for visual 164 | representation of this interface as not having complete data available. 165 | Not present if there was no error. 166 | 167 | .TP 168 | parents 169 | .I (array) 170 | Array of parent interfaces, as connection objects. Not present if there 171 | are no parents. 172 | 173 | .TP 174 | children 175 | .I (array) 176 | Array of children interfaces, as connection objects. Not present if there 177 | are no children. 178 | 179 | .TP 180 | peer 181 | .I (object) 182 | The peer interface, as a connection object. Not present if there's no peer. 183 | 184 | .SS Connection object fields 185 | 186 | .TP 187 | info 188 | .I (array) 189 | Array of strings. Contains additional information about the connection 190 | between the two interfaces, formatted. May be an empty array. 191 | 192 | .TP 193 | target 194 | .I (string) 195 | Id of the interface that is being linked to. 196 | 197 | .SS Link namespace object fields 198 | 199 | .TP 200 | info 201 | .I (array) 202 | Array of strings. Contains additional information about the connection 203 | between the interface and network namespace, formatted. May be an 204 | empty array. 205 | 206 | .TP 207 | target 208 | .I (string) 209 | Id of the network namespace that is being linked to. 210 | 211 | .SS Address object fields 212 | 213 | .TP 214 | family 215 | .I (string) 216 | Currently only "INET" or "INET6". More types will be added in the future 217 | (without considering it a format change). 218 | 219 | .TP 220 | address 221 | .I (string) 222 | Address formatted for user consumption. May include net mask. This field 223 | should be generally machine parseable. 224 | 225 | .TP 226 | peer 227 | .I (object) 228 | If present, the peer address corresponding to this address. It's of the 229 | address object type but cannot contain futher peer field. 230 | 231 | .SS Routing table object fields 232 | 233 | .TP 234 | name 235 | .I (string) 236 | The well-known name of the table, if there is such. 237 | 238 | .TP 239 | routes 240 | .I (array) 241 | An array of route objects. 242 | 243 | .SS Route object fields 244 | 245 | .TP 246 | destination 247 | .I (string) 248 | Formatted prefix of the route destination. 249 | 250 | .TP 251 | family 252 | .I (string) 253 | A family of all addresses in this route. Currently only "INET" or "INET6". 254 | 255 | .TP 256 | gateway 257 | .I (string) 258 | Formatted address of the gateway, if this is a next-hop route. 259 | 260 | .TP 261 | iif 262 | .I (string) 263 | Interface id of the input interface, if any. 264 | 265 | .TP 266 | metrics 267 | .I (object) 268 | Associative array of integers, containing all integer metrics that kernel 269 | reports, if any. Keys are one of the following: mtu, window, rtt, rttvar, 270 | sshthresh, cwnd, advmss, reordering, hoplimit, initcwnd, features, rto_min, 271 | initrwnd, quickack. 272 | 273 | .TP 274 | oif 275 | .I (string) 276 | Interface id of the output interface, if any. 277 | 278 | .TP 279 | priority 280 | .I (integer) 281 | Kernel reported priority of this route. 282 | 283 | .TP 284 | protocol 285 | .I (string) 286 | An origin of the route. Currently one of redirect, kernel, boot, static, gated, 287 | ra, mrt, zebra, bird, dnrouted, xorp, ntk, dhcp, mrouted or babel, but others 288 | may be added in the future. 289 | 290 | .TP 291 | scope 292 | .I (string) 293 | A scope of the route. 294 | 295 | .TP 296 | source 297 | .I (string) 298 | A formatted source prefix. 299 | 300 | .TP 301 | preferred-source 302 | .I (string) 303 | A formatted preferred source address. 304 | 305 | .TP 306 | tos 307 | .I (integer) 308 | The type of service. 309 | 310 | .TP 311 | type 312 | .I (string) 313 | Route type, one of the following: unspec, unicast, local, broadcast, anycast, 314 | multicast, blackhole, unreachable, prohibit, throw, nat. Others may be added in 315 | the future, without breaking the format. 316 | 317 | .SS Xdp object fields 318 | 319 | .TP 320 | mode 321 | .I (string) 322 | XDP mode. Currently one of the following: driver, generic, offloaded. 323 | 324 | .TP 325 | prog_id 326 | .I (integer) 327 | XDP program id. 328 | 329 | .SH SEE ALSO 330 | .BR plotnetcfg (8) 331 | 332 | .SH AUTHOR 333 | .B plotnetcfg 334 | was written and is maintained by Jiri Benc . 335 | -------------------------------------------------------------------------------- /plotnetcfg.8: -------------------------------------------------------------------------------- 1 | .TH plotnetcfg 8 "9 April 2015" 2 | .SH NAME 3 | plotnetcfg \- plot diagram of network configuration 4 | .SH SYNOPSIS 5 | .B plotnetcfg 6 | .RB [ options ] 7 | .SH DESCRIPTION 8 | .B plotnetcfg 9 | is a tool that scans networking configuration on the machine and outputs 10 | a diagram of the configuration hierarchy. 11 | 12 | By default, the output is in the 13 | .B dot 14 | language. The output can be converted by the 15 | .BR dot (1) 16 | tool from 17 | .BR graphviz (7) 18 | package to various formats such as PDF, PostScript or SVG. 19 | 20 | .B plotnetcfg 21 | is able to scan different network name spaces and figure out relations that 22 | span name spaces. It supports all kinds of network interfaces and 23 | understands specifics of VLANs, bridges, veth pairs and Open vSwitch. 24 | 25 | The tool has to be run under root or with both CAP_SYS_ADMIN and 26 | CAP_NET_ADMIN capabilities. See 27 | .BR capabilities (7) 28 | for details. 29 | 30 | .B plotnetcfg 31 | is lightweight with as few library dependencies as possible. Currently, the 32 | only dependencies are 33 | .B libc 34 | and 35 | .BR libjansson . 36 | Optionally, it can be statically linked against the latter which allows the 37 | .B plotnetcfg 38 | binary to be copied to a target machine and run there (provided it has the 39 | same or newer version of 40 | .B glibc 41 | as 42 | .B plotnetcfg 43 | was compiled against). 44 | 45 | See EXAMPLES for typical 46 | .B plotnetcfg 47 | usage. 48 | 49 | .SH OPTIONS 50 | .TP 51 | \fB-f\fr, \fB--format\fR=\fIFORMAT\fR 52 | Set output format. When not specified, 53 | .B dot 54 | is used. Can be specified multiple times with 55 | .B --output. 56 | Order in which multiple outputs are processed is undefined. All the outputs 57 | contain the same snapshot of network configuration state. 58 | .TP 59 | \fB-o\fr, \fB--output\fR=\fIFILE\fR 60 | Send output to 61 | .I FILE 62 | in the last format preceding this option. Can be specified multiple times. When 63 | no file is given, send output to standard output. Every \fB--output\fR starts 64 | a new output file, for which output specific options apply. 65 | .TP 66 | \fB-C\fr, \fB--config-only\fR 67 | Output specific. Prints just configuration of the network topology, no state 68 | information. 69 | .TP 70 | \fB-F\fr, \fB--list-formats\fR 71 | Print available output formats. 72 | .TP 73 | \fB-D\fr, \fB--ovs-db\fR=\fIPATH\fR 74 | Path to the socket used to communicate with Open vSwitch database server. 75 | Only UNIX sockets are supported. The default is 76 | .BR /var/run/openvswitch/db.sock . 77 | .TP 78 | \fB-h\fR, \fB--help\fR 79 | Print short help and exit. 80 | .TP 81 | \fB--version\fR 82 | Print program version and exit. 83 | 84 | .SH EXAMPLES 85 | Display network configuration of the local machine: 86 | .RS 87 | .B plotnetcfg | dot -Tpdf | okular - 88 | .RE 89 | 90 | Save network configuration to a file for later analysis: 91 | .RS 92 | .B plotnetcfg > 93 | .I file 94 | .RE 95 | 96 | More complex output setting: 97 | .RS 98 | .BI "plotnetcfg -f json -o " "json.js" " -o " "json-config.js" " --config-only -f dot -o " "dot1.gv" 99 | .B -o - | dot -Tpdf > 100 | .I network.pdf 101 | .RE 102 | 103 | 104 | Create PDF with network configuration of a remote machine: 105 | .RS 106 | .B scp /usr/sbin/plotnetcfg 107 | .BI root@ target : 108 | .br 109 | .B ssh 110 | .BI root@ target 111 | .B ./plotnetcfg | dot -Tpdf > 112 | .I file.pdf 113 | 114 | .SH SEE ALSO 115 | .BR dot (1), 116 | .BR graphviz (7), 117 | .BR plotnetcfg-json (5) 118 | 119 | .SH AUTHOR 120 | .B plotnetcfg 121 | was written and is maintained by Jiri Benc . 122 | -------------------------------------------------------------------------------- /route.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg; a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat; Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License; or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful; 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "route.h" 17 | #include 18 | #include "compat.h" 19 | 20 | static const char *route_unknown(int num) 21 | { 22 | static char buf [64]; 23 | snprintf(buf, sizeof(buf), "unknown (%u)", num); 24 | return buf; 25 | } 26 | 27 | const char *route_metric(int metric) 28 | { 29 | switch (metric) { 30 | case RTAX_MTU: return "mtu"; 31 | case RTAX_WINDOW: return "window"; 32 | case RTAX_RTT: return "rtt"; 33 | case RTAX_RTTVAR: return "rttvar"; 34 | case RTAX_SSTHRESH: return "ssthresh"; 35 | case RTAX_CWND: return "cwnd"; 36 | case RTAX_ADVMSS: return "advmss"; 37 | case RTAX_REORDERING: return "reordering"; 38 | case RTAX_HOPLIMIT: return "hoplimit"; 39 | case RTAX_INITCWND: return "initcwnd"; 40 | case RTAX_FEATURES: return "features"; 41 | case RTAX_RTO_MIN: return "rto_min"; 42 | case RTAX_INITRWND: return "initrwnd"; 43 | case RTAX_QUICKACK: return "quickack"; 44 | case RTAX_CC_ALGO: return "congctl"; 45 | } 46 | return route_unknown(metric); 47 | } 48 | 49 | const char *route_protocol(int protocol) 50 | { 51 | switch (protocol) { 52 | case RTPROT_UNSPEC: return "unspec"; 53 | case RTPROT_REDIRECT: return "redirect"; 54 | case RTPROT_KERNEL: return "kernel"; 55 | case RTPROT_BOOT: return "boot"; 56 | case RTPROT_STATIC: return "static"; 57 | case RTPROT_GATED: return "gated"; 58 | case RTPROT_RA: return "ra"; 59 | case RTPROT_MRT: return "mrt"; 60 | case RTPROT_ZEBRA: return "zebra"; 61 | case RTPROT_BIRD: return "bird"; 62 | case RTPROT_DNROUTED: return "dnrouted"; 63 | case RTPROT_XORP: return "xorp"; 64 | case RTPROT_NTK: return "ntk"; 65 | case RTPROT_DHCP: return "dhcp"; 66 | case RTPROT_MROUTED: return "mrouted"; 67 | case RTPROT_BABEL: return "babel"; 68 | } 69 | return route_unknown(protocol); 70 | } 71 | 72 | const char *route_scope(int scope) 73 | { 74 | switch (scope) { 75 | case RT_SCOPE_UNIVERSE: return "universe"; 76 | case RT_SCOPE_SITE: return "site"; 77 | case RT_SCOPE_LINK: return "link"; 78 | case RT_SCOPE_HOST: return "host"; 79 | } 80 | return route_unknown(scope); 81 | } 82 | 83 | const char *route_table(int table) 84 | { 85 | switch (table) { 86 | case RT_TABLE_UNSPEC: return "unspec"; 87 | case RT_TABLE_COMPAT: return "compat"; 88 | case RT_TABLE_DEFAULT: return "default"; 89 | case RT_TABLE_MAIN: return "main"; 90 | case RT_TABLE_LOCAL: return "local"; 91 | } 92 | return route_unknown(table); 93 | } 94 | 95 | const char *route_type(int type) 96 | { 97 | switch (type) { 98 | case RTN_UNSPEC: return "unspec"; 99 | case RTN_UNICAST: return "unicast"; 100 | case RTN_LOCAL: return "local"; 101 | case RTN_BROADCAST: return "broadcast"; 102 | case RTN_ANYCAST: return "anycast"; 103 | case RTN_MULTICAST: return "multicast"; 104 | case RTN_BLACKHOLE: return "blackhole"; 105 | case RTN_UNREACHABLE: return "unreachable"; 106 | case RTN_PROHIBIT: return "prohibit"; 107 | case RTN_THROW: return "throw"; 108 | case RTN_NAT: return "nat"; 109 | } 110 | return route_unknown(type); 111 | } 112 | -------------------------------------------------------------------------------- /route.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2016 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _ROUTE_H 17 | #define _ROUTE_H 18 | 19 | #include "addr.h" 20 | #include "list.h" 21 | #include 22 | #include 23 | 24 | struct if_entry; 25 | 26 | struct rtmetric { 27 | struct node n; 28 | int type, value; 29 | }; 30 | 31 | struct route { 32 | struct node n; 33 | struct if_entry *iif, *oif; 34 | struct list metrics; 35 | struct addr dst, gw, prefsrc, src; 36 | unsigned int iifindex, oifindex, priority; 37 | unsigned char table_id, tos, protocol, type, family, scope; 38 | }; 39 | 40 | struct rtable { 41 | struct node n; 42 | int id; 43 | struct list routes; 44 | }; 45 | 46 | const char *route_metric(int type); 47 | const char *route_protocol(int protocol); 48 | const char *route_scope(int scope); 49 | const char *route_table(int table); 50 | const char *route_type(int type); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /sysfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "sysfs.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define PATH "/tmp/plotnetcfg-sys-XXXXXX" 27 | #define LEN sizeof(PATH) 28 | 29 | static char sysfs_mountpoint[LEN]; 30 | long page_size; 31 | 32 | void sysfs_clean() 33 | { 34 | struct stat st; 35 | 36 | if (stat(sysfs_mountpoint, &st)) 37 | return; 38 | 39 | if (S_ISDIR(st.st_mode)) 40 | sysfs_umount(); 41 | 42 | rmdir(sysfs_mountpoint); 43 | } 44 | 45 | int sysfs_init() 46 | { 47 | strcpy(sysfs_mountpoint, PATH); 48 | if (!mkdtemp(sysfs_mountpoint)) 49 | return errno; 50 | 51 | page_size = sysconf(_SC_PAGESIZE); 52 | 53 | atexit(sysfs_clean); 54 | return 0; 55 | } 56 | 57 | int sysfs_mount(const char *name) 58 | { 59 | sysfs_umount(); 60 | if (mount(name, sysfs_mountpoint, "sysfs", 0, NULL) < 0) 61 | return errno; 62 | return 0; 63 | } 64 | 65 | void sysfs_umount() 66 | { 67 | umount2(sysfs_mountpoint, MNT_DETACH); 68 | } 69 | 70 | char *sysfs_realpath(const char *sys_path) 71 | { 72 | char *resolved; 73 | char *path; 74 | 75 | if (asprintf(&path, "%s/%s", sysfs_mountpoint, sys_path) < 0) 76 | return NULL; 77 | 78 | resolved = realpath(path, NULL); 79 | 80 | free(path); 81 | 82 | return resolved ? resolved + LEN : NULL; 83 | } 84 | 85 | ssize_t sysfs_readfile(char **dest, const char *sys_path) 86 | { 87 | ssize_t ret; 88 | char *path; 89 | int fd; 90 | 91 | *dest = NULL; 92 | 93 | if (asprintf(&path, "%s/%s", sysfs_mountpoint, sys_path) < 0) 94 | return -errno; 95 | 96 | fd = open(path, O_RDONLY); 97 | ret = -errno; 98 | free(path); 99 | if (fd < 0) 100 | return ret; 101 | 102 | *dest = malloc(page_size); 103 | if (!*dest) { 104 | ret = -ENOMEM; 105 | goto err; 106 | } 107 | 108 | ret = read(fd, *dest, page_size); 109 | if (ret <= 0) { 110 | ret = -errno; 111 | free(*dest); 112 | *dest = NULL; 113 | goto err; 114 | } 115 | 116 | /* Strip the last newline, as we usually read one line only */ 117 | (*dest)[ret - 1] = '\0'; 118 | 119 | err: 120 | close(fd); 121 | return ret; 122 | } 123 | 124 | void sysfs_free(char *path) 125 | { 126 | free(path - LEN); 127 | } 128 | -------------------------------------------------------------------------------- /sysfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2015 Red Hat, Inc. -- Ondrej Hlavaty 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _SYSFS_H 17 | #define _SYSFS_H 18 | 19 | #include 20 | 21 | int sysfs_init(); 22 | int sysfs_mount(const char *name); 23 | void sysfs_umount(); 24 | 25 | /** 26 | * Calls readlink on sys_path inside sysfs. 27 | * The buffer it returns is allocated with malloc by realpath(3) 28 | * and must be freed by sysfs_free. 29 | */ 30 | char *sysfs_realpath(const char *sys_path); 31 | void sysfs_free(char *ptr); 32 | 33 | /* 34 | * Reads file into allocated buffer, must be freed after use. 35 | */ 36 | ssize_t sysfs_readfile(char **dest, const char *sys_path); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /tunnel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "tunnel.h" 17 | #include 18 | #include 19 | #include "addr.h" 20 | #include "if.h" 21 | #include "match.h" 22 | 23 | static int match_tunnel(struct if_entry *entry, void *arg) 24 | { 25 | struct addr *data = arg; 26 | struct if_addr *addr; 27 | 28 | if (!(entry->flags & IF_UP)) 29 | return 0; 30 | list_for_each(addr, entry->addr) { 31 | if (addr->addr.family != data->family) 32 | continue; 33 | if (!memcmp(addr->addr.raw, data->raw, data->family == AF_INET ? 4 : 16)) 34 | return 1; 35 | } 36 | return 0; 37 | } 38 | 39 | struct if_entry *tunnel_find_str(struct netns_entry *ns, const char *addr) 40 | { 41 | struct addr data; 42 | char buf [16]; 43 | struct match_desc match; 44 | 45 | data.raw = buf; 46 | data.family = addr_parse_raw(data.raw, addr); 47 | if (data.family < 0) 48 | return NULL; 49 | 50 | match_init(&match); 51 | match.ns = ns; 52 | if (match_if(&match, match_tunnel, &data)) 53 | return NULL; 54 | return match_ambiguous(match) ? NULL : match_found(match); 55 | } 56 | 57 | struct if_entry *tunnel_find_addr(struct netns_entry *ns, struct addr *addr) 58 | { 59 | struct match_desc match; 60 | 61 | match_init(&match); 62 | match.ns = ns; 63 | 64 | if (match_if(&match, match_tunnel, addr)) 65 | return NULL; 66 | return match_ambiguous(match) ? NULL : match_found(match); 67 | } 68 | -------------------------------------------------------------------------------- /tunnel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _TUNNEL_H 17 | #define _TUNNEL_H 18 | 19 | struct addr; 20 | struct if_entry; 21 | struct netns_entry; 22 | 23 | struct if_entry *tunnel_find_str(struct netns_entry *ns, const char *addr); 24 | struct if_entry *tunnel_find_addr(struct netns_entry *ns, struct addr *addr); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "utils.h" 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "if.h" 22 | #include "netns.h" 23 | #include "route.h" 24 | 25 | char *ifstr(struct if_entry *entry) 26 | { 27 | static char buf[IFNAMSIZ + NAME_MAX + 2]; 28 | 29 | if (!entry->ns->name) 30 | /* root ns */ 31 | snprintf(buf, sizeof(buf), "/%s", entry->if_name); 32 | else 33 | snprintf(buf, sizeof(buf), "%s/%s", entry->ns->name, entry->if_name); 34 | return buf; 35 | } 36 | 37 | #define INTERNAL_NS_MAX (NAME_MAX) 38 | #define NETNS_MAX (NAME_MAX + 1) 39 | #define IFID_MAX (INTERNAL_NS_MAX + NETNS_MAX + IFNAMSIZ + 1) 40 | char *ifid(struct if_entry *entry) 41 | { 42 | static char buf[IFID_MAX + 1]; 43 | char *ins, *ns; 44 | 45 | ins = entry->internal_ns ? : ""; 46 | ns = nsid(entry->ns); 47 | 48 | snprintf(buf, sizeof(buf), "%s%s/%s", ns, ins, entry->if_name); 49 | return buf; 50 | } 51 | 52 | char *ifdrv(struct if_entry *entry) 53 | { 54 | static char buf[256]; 55 | 56 | if (!entry->driver) 57 | return ""; 58 | if (!entry->sub_driver) 59 | return entry->driver; 60 | 61 | snprintf(buf, sizeof(buf), "%s/%s", entry->driver, entry->sub_driver); 62 | return buf; 63 | } 64 | 65 | char *nsid(struct netns_entry *entry) 66 | { 67 | static char buf[NETNS_MAX + 1]; 68 | 69 | if (!entry->name) 70 | return "/"; 71 | 72 | snprintf(buf, sizeof(buf), "%s/", entry->name); 73 | return buf; 74 | } 75 | 76 | char *rtid(struct rtable *rt) 77 | { 78 | static char buf [32]; 79 | 80 | snprintf(buf, sizeof(buf), "%u", rt->id); 81 | return buf; 82 | } 83 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is a part of plotnetcfg, a tool to visualize network config. 3 | * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #ifndef _UTILS_H 17 | #define _UTILS_H 18 | 19 | struct if_entry; 20 | struct netns_entry; 21 | struct rtable; 22 | 23 | #define _unused __attribute__((unused)) 24 | 25 | #define OFFSETOF(s, i) ((size_t) &((s *)0)->i) 26 | #define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i))) 27 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) 28 | 29 | 30 | /* Returns static buffer. */ 31 | char *ifstr(struct if_entry *entry); 32 | char *ifid(struct if_entry *entry); 33 | char *ifdrv(struct if_entry *entry); 34 | char *nsid(struct netns_entry *entry); 35 | char *rtid(struct rtable *rt); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | v0.4.1 2 | --------------------------------------------------------------------------------