pax_global_header00006660000000000000000000000064125553723540014525gustar00rootroot0000000000000052 comment=4f5c45238ef77e5d6b88bc403432bd59de7efde9 plotnetcfg-0.4.1/000077500000000000000000000000001255537235400136745ustar00rootroot00000000000000plotnetcfg-0.4.1/.gitignore000066400000000000000000000000361255537235400156630ustar00rootroot00000000000000*.o plotnetcfg version.h tags plotnetcfg-0.4.1/COPYING000066400000000000000000000432541255537235400147370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. plotnetcfg-0.4.1/Makefile000066400000000000000000000043251255537235400153400ustar00rootroot00000000000000ifeq ($(jansson),) libs=$(shell pkg-config --libs jansson) else libs=$(jansson)/src/.libs/libjansson.a INCLUDE=-I$(jansson)/src endif CFLAGS=-W -Wall $(INCLUDE) all: check-libs plotnetcfg plotnetcfg: args.o ethtool.o frontend.o handler.o if.o label.o main.o match.o netlink.o \ netns.o tunnel.o utils.o \ handlers/bridge.o handlers/master.o handlers/openvswitch.o handlers/veth.o \ handlers/vlan.o \ frontends/dot.o frontends/json.o gcc -o $@ $+ $(libs) args.o: args.c args.h ethtool.o: ethtool.c ethtool.h frontend.o: frontend.c frontend.h args.h utils.h handler.o: handler.c handler.h if.h netns.h if.o: if.c if.h compat.h ethtool.h handler.h label.h netlink.h utils.h label.o: label.h label.c utils.h main.o: main.c args.h frontend.h handler.h netns.h utils.h version.h match.o: match.c match.h if.h netns.h netlink.o: netlink.c netlink.h utils.h netns.o: netns.c netns.h compat.h handler.h if.h match.h utils.h tunnel.o: tunnel.c tunnel.h handler.h if.h match.h netns.h utils.h tunnel.h utils.o: utils.c utils.h if.h netns.h handlers/bridge.o: handlers/bridge.c handlers/bridge.h handler.h handlers/master.o: handlers/master.c handlers/master.h handler.h match.h utils.h handlers/openvswitch.o: handlers/openvswitch.h args.h handler.h label.h match.h netlink.h tunnel.h utils.h handlers/veth.o: handlers/veth.c handlers/veth.h handler.h match.h utils.h handlers/vlan.o: handlers/vlan.c handlers/vlan.h handler.h netlink.h frontends/dot.o: frontends/dot.c frontends/dot.h frontend.h handler.h if.h label.h netns.h \ utils.h version.h frontends/json.o: frontends/json.c frontends/json.h frontend.h if.h label.h netns.h utils.h \ version.h version.h: echo "#define VERSION \"`git describe 2> /dev/null || cat version`\"" > version.h clean: rm -f version.h *.o frontends/*.o handlers/*.o plotnetcfg install: plotnetcfg install -d $(DESTDIR)/usr/sbin/ install plotnetcfg $(DESTDIR)/usr/sbin/ install -d $(DESTDIR)/usr/share/man/man8/ install -d $(DESTDIR)/usr/share/man/man5/ install -m 644 plotnetcfg.8 $(DESTDIR)/usr/share/man/man8/ install -m 644 plotnetcfg-json.5 $(DESTDIR)/usr/share/man/man5/ .PHONY: check-libs check-libs: @if [ "$(libs)" = "" ]; then \ echo "ERROR: libjansson not found."; \ exit 1; \ fi plotnetcfg-0.4.1/README000066400000000000000000000020271255537235400145550ustar00rootroot00000000000000plotnetcfg is a tool that scans networking configuration on the machine and plots a diagram of the configuration hierarchy. Design goals are: 1. Lighweight. It should have no run time dependencies. Just scp the binary to the target machine. 2. Works across net namespaces. 3. Allows multiple frontends. (Right now, only graphviz fronted is implemented.) Example usage: ssh target_machine plotnetcfg | dot -Tpdf > output.pdf Building: Jansson library is required, version 2.3 or newer: http://www.digip.org/jansson/ To statically link against Jansson, build the Jansson library and invoke in the plotnetcfg source directory: make jansson=/path/to/jansson To dynamically link against Jansson, ensure Jansson is properly installed in the system (e.g. using the devel package provided by your distribution) and invoke: make Bugs: Report bugs to jbenc@redhat.com. Patches are welcome. Contains code from iproute2, originally licensed under GPLv2 or later, http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 plotnetcfg-0.4.1/args.c000066400000000000000000000100041255537235400147670ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include "args.h" static struct arg_option *options = NULL; static struct arg_option *options_tail = NULL; static int options_count = 0; void arg_register(struct arg_option *opt) { options_count++; opt->next = NULL; if (!options) { options = options_tail = opt; return; } options_tail->next = opt; options_tail = opt; } void arg_register_batch(struct arg_option *opt, int count) { int i; for (i = 0; i < count; i++) arg_register(opt + i); } int arg_parse(int argc, char **argv) { struct option *glong; char *gshort; struct arg_option *opt; int glong_index, gshort_index, i; int res = 0; /* construct parameter lists for getopt */ glong = calloc(options_count + 1, sizeof(*glong)); gshort = calloc(options_count * 3 + 1, 1); if (!glong || !gshort) return 1; glong_index = gshort_index = 0; for (opt = options; opt; opt = opt->next) { if (opt->long_name) { glong[glong_index].name = opt->long_name; glong[glong_index].has_arg = opt->has_arg; glong_index++; } if (opt->short_name) { gshort[gshort_index++] = opt->short_name; for (i = 0; i < opt->has_arg; i++) gshort[gshort_index++] = ':'; } } while (1) { gshort_index = getopt_long(argc, argv, gshort, glong, &glong_index); if (gshort_index < 0) break; if (gshort_index == '?' || gshort_index == ':') { res = 1; break; } /* find the respective arg_option */ if (!gshort_index) { opt = options; for (i = 0; i < glong_index; i++) opt = opt->next; } else { for (opt = options; opt; opt = opt->next) if (opt->short_name == gshort_index) break; assert(opt); } if (optarg) { switch (opt->type) { case ARG_NONE: assert(0); break; case ARG_INT: *opt->action.int_var = atoi(optarg); break; case ARG_CHAR: *opt->action.char_var = strdup(optarg); break; case ARG_CALLBACK: /* will be handled below */ break; } } if (opt->type == ARG_CALLBACK) { res = opt->action.callback(optarg); if (res) break; } } free(gshort); free(glong); return res; } static int str_append(char *dest, const char *src, int size, int col) { int len, i; if (!size) return 0; len = strlen(dest); dest += len; i = 0; while (i < size - 1 && len + i < col) { *dest++ = ' '; i++; } while (i < size - 1 && *src) { *dest++ = *src++; i++; } *dest = '\0'; return i; } #define HELP_BUF_SIZE 512 void arg_get_help(arg_help_handler_t handler) { struct arg_option *opt; char *buf; int size; buf = malloc(HELP_BUF_SIZE); if (!buf) return; for (opt = options; opt; opt = opt->next) { *buf = '\0'; size = HELP_BUF_SIZE; if (opt->short_name) { buf[0] = '-'; buf[1] = opt->short_name; buf[2] = '\0'; size -= 2; if (opt->has_arg && !opt->long_name) { if (opt->has_arg == 1) size -= str_append(buf, " ARG", size, 0); else size -= str_append(buf, " [ARG]", size, 0); } } if (opt->long_name) { if (opt->short_name) size -= str_append(buf, ", ", size, 0); size -= str_append(buf, "--", size, 4); size -= str_append(buf, opt->long_name, 16, 0); if (opt->has_arg == 1) size -= str_append(buf, "=ARG", size, 0); else if (opt->has_arg == 2) size -= str_append(buf, "[=ARG]", size, 0); } size -= str_append(buf, opt->help, size, 26); handler(buf); } free(buf); } plotnetcfg-0.4.1/args.h000066400000000000000000000025521255537235400150050ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _ARGS_H #define _ARGS_H enum arg_type { ARG_NONE, ARG_INT, ARG_CHAR, ARG_CALLBACK, }; union arg_action { void *void_var; int *int_var; char **char_var; /* callback returns 0 in case of success, >0 in case of error */ int (*callback)(char *arg); }; struct arg_option { struct arg_option *next; const char *long_name; char short_name; int has_arg; /* see getopt(3) for possible values */ enum arg_type type; union arg_action action; const char *help; }; void arg_register(struct arg_option *opt); void arg_register_batch(struct arg_option *opt, int count); int arg_parse(int argc, char **argv); typedef void (*arg_help_handler_t)(const char *help); void arg_get_help(arg_help_handler_t handler); #endif plotnetcfg-0.4.1/compat.h000066400000000000000000000025121255537235400153300ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _COMPAT_H #define _COMPAT_H #define IFLA_LINK_NETNSID 37 #if IFLA_MAX < IFLA_LINK_NETNSID #undef IFLA_MAX #define IFLA_MAX IFLA_LINK_NETNSID #endif #define NETNSA_NSID 1 #define NETNSA_FD 3 #if NETNSA_MAX < NETNSA_FD #undef NETNSA_MAX #define NETNSA_MAX NETNSA_FD #endif #ifndef RTM_GETNSID #define RTM_GETNSID 90 #endif #if RTM_MAX < RTM_GETNSID #undef RTM_MAX #define RTM_MAX (((RTM_GETNSID + 4) & ~3) - 1) #endif #ifndef NETNS_RTA #define NETNS_RTA(r) \ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) #endif #define OVS_VPORT_FAMILY "ovs_vport" #define OVS_VPORT_CMD_GET 3 #define OVS_VPORT_ATTR_NAME 3 struct ovs_header { int dp_ifindex; }; #endif plotnetcfg-0.4.1/ethtool.c000066400000000000000000000052301255537235400155160ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "ethtool.h" #include static int ethtool_ioctl(const char *ifname, void *data) { struct ifreq ifr; int fd, err = 0; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return errno; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ifname); ifr.ifr_data = data; if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) err = errno; close(fd); return err; } char *ethtool_driver(const char *ifname) { struct ethtool_drvinfo info; memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GDRVINFO; if (ethtool_ioctl(ifname, &info)) return NULL; return strdup(info.driver); } unsigned int ethtool_veth_peer(const char *ifname) { struct ethtool_drvinfo info; struct ethtool_gstrings *strs; struct ethtool_stats *stats; unsigned int i, res = 0; memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GDRVINFO; if (ethtool_ioctl(ifname, &info)) return 0; if (!info.n_stats) return 0; strs = malloc(sizeof(struct ethtool_gstrings) + info.n_stats * ETH_GSTRING_LEN); if (!strs) return 0; memset(strs, 0, sizeof(struct ethtool_gstrings)); strs->cmd = ETHTOOL_GSTRINGS; strs->string_set = ETH_SS_STATS; strs->len = info.n_stats; if (ethtool_ioctl(ifname, strs)) goto fail_strs; if (strs->len != info.n_stats) goto fail_strs; stats = malloc(sizeof(struct ethtool_stats) + info.n_stats * sizeof(__u64)); if (!stats) goto fail_strs; memset(stats, 0, sizeof(struct ethtool_stats)); stats->cmd = ETHTOOL_GSTATS; stats->n_stats = info.n_stats; if (ethtool_ioctl(ifname, stats)) goto fail_stats; if (stats->n_stats != info.n_stats) goto fail_stats; for (i = 0; i < info.n_stats; i++) { if (!strcmp((char *)strs->data + i * ETH_GSTRING_LEN, "peer_ifindex")) { res = stats->data[i]; break; } } fail_stats: free(stats); fail_strs: free(strs); return res; } plotnetcfg-0.4.1/ethtool.h000066400000000000000000000014331255537235400155240ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _ETHTOOL_H #define _ETHTOOL_H char *ethtool_driver(const char *ifname); unsigned int ethtool_veth_peer(const char *ifname); #endif plotnetcfg-0.4.1/frontend.c000066400000000000000000000036201255537235400156600ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "args.h" #include "utils.h" #include "frontend.h" static struct frontend *frontends = NULL; static struct frontend *frontends_tail = NULL; static char *format = "dot"; static int print_formats(_unused char *arg) { struct frontend *f; printf("Available output formats:\n"); for (f = frontends; f; f = f->next) printf("\t%s - %s\n", f->format, f->desc); return 1; } static struct arg_option options[] = { { .long_name = "format", .short_name = 'f', .has_arg = 1, .type = ARG_CHAR, .action.char_var = &format, .help = "output format (dot by default)", }, { .long_name = "list-formats", .short_name = 'F', .type = ARG_CALLBACK, .action.callback = print_formats, .help = "list available output formats", }, }; void frontend_init(void) { arg_register_batch(options, ARRAY_SIZE(options)); } void frontend_register(struct frontend *f) { f->next = NULL; if (!frontends) { frontends = frontends_tail = f; return; } frontends_tail->next = f; frontends_tail = f; } int frontend_output(struct netns_entry *root) { struct frontend *f; for (f = frontends; f; f = f->next) { if (!strcmp(f->format, format)) { f->output(root); return 0; } } return EINVAL; } plotnetcfg-0.4.1/frontend.h000066400000000000000000000017101255537235400156630ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _FRONTEND_H #define _FRONTEND_H #include "netns.h" struct frontend { struct frontend *next; const char *format; const char *desc; void (*output)(struct netns_entry *root); }; void frontend_init(void); void frontend_register(struct frontend *f); int frontend_output(struct netns_entry *root); #endif plotnetcfg-0.4.1/frontends/000077500000000000000000000000001255537235400156765ustar00rootroot00000000000000plotnetcfg-0.4.1/frontends/dot.c000066400000000000000000000076641255537235400166450ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "../frontend.h" #include "../handler.h" #include "../if.h" #include "../label.h" #include "../netns.h" #include "../utils.h" #include "../version.h" #include "dot.h" static void output_label(struct label *list) { struct label *ptr; for (ptr = list; ptr; ptr = ptr->next) printf("\\n%s", ptr->text); } static void output_addresses(struct if_addr_entry *list) { struct if_addr_entry *ptr; for (ptr = list; ptr; ptr = ptr->next) { printf("\\n%s", ptr->addr.formatted); if (ptr->peer.formatted) printf(" peer %s", ptr->peer.formatted); } } static void output_mtu(struct if_entry *ptr) { if (ptr->mtu && ptr->mtu != 1500 && !(ptr->flags & IF_LOOPBACK)) printf("\\nMTU %d", ptr->mtu); } static void output_ifaces_pass1(struct if_entry *list) { struct if_entry *ptr; for (ptr = list; ptr; ptr = ptr->next) { printf("\"%s\" [label=\"%s", ifid(ptr), ptr->if_name); if (ptr->driver) printf(" (%s)", ptr->driver); output_label(ptr->label); output_mtu(ptr); output_addresses(ptr->addr); printf("\""); if (ptr->flags & IF_INTERNAL) printf(",style=dotted"); else if (!(ptr->flags & IF_UP)) printf(",style=filled,fillcolor=\"grey\""); else if (!(ptr->flags & IF_HAS_LINK)) printf(",style=filled,fillcolor=\"pink\""); else printf(",style=filled,fillcolor=\"darkolivegreen1\""); if (ptr->warnings) printf(",color=\"red\""); printf("]\n"); } } static void output_ifaces_pass2(struct if_entry *list) { struct if_entry *ptr; for (ptr = list; ptr; ptr = ptr->next) { if (ptr->master) { printf("\"%s\" -> ", ifid(ptr)); printf("\"%s\"", ifid(ptr->master)); if (ptr->edge_label && !ptr->link) printf(" [label=\"%s\"]", ptr->edge_label); printf("\n"); } if (ptr->link) { printf("\"%s\" -> ", ifid(ptr->link)); printf("\"%s\"", ifid(ptr)); if (ptr->edge_label) printf(" [label=\"%s\"]", ptr->edge_label); printf("\n"); } if (ptr->peer && (((unsigned long)ptr > (unsigned long)ptr->peer) || !ptr->peer->peer)) { printf("\"%s\" -> ", ifid(ptr)); printf("\"%s\" [dir=none%s]\n", ifid(ptr->peer), ptr->flags & IF_PEER_WEAK ? ",style=dotted" : ""); } } } static void output_warnings(struct netns_entry *root) { struct netns_entry *ns; int was_label = 0; for (ns = root; ns; ns = ns->next) { if (ns->warnings) { if (!was_label) printf("label=\""); was_label = 1; output_label(ns->warnings); } } if (was_label) { printf("\"\n"); printf("fontcolor=\"red\"\n"); } } static void dot_output(struct netns_entry *root) { struct netns_entry *ns; time_t cur; time(&cur); printf("// generated by plotnetcfg " VERSION " on %s", ctime(&cur)); printf("digraph {\nnode [shape=box]\n"); for (ns = root; ns; ns = ns->next) { if (ns->name) { printf("subgraph \"cluster_%s\" {\n", ns->name); printf("label=\"%s\"\n", ns->name); printf("fontcolor=\"black\"\n"); } output_ifaces_pass1(ns->ifaces); if (ns->name) printf("}\n"); } for (ns = root; ns; ns = ns->next) { output_ifaces_pass2(ns->ifaces); } output_warnings(root); printf("}\n"); } static struct frontend fe_dot = { .format = "dot", .desc = "dot language (suitable for graphviz)", .output = dot_output, }; void frontend_dot_register(void) { frontend_register(&fe_dot); } plotnetcfg-0.4.1/frontends/dot.h000066400000000000000000000013531255537235400166370ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _FRONTENDS_DOT_H #define _FRONTENDS_DOT_H void frontend_dot_register(void); #endif plotnetcfg-0.4.1/frontends/json.c000066400000000000000000000121271255537235400170160ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "../frontend.h" #include "../if.h" #include "../label.h" #include "../netns.h" #include "../utils.h" #include "../version.h" #include "json.h" static json_t *label_to_array(struct label *entry) { json_t *arr; arr = json_array(); while (entry) { json_array_append_new(arr, json_string(entry->text)); entry = entry->next; } return arr; } static json_t *address_to_obj(struct addr *addr) { json_t *obj; char *s; obj = json_object(); switch (addr->family) { case AF_INET: s = "INET"; break; case AF_INET6: s = "INET6"; break; default: /* should not happen */ s = "unknown"; } json_object_set_new(obj, "family", json_string(s)); json_object_set_new(obj, "address", json_string(addr->formatted)); return obj; } static json_t *addresses_to_array(struct if_addr_entry *entry) { json_t *arr, *addr; arr = json_array(); while (entry) { addr = address_to_obj(&entry->addr); if (entry->peer.formatted) json_object_set_new(addr, "peer", address_to_obj(&entry->peer)); json_array_append_new(arr, addr); entry = entry->next; } return arr; } static json_t *connection(struct if_entry *target, char *edge_label) { json_t *obj, *arr; obj = json_object(); json_object_set_new(obj, "target", json_string(ifid(target))); arr = json_array(); if (edge_label) json_array_append_new(arr, json_string(edge_label)); json_object_set_new(obj, "info", arr); return obj; } static json_t *interfaces_to_array(struct if_entry *entry) { struct if_list_entry *iflist; json_t *ifarr, *ifobj, *children; char *s; ifarr = json_array(); while (entry) { ifobj = json_object(); json_object_set_new(ifobj, "id", json_string(ifid(entry))); json_object_set_new(ifobj, "name", json_string(entry->if_name)); json_object_set_new(ifobj, "driver", json_string(entry->driver ? entry->driver : "")); json_object_set_new(ifobj, "info", label_to_array(entry->label)); json_object_set_new(ifobj, "addresses", addresses_to_array(entry->addr)); json_object_set_new(ifobj, "mtu", json_integer(entry->mtu)); json_object_set_new(ifobj, "type", json_string(entry->flags & IF_INTERNAL ? "internal" : "device")); if (entry->flags & IF_INTERNAL) s = "none"; else if (!(entry->flags & IF_UP)) s = "down"; else if (!(entry->flags & IF_HAS_LINK)) s = "up_no_link"; else s = "up"; json_object_set_new(ifobj, "state", json_string(s)); if (entry->warnings) json_object_set_new(ifobj, "warning", json_true()); if (entry->master) json_object_set_new(ifobj, "parent", connection(entry->master, entry->link ? NULL : entry->edge_label)); else if (entry->rev_link) json_object_set_new(ifobj, "parent", connection(entry->rev_link, entry->rev_link->edge_label)); children = json_array(); if (entry->link) json_array_append_new(children, connection(entry->link, entry->edge_label)); if (entry->rev_master) { for (iflist = entry->rev_master; iflist; iflist = iflist->next) json_array_append_new(children, connection(iflist->entry, iflist->entry->link ? NULL : iflist->entry->edge_label)); } if (json_array_size(children)) json_object_set(ifobj, "children", children); json_decref(children); if (entry->peer) json_object_set(ifobj, "peer", connection(entry->peer, NULL)); json_array_append_new(ifarr, ifobj); entry = entry->next; } return ifarr; } static void json_output(struct netns_entry *root) { struct netns_entry *entry; json_t *output, *ns_list, *ns; time_t cur; time(&cur); output = json_object(); json_object_set_new(output, "format", json_integer(1)); json_object_set_new(output, "version", json_string(VERSION)); json_object_set_new(output, "date", json_string(ctime(&cur))); ns_list = json_array(); for (entry = root; entry; entry = entry->next) { ns = json_object(); json_object_set_new(ns, "name", json_string(entry->name ? entry->name : "")); json_object_set_new(ns, "interfaces", interfaces_to_array(entry->ifaces)); if (entry->warnings) json_object_set_new(ns, "warnings", label_to_array(entry->warnings)); json_array_append_new(ns_list, ns); } json_object_set_new(output, "namespaces", ns_list); json_dumpf(output, stdout, 0); json_decref(output); } static struct frontend fe_json = { .format = "json", .desc = "JSON, see plotnetcfg-json(5)", .output = json_output, }; void frontend_json_register(void) { frontend_register(&fe_json); } plotnetcfg-0.4.1/frontends/json.h000066400000000000000000000013561255537235400170250ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _FRONTENDS_JSON_H #define _FRONTENDS_JSON_H void frontend_json_register(void); #endif plotnetcfg-0.4.1/handler.c000066400000000000000000000064471255537235400154700ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include "netns.h" #include "if.h" #include "handler.h" static struct handler *handlers = NULL; static struct handler *handlers_tail = NULL; static struct global_handler *ghandlers = NULL; static struct global_handler *ghandlers_tail = NULL; void handler_register(struct handler *h) { h->next = NULL; if (!handlers) { handlers = handlers_tail = h; return; } handlers_tail->next = h; handlers_tail = h; } static int driver_match(struct handler *h, struct if_entry *e) { return !h->driver || (e->driver && !strcmp(h->driver, e->driver)); } #define handler_err_loop(err, method, entry, ...) \ { \ struct handler *ptr; \ \ for (ptr = handlers; ptr; ptr = ptr->next) { \ if (!ptr->method || !driver_match(ptr, entry)) \ continue; \ err = ptr->method(entry, ##__VA_ARGS__); \ if (err) \ break; \ } \ } #define handler_loop(method, entry, ...) \ { \ struct handler *ptr; \ \ for (ptr = handlers; ptr; ptr = ptr->next) { \ if (!ptr->method || !driver_match(ptr, entry)) \ continue; \ ptr->method(entry, ##__VA_ARGS__); \ } \ } int handler_netlink(struct if_entry *entry, struct rtattr **tb) { int err = 0; handler_err_loop(err, netlink, entry, tb); return err; } int handler_scan(struct if_entry *entry) { int err = 0; handler_err_loop(err, scan, entry); return err; } int handler_post(struct netns_entry *root) { struct netns_entry *ns; struct if_entry *entry; int err; for (ns = root; ns; ns = ns->next) { for (entry = ns->ifaces; entry; entry = entry->next) { handler_err_loop(err, post, entry, root); if (err) return err; } } return 0; } void handler_cleanup(struct if_entry *entry) { handler_loop(cleanup, entry); } void handler_generic_cleanup(struct if_entry *entry) { free(entry->handler_private); } #define ghandler_loop(method, ...) \ { \ struct global_handler *ptr; \ \ for (ptr = ghandlers; ptr; ptr = ptr->next) { \ if (!ptr->method) \ continue; \ ptr->method(__VA_ARGS__); \ } \ } void global_handler_register(struct global_handler *h) { h->next = NULL; if (!ghandlers) { ghandlers = ghandlers_tail = h; return; } ghandlers_tail->next = h; ghandlers_tail = h; } void global_handler_init(void) { ghandler_loop(init); } int global_handler_post(struct netns_entry *root) { struct global_handler *h; int err; for (h = ghandlers; h; h = h->next) { if (!h->post) continue; err = h->post(root); if (err) return err; } return 0; } void global_handler_cleanup(struct netns_entry *root) { ghandler_loop(cleanup, root); } plotnetcfg-0.4.1/handler.h000066400000000000000000000044101255537235400154610ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLER_H #define _HANDLER_H #include #include #include #include #include "if.h" #include "netns.h" /* Only one handler for each driver allowed. * Generic handlers called for every interface are supported and are created * by setting driver to NULL. Generic handlers are not allowed to use * handler_private field in struct if_entry. * * The scan callback is called during interface scanning. Only actions * related to the passed single interface may be performed. * The post callback is called after all interfaces are scanned. * Inter-interface analysis is possible at this step. */ struct handler { struct handler *next; const char *driver; int (*netlink)(struct if_entry *entry, struct rtattr **tb); int (*scan)(struct if_entry *entry); int (*post)(struct if_entry *entry, struct netns_entry *root); void (*cleanup)(struct if_entry *entry); }; void handler_register(struct handler *h); int handler_netlink(struct if_entry *entry, struct rtattr **tb); int handler_scan(struct if_entry *entry); int handler_post(struct netns_entry *root); void handler_cleanup(struct if_entry *entry); /* For use as a callback. It calls free() on handler_private. */ void handler_generic_cleanup(struct if_entry *entry); struct global_handler { struct global_handler *next; void (*init)(void); int (*post)(struct netns_entry *root); void (*cleanup)(struct netns_entry *root); }; void global_handler_register(struct global_handler *h); void global_handler_init(void); int global_handler_post(struct netns_entry *root); void global_handler_cleanup(struct netns_entry *root); #endif plotnetcfg-0.4.1/handlers/000077500000000000000000000000001255537235400154745ustar00rootroot00000000000000plotnetcfg-0.4.1/handlers/bridge.c000066400000000000000000000027621255537235400171030ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include "../handler.h" #include "bridge.h" static int bridge_scan(struct if_entry *entry); static struct handler h_bridge = { .scan = bridge_scan, }; void handler_bridge_register(void) { handler_register(&h_bridge); } static int bridge_scan(struct if_entry *entry) { int fd, res; char buf[37 + IFNAMSIZ + 1]; if (entry->master_index) return 0; snprintf(buf, sizeof(buf), "/sys/class/net/%s/brport/bridge/ifindex", entry->if_name); fd = open(buf, O_RDONLY); if (fd < 0) { if (errno == ENOENT) return 0; return errno; } res = read(fd, buf, sizeof(buf)); if (res < 0) { res = errno; goto out; } if (res == 0) { res = EINVAL; goto out; } entry->master_index = atoi(buf); res = 0; out: close(fd); return res; } plotnetcfg-0.4.1/handlers/bridge.h000066400000000000000000000013611255537235400171020ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLERS_BRIDGE_H #define _HANDLERS_BRIDGE_H void handler_bridge_register(void); #endif plotnetcfg-0.4.1/handlers/master.c000066400000000000000000000047201255537235400171360ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* Resolves master_index to the actual interface. */ #include #include #include "../handler.h" #include "../match.h" #include "../utils.h" #include "master.h" static int master_post(struct netns_entry *root); static struct global_handler h_master = { .post = master_post, }; void handler_master_register(void) { global_handler_register(&h_master); } static int match_master(struct if_entry *entry, void *arg) { struct if_entry *slave = arg; if (entry->if_index != slave->master_index) return 0; if (entry->ns == slave->ns) return 2; return 1; } static int match_link(struct if_entry *entry, void *arg) { struct if_entry *slave = arg; if (entry->if_index != slave->link_index) return 0; if (entry->ns == slave->ns) return 2; return 1; } static int err_msg(int err, const char *type, struct if_entry *entry, struct if_entry *check) { if (err > 0) return err; if (err < 0) return if_add_warning(entry, "has a %s but failed to find it reliably", type); if (!check) return if_add_warning(entry, "has a %s but failed to find it", type); return 0; } static int process(struct if_entry *entry, struct netns_entry *root) { int err; if (entry->master_index) { err = match_if_heur(&entry->master, root, 1, entry, match_master, entry); if ((err = err_msg(err, "master", entry, entry->master))) return err; } if (!entry->link && entry->link_index) { err = match_if_heur(&entry->link, root, 1, entry, match_link, entry); if ((err = err_msg(err, "link", entry, entry->link))) return err; } return 0; } static int master_post(struct netns_entry *root) { struct netns_entry *ns; struct if_entry *entry; int err; for (ns = root; ns; ns = ns->next) { for (entry = ns->ifaces; entry; entry = entry->next) { err = process(entry, root); if (err) return err; } } return 0; } plotnetcfg-0.4.1/handlers/master.h000066400000000000000000000013611255537235400171410ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLERS_MASTER_H #define _HANDLERS_MASTER_H void handler_master_register(void); #endif plotnetcfg-0.4.1/handlers/openvswitch.c000066400000000000000000000427351255537235400202240ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "../args.h" #include "../handler.h" #include "../if.h" #include "../label.h" #include "../match.h" #include "../netlink.h" #include "../netns.h" #include "../tunnel.h" #include "../utils.h" #include "openvswitch.h" #include "../compat.h" #define OVS_DB_DEFAULT "/var/run/openvswitch/db.sock"; static char *db; static unsigned int vport_genl_id; struct ovs_if { struct ovs_if *next; struct ovs_port *port; struct if_entry *link; char *name; char *type; /* for vxlan: */ char *local_ip; char *remote_ip; /* for patch port: */ char *peer; }; struct ovs_port { struct ovs_port *next; struct ovs_bridge *bridge; struct if_entry *link; char *name; struct ovs_if *ifaces; int iface_count; /* vlan tags: */ unsigned int tag; unsigned int trunks_count; unsigned int *trunks; /* bonding: */ char *bond_mode; }; struct ovs_bridge { struct ovs_bridge *next; char *name; struct ovs_port *ports; struct ovs_port *system; }; static struct ovs_bridge *br_list; static int is_set(json_t *j) { return (!strcmp(json_string_value(json_array_get(j, 0)), "set")); } static int is_map(json_t *j) { return (!strcmp(json_string_value(json_array_get(j, 0)), "map")); } static int is_uuid(json_t *j) { return (!strcmp(json_string_value(json_array_get(j, 0)), "uuid")); } static int is_empty(json_t *j) { return json_is_array(j) && is_set(j); } static struct ovs_if *parse_iface(json_t *jresult, json_t *uuid) { struct ovs_if *iface; json_t *jif, *jarr; unsigned int i; if (!is_uuid(uuid)) return NULL; jif = json_object_get(jresult, "Interface"); jif = json_object_get(jif, json_string_value(json_array_get(uuid, 1))); jif = json_object_get(jif, "new"); iface = calloc(sizeof(*iface), 1); if (!iface) return NULL; iface->name = strdup(json_string_value(json_object_get(jif, "name"))); iface->type = strdup(json_string_value(json_object_get(jif, "type"))); jarr = json_object_get(jif, "options"); if (!strcmp(iface->type, "vxlan") && is_map(jarr)) { jarr = json_array_get(jarr, 1); for (i = 0; i < json_array_size(jarr); i++) { json_t *jkv; const char *key, *val; jkv = json_array_get(jarr, i); key = json_string_value(json_array_get(jkv, 0)); val = json_string_value(json_array_get(jkv, 1)); if (!strcmp(key, "local_ip")) iface->local_ip = strdup(val); else if (!strcmp(key, "remote_ip")) iface->remote_ip = strdup(val); } } else if (!strcmp(iface->type, "patch") && is_map(jarr)) { jarr = json_array_get(jarr, 1); for (i = 0; i < json_array_size(jarr); i++) { json_t *jkv; const char *key, *val; jkv = json_array_get(jarr, i); key = json_string_value(json_array_get(jkv, 0)); val = json_string_value(json_array_get(jkv, 1)); if (!strcmp(key, "peer")) iface->peer = strdup(val); } } return iface; } static int parse_port_info(struct ovs_port *port, json_t *jport) { json_t *jval, *jarr; unsigned int i, cnt; jval = json_object_get(jport, "tag"); if (!is_empty(jval)) port->tag = json_integer_value(jval); jarr = json_object_get(jport, "trunks"); jarr = json_array_get(jarr, 1); cnt = json_array_size(jarr); if (cnt > 0) { port->trunks = malloc(sizeof(*port->trunks) * cnt); if (!port->trunks) return ENOMEM; port->trunks_count = cnt; for (i = 0; i < cnt; i++) port->trunks[i] = json_integer_value(json_array_get(jarr, i)); } jval = json_object_get(jport, "bond_mode"); if (!is_empty(jval)) port->bond_mode = strdup(json_string_value(jval)); return 0; } static struct ovs_port *parse_port(json_t *jresult, json_t *uuid, struct ovs_bridge *br) { struct ovs_port *port; struct ovs_if *ptr = NULL; /* GCC false positive */ struct ovs_if *iface; json_t *jport, *jarr; unsigned int i; if (!is_uuid(uuid)) return NULL; jport = json_object_get(jresult, "Port"); jport = json_object_get(jport, json_string_value(json_array_get(uuid, 1))); jport = json_object_get(jport, "new"); port = calloc(sizeof(*port), 1); if (!port) return NULL; port->name = strdup(json_string_value(json_object_get(jport, "name"))); port->bridge = br; if (parse_port_info(port, jport)) return NULL; jarr = json_object_get(jport, "interfaces"); if (is_set(jarr)) { jarr = json_array_get(jarr, 1); for (i = 0; i < json_array_size(jarr); i++) { iface = parse_iface(jresult, json_array_get(jarr, i)); if (!iface) return NULL; iface->port = port; if (!port->ifaces) port->ifaces = iface; else ptr->next = iface; ptr = iface; port->iface_count++; } } else { iface = parse_iface(jresult, jarr); if (!iface) return NULL; iface->port = port; port->ifaces = iface; port->iface_count = 1; } if (!strcmp(json_string_value(json_object_get(jport, "name")), br->name)) br->system = port; return port; } static struct ovs_bridge *parse_bridge(json_t *jresult, json_t *uuid) { struct ovs_bridge *br; struct ovs_port *ptr = NULL; /* GCC false positive */ struct ovs_port *port; json_t *jbridge, *jarr; unsigned int i; if (!is_uuid(uuid)) return NULL; jbridge = json_object_get(jresult, "Bridge"); jbridge = json_object_get(jbridge, json_string_value(json_array_get(uuid, 1))); jbridge = json_object_get(jbridge, "new"); br = calloc(sizeof(*br), 1); if (!br) return NULL; br->name = strdup(json_string_value(json_object_get(jbridge, "name"))); jarr = json_object_get(jbridge, "ports"); if (is_set(jarr)) { jarr = json_array_get(jarr, 1); for (i = 0; i < json_array_size(jarr); i++) { port = parse_port(jresult, json_array_get(jarr, i), br); if (!port) return NULL; if (!br->ports) br->ports = port; else ptr->next = port; ptr = port; } } else br->ports = parse_port(jresult, jarr, br); if (!br->ports) return NULL; return br; } static struct ovs_bridge *parse(char *answer) { struct ovs_bridge *list = NULL, *ptr, *br; json_t *jroot, *jresult, *jovs, *jarr; unsigned int i; jroot = json_loads(answer, 0, NULL); if (!jroot) return NULL; jresult = json_object_get(jroot, "result"); if (!jresult) return NULL; /* TODO: add the rest of error handling */ jovs = json_object_get(jresult, "Open_vSwitch"); if (json_object_size(jovs) != 1) return NULL; jovs = json_object_iter_value(json_object_iter(jovs)); jovs = json_object_get(jovs, "new"); jarr = json_object_get(jovs, "bridges"); if (is_set(jarr)) { jarr = json_array_get(jarr, 1); for (i = 0; i < json_array_size(jarr); i++) { br = parse_bridge(jresult, json_array_get(jarr, i)); if (!br) return NULL; if (!list) list = br; else ptr->next = br; ptr = br; } } else list = parse_bridge(jresult, jarr); json_decref(jroot); return list; } static void add_table(json_t *parmobj, char *table, ...) { va_list ap; json_t *tableobj, *cols; char *s; va_start(ap, table); tableobj = json_object(); cols = json_array(); while ((s = va_arg(ap, char *))) json_array_append_new(cols, json_string(s)); json_object_set_new(tableobj, "columns", cols); json_object_set_new(parmobj, table, tableobj); va_end(ap); } static char *construct_query(void) { json_t *root, *params, *po; char *res; root = json_object(); json_object_set_new(root, "method", json_string("monitor")); json_object_set_new(root, "id", json_integer(0)); params = json_array(); json_array_append_new(params, json_string("Open_vSwitch")); json_array_append_new(params, json_null()); po = json_object(); add_table(po, "Open_vSwitch", "bridges", "ovs_version", NULL); add_table(po, "Bridge", "name", "ports", NULL); add_table(po, "Port", "interfaces", "name", "tag", "trunks", "bond_mode", NULL); add_table(po, "Interface", "name", "type", "options", "admin_state", "link_state", NULL); json_array_append_new(params, po); json_object_set_new(root, "params", params); res = json_dumps(root, 0); json_decref(root); return res; } static int connect_ovs(void) { int fd; struct sockaddr_un sun; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return fd; sun.sun_family = AF_UNIX; strncpy(sun.sun_path, db, UNIX_PATH_MAX); sun.sun_path[UNIX_PATH_MAX - 1] = '\0'; connect(fd, (struct sockaddr *)&sun, sizeof(sun.sun_family) + strlen(sun.sun_path) + 1); return fd; } #define CHUNK 65536 static char *read_all(int fd) { char *buf, *newbuf; size_t len = 0; ssize_t res; buf = malloc(CHUNK); if (!buf) return NULL; while (1) { res = read(fd, buf + len, CHUNK); if (res < 0) { free(buf); return NULL; } len += res; if (res < CHUNK) { buf[len] = '\0'; return buf; } newbuf = realloc(buf, len + CHUNK); if (!newbuf) { free(buf); return NULL; } buf = newbuf; } } static int check_vport(struct netns_entry *ns, struct if_entry *entry) { struct nl_handle hnd; struct ovs_header oh; void *payload; struct nlmsg_entry *dest; int len; int err; /* Be paranoid. If anything goes wrong, assume the interace is not * a vport. It's better to present an interface as unconnected to * the bridge when it's in fact connected, than vice versa. */ if (!vport_genl_id) return 0; if (netns_switch(ns)) return 0; if (genl_open(&hnd)) return 0; oh.dp_ifindex = 0; len = nla_add_str(&oh, sizeof(oh), OVS_VPORT_ATTR_NAME, entry->if_name, &payload); if (!len) goto out_hnd; err = genl_request(&hnd, vport_genl_id, OVS_VPORT_CMD_GET, payload, len, &dest); if (err) goto out_payload; /* Keep err = 0. We're only interested whether the call succeeds or * not, we don't care about the returned data. */ nlmsg_free(dest); out_payload: free(payload); out_hnd: nl_close(&hnd); return !err; } static int link_iface_search(struct if_entry *entry, void *arg) { struct ovs_if *iface = arg; int search_for_system = !iface->port->bridge->system->ifaces->link; int weight; if (!search_for_system && entry->master && strcmp(entry->master->if_name, "ovs-system")) return 0; /* Ignore ifindex reported by ovsdb, as it is guessed by the * interface name anyway and does not work correctly accross netns. * The heuristics below is much more reliable, though obviously far * from good, it fails spectacularly when the netdev interface is * renamed. */ if (strcmp(iface->name, entry->if_name)) return 0; if (!strcmp(iface->type, "internal") && strcmp(entry->driver, "openvswitch")) return 0; /* We've got a match. This still may not mean the interface is * actually connected in the kernel datapath. Newer kernels set * master (at least for netdev interface type) to ovs-system, which * we check above. For older kernels, we need to be more clever. */ if (!search_for_system && !entry->master && !check_vport(iface->port->bridge->system->ifaces->link->ns, entry)) return 0; weight = 1; if (!search_for_system) { if (iface->port->bridge->system->ifaces->link->ns == entry->ns) weight++; } else { if (!entry->ns->name) weight++; } return weight; } static int link_iface(struct ovs_if *iface, struct netns_entry *root, int required) { int err; if (iface->link) return 0; err = match_if_heur(&iface->link, root, 1, NULL, link_iface_search, iface); if (err > 0) return err; if (err < 0) return label_add(&root->warnings, "Failed to map openvswitch interface %s reliably", iface->name); if (required && !iface->link) return label_add(&root->warnings, "Failed to map openvswitch interface %s", iface->name); return 0; } static struct if_entry *create_iface(char *name, char *br_name, struct netns_entry *root) { struct if_entry *entry; char buf[IFNAMSIZ + 4 + 1]; entry = calloc(sizeof(*entry), 1); if (!entry) return NULL; entry->ns = root; snprintf(buf, sizeof(buf), "ovs:%s", br_name); entry->internal_ns = strdup(buf); entry->if_name = strdup(name); if (!entry->internal_ns || !entry->if_name) return NULL; entry->flags |= IF_INTERNAL; if_append(&root->ifaces, entry); return entry; } static void label_iface(struct ovs_if *iface) { if (iface->type && *iface->type) label_add(&iface->link->label, "type: %s", iface->type); if (iface->local_ip) label_add(&iface->link->label, "from %s", iface->local_ip); if (iface->remote_ip) label_add(&iface->link->label, "to %s", iface->remote_ip); } static void label_port_or_iface(struct ovs_port *port, struct if_entry *link) { if (port->tag) { if (asprintf(&link->edge_label, "tag %u", port->tag) < 0) link->edge_label = NULL; } else if (port->trunks_count) { char *buf, *ptr; unsigned int i; buf = malloc(16 * port->trunks_count + 7 + 1); if (!buf) return; ptr = buf + sprintf(buf, "trunks %u", port->trunks[0]); for (i = 1; i < port->trunks_count; i++) ptr += sprintf(ptr, ", %u", port->trunks[i]); link->edge_label = buf; } if (port->bond_mode) label_add(&link->label, "bond mode: %s", port->bond_mode); } static void link_vxlan(struct ovs_if *iface) { if (!iface->local_ip || !*iface->local_ip) return; iface->link->peer = tunnel_find_iface(iface->link->ns, iface->local_ip); iface->link->flags |= IF_PEER_WEAK; } static int link_patch_search(struct if_entry *entry, void *arg) { struct ovs_if *iface = arg; if (!iface->peer || strcmp(iface->peer, entry->if_name) || !(entry->flags & IF_INTERNAL)) return 0; return 1; } static int link_patch(struct ovs_if *iface, struct netns_entry *root) { int err; err = match_if_heur(&iface->link->peer, root, 1, NULL, link_patch_search, iface); if (err > 0) return err; if (err < 0) return if_add_warning(iface->link, "failed to find openvswitch patch port peer reliably"); if (iface->link->peer) iface->link->peer->peer = iface->link; /* Ignore case when the peer is not found, it will be found from the * other side. */ return 0; } static int link_ifaces(struct netns_entry *root) { struct ovs_bridge *br; struct ovs_port *port; struct ovs_if *iface; struct if_entry *master; int err; for (br = br_list; br; br = br->next) { if (!br->system || !br->system->iface_count) return label_add(&root->warnings, "Failed to find main interface for openvswitch bridge %s", br->name); if (br->system->iface_count > 1) return label_add(&root->warnings, "Main port for openvswitch bridge %s appears to have several interfaces", br->name); if ((err = link_iface(br->system->ifaces, root, 1))) return err; for (port = br->ports; port; port = port->next) { if (port == br->system) continue; master = br->system->ifaces->link; if (port->iface_count > 1) { port->link = create_iface(port->name, port->bridge->name, root); if (!port->link) return ENOMEM; port->link->master = master; master = port->link; label_port_or_iface(port, port->link); } for (iface = port->ifaces; iface; iface = iface->next) { if ((err = link_iface(iface, root, 0))) return err; if (!iface->link) { iface->link = create_iface(iface->name, iface->port->bridge->name, root); if (!iface->link) return ENOMEM; } /* reconnect to the ovs master */ iface->link->master = master; label_iface(iface); if (port->iface_count == 1) label_port_or_iface(port, iface->link); if (!strcmp(iface->type, "vxlan")) link_vxlan(iface); else if (!strcmp(iface->type, "patch")) { if ((err = link_patch(iface, root))) return err; } } } } return 0; } static int ovs_global_post(struct netns_entry *root) { char *str; int fd, len; int err; str = construct_query(); len = strlen(str); fd = connect_ovs(); if (fd < 0) return 0; if (write(fd, str, len) < len) { close(fd); return 0; } free(str); str = read_all(fd); br_list = parse(str); free(str); close(fd); if (!br_list) return 0; if ((err = link_ifaces(root))) return err; return 0; } static void destruct_if(struct ovs_if *iface) { free(iface->name); free(iface->type); free(iface->local_ip); free(iface->remote_ip); } static void destruct_port(struct ovs_port *port) { free(port->name); free(port->trunks); list_free(port->ifaces, (destruct_f)destruct_if); } static void destruct_bridge(struct ovs_bridge *br) { free(br->name); list_free(br->ports, (destruct_f)destruct_port); } static void ovs_global_init(void) { struct nl_handle hnd; if (genl_open(&hnd)) { vport_genl_id = 0; return; } vport_genl_id = genl_family_id(&hnd, OVS_VPORT_FAMILY); nl_close(&hnd); return; } static void ovs_global_cleanup(_unused struct netns_entry *root) { list_free(br_list, (destruct_f)destruct_bridge); } static struct global_handler gh_ovs = { .init = ovs_global_init, .post = ovs_global_post, .cleanup = ovs_global_cleanup, }; static struct arg_option options[] = { { .long_name = "ovs-db", .short_name = 'D', .has_arg = 1, .type = ARG_CHAR, .action.char_var = &db, .help = "path to openvswitch database" }, }; void handler_ovs_register(void) { db = OVS_DB_DEFAULT; arg_register_batch(options, ARRAY_SIZE(options)); global_handler_register(&gh_ovs); } plotnetcfg-0.4.1/handlers/openvswitch.h000066400000000000000000000013701255537235400202170ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLERS_OPENVSWITCH_H #define _HANDLERS_OPENVSWITCH_H void handler_ovs_register(void); #endif plotnetcfg-0.4.1/handlers/veth.c000066400000000000000000000043021255537235400166050ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "../ethtool.h" #include "../handler.h" #include "../match.h" #include "../utils.h" #include "veth.h" static int veth_scan(struct if_entry *entry); static int veth_post(struct if_entry *entry, struct netns_entry *root); static struct handler h_veth = { .driver = "veth", .scan = veth_scan, .post = veth_post, }; void handler_veth_register(void) { handler_register(&h_veth); } static int veth_scan(struct if_entry *entry) { if (entry->link_index) { entry->peer_index = entry->link_index; entry->peer_netnsid = entry->link_netnsid; entry->peer = entry->link; entry->link_index = 0; entry->link_netnsid = -1; entry->link = NULL; } else { entry->peer_index = ethtool_veth_peer(entry->if_name); } return 0; } static int match_peer(struct if_entry *entry, void *arg) { struct if_entry *link = arg; if (entry->if_index != link->peer_index || entry->peer_index != link->if_index || strcmp(entry->driver, "veth")) return 0; if (entry->peer && entry->peer != link) return 0; if (entry->ns == link->ns) return 2; return 1; } static int veth_post(struct if_entry *entry, struct netns_entry *root) { int err; if (entry->peer) return 0; if (!entry->peer_index) return ENOENT; err = match_if_heur(&entry->peer, root, 1, entry, match_peer, entry); if (err > 0) return err; if (err < 0) return if_add_warning(entry, "failed to find the veth peer reliably"); if (!entry->peer) return if_add_warning(entry, "failed to find the veth perr"); return 0; } plotnetcfg-0.4.1/handlers/veth.h000066400000000000000000000013531255537235400166150ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLERS_VETH_H #define _HANDLERS_VETH_H void handler_veth_register(void); #endif plotnetcfg-0.4.1/handlers/vlan.c000066400000000000000000000036601255537235400166050ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "../handler.h" #include "../netlink.h" #include "vlan.h" static int vlan_netlink(struct if_entry *entry, struct rtattr **tb); static struct handler h_vlan = { .driver = "802.1Q VLAN Support", .netlink = vlan_netlink, .cleanup = handler_generic_cleanup, }; struct vlan_private { unsigned int tag; }; void handler_vlan_register(void) { handler_register(&h_vlan); } static int vlan_netlink(struct if_entry *entry, struct rtattr **tb) { struct vlan_private *priv; struct rtattr *linkinfo[IFLA_INFO_MAX + 1]; struct rtattr *vlanattr[IFLA_VLAN_MAX + 1]; priv = calloc(sizeof(*priv), 1); if (!priv) return ENOMEM; entry->handler_private = priv; if (!tb[IFLA_LINKINFO]) return ENOENT; rtnl_parse_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); if (!linkinfo[IFLA_INFO_DATA]) return ENOENT; rtnl_parse_nested(vlanattr, IFLA_VLAN_MAX, linkinfo[IFLA_INFO_DATA]); if (!vlanattr[IFLA_VLAN_ID] || RTA_PAYLOAD(vlanattr[IFLA_VLAN_ID]) < sizeof(__u16)) return ENOENT; priv->tag = *(uint16_t *)RTA_DATA(vlanattr[IFLA_VLAN_ID]); if (asprintf(&entry->edge_label, "tag %d", priv->tag) < 0) return ENOMEM; return 0; } plotnetcfg-0.4.1/handlers/vlan.h000066400000000000000000000013531255537235400166070ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _HANDLERS_VLAN_H #define _HANDLERS_VLAN_H void handler_vlan_register(void); #endif plotnetcfg-0.4.1/if.c000066400000000000000000000145561255537235400144510ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "ethtool.h" #include "handler.h" #include "label.h" #include "netlink.h" #include "utils.h" #include "if.h" #include "compat.h" static void fill_if_link(struct if_entry *dest, struct nlmsghdr *n) { struct ifinfomsg *ifi = NLMSG_DATA(n); struct rtattr *tb[IFLA_MAX + 1]; int len = n->nlmsg_len; if (n->nlmsg_type != RTM_NEWLINK) return; len -= NLMSG_LENGTH(sizeof(*ifi)); if (len < 0) return; rtnl_parse(tb, IFLA_MAX, IFLA_RTA(ifi), len); if (tb[IFLA_IFNAME] == NULL) return; dest->if_index = ifi->ifi_index; dest->if_name = strdup(RTA_DATA(tb[IFLA_IFNAME])); if (ifi->ifi_flags & IFF_UP) { dest->flags |= IF_UP; if (ifi->ifi_flags & IFF_RUNNING) dest->flags |= IF_HAS_LINK; } if (tb[IFLA_MASTER]) dest->master_index = *(int *)RTA_DATA(tb[IFLA_MASTER]); if (tb[IFLA_LINK]) { dest->link_index = *(int*)RTA_DATA(tb[IFLA_LINK]); if (tb[IFLA_LINK_NETNSID]) dest->link_netnsid = *(int*)RTA_DATA(tb[IFLA_LINK_NETNSID]); } if (tb[IFLA_MTU]) dest->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); if (ifi->ifi_flags & IFF_LOOPBACK) { dest->driver = strdup("loopback"); dest->flags |= IF_LOOPBACK; } else dest->driver = ethtool_driver(dest->if_name); handler_netlink(dest, tb); } static int store_addr(struct addr *dest, const struct ifaddrmsg *ifa, const struct rtattr *rta) { char buf[64]; unsigned int len = RTA_PAYLOAD(rta); dest->family = ifa->ifa_family; dest->prefixlen = ifa->ifa_prefixlen; dest->raw = malloc(len); if (!dest->raw) return ENOMEM; memcpy(dest->raw, RTA_DATA(rta), len); if (!inet_ntop(ifa->ifa_family, RTA_DATA(rta), buf, sizeof(buf))) return errno; len = strlen(buf); snprintf(buf + len, sizeof(buf) - len, "/%d", ifa->ifa_prefixlen); dest->formatted = strdup(buf); if (!dest->formatted) return ENOMEM; return 0; } static int fill_if_addr(struct if_entry *dest, struct nlmsg_entry *ainfo) { struct if_addr_entry *entry, *ptr = NULL; struct nlmsghdr *n; struct ifaddrmsg *ifa; struct rtattr *rta_tb[IFA_MAX + 1]; int len, err; for (; ainfo; ainfo = ainfo->next) { n = &ainfo->h; ifa = NLMSG_DATA(n); if (ifa->ifa_index != dest->if_index) continue; if (n->nlmsg_type != RTM_NEWADDR) continue; len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); if (len < 0) continue; if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) /* only IP addresses supported (at least for now) */ continue; rtnl_parse(rta_tb, IFA_MAX, IFA_RTA(ifa), len); if (!rta_tb[IFA_LOCAL] && !rta_tb[IFA_ADDRESS]) /* don't care about broadcast and anycast adresses */ continue; entry = calloc(sizeof(struct if_addr_entry), 1); if (!entry) return ENOMEM; if (!rta_tb[IFA_LOCAL]) { rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; rta_tb[IFA_ADDRESS] = NULL; } if ((err = store_addr(&entry->addr, ifa, rta_tb[IFA_LOCAL]))) return err; if (rta_tb[IFA_ADDRESS] && memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), ifa->ifa_family == AF_INET ? 4 : 16)) { if ((err = store_addr(&entry->peer, ifa, rta_tb[IFA_ADDRESS]))) return err; } if (!ptr) dest->addr = entry; else ptr->next = entry; ptr = entry; } return 0; } static struct if_entry *if_alloc(void) { struct if_entry *entry; entry = calloc(sizeof(struct if_entry), 1); if (!entry) return NULL; entry->link_netnsid = -1; entry->peer_netnsid = -1; return entry; } int if_list(struct if_entry **result, struct netns_entry *ns) { struct nl_handle hnd; struct nlmsg_entry *linfo, *ainfo, *l; struct if_entry *entry, *ptr = NULL; int err; *result = NULL; if ((err = rtnl_open(&hnd))) return err; err = rtnl_dump(&hnd, AF_UNSPEC, RTM_GETLINK, &linfo); if (err) return err; err = rtnl_dump(&hnd, AF_UNSPEC, RTM_GETADDR, &ainfo); if (err) return err; for (l = linfo; l; l = l->next) { entry = if_alloc(); if (!entry) return ENOMEM; entry->ns = ns; fill_if_link(entry, &l->h); if ((err = fill_if_addr(entry, ainfo))) return err; if ((err = handler_scan(entry))) return err; if (!ptr) *result = entry; else ptr->next = entry; ptr = entry; } nlmsg_free(linfo); nlmsg_free(ainfo); nl_close(&hnd); return 0; } static void if_addr_destruct(struct if_addr_entry *entry) { free(entry->addr.raw); free(entry->addr.formatted); free(entry->peer.raw); free(entry->peer.formatted); } static void if_list_destruct(struct if_entry *entry) { handler_cleanup(entry); free(entry->internal_ns); free(entry->if_name); free(entry->edge_label); label_free(entry->label); list_free(entry->addr, (destruct_f)if_addr_destruct); list_free(entry->rev_master, NULL); } void if_list_free(struct if_entry *list) { list_free(list, (destruct_f)if_list_destruct); } void if_append(struct if_entry **list, struct if_entry *item) { struct if_entry *ptr = *list; item->next = NULL; if (!ptr) { *list = item; return; } while (ptr->next) ptr = ptr->next; ptr->next = item; } int if_add_warning(struct if_entry *entry, char *fmt, ...) { va_list ap; char *warn; int err = ENOMEM; va_start(ap, fmt); entry->warnings++; if (vasprintf(&warn, fmt, ap) < 0) goto out; err = label_add(&entry->ns->warnings, "%s: %s", ifstr(entry), warn); free(warn); out: va_end(ap); return err; } int if_list_build_rev(struct if_entry *list) { while (list) { if (list->master) { struct if_list_entry *le; le = malloc(sizeof(*le)); if (!le) return ENOMEM; le->entry = list; le->next = list->master->rev_master; list->master->rev_master = le; } if (list->link) list->link->rev_link = list->link; list = list->next; } return 0; } plotnetcfg-0.4.1/if.h000066400000000000000000000036371255537235400144540ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _IF_H #define _IF_H struct netns_entry; struct label; struct addr { int family; int prefixlen; char *formatted; void *raw; }; struct if_addr_entry { struct if_addr_entry *next; struct addr addr; struct addr peer; }; struct if_list_entry { struct if_list_entry *next; struct if_entry *entry; }; struct if_entry { struct if_entry *next; struct netns_entry *ns; char *internal_ns; unsigned int if_index; unsigned int flags; int mtu; char *if_name; char *driver; struct label *label; unsigned int master_index; struct if_entry *master; unsigned int link_index; int link_netnsid; struct if_entry *link; unsigned int peer_index; int peer_netnsid; struct if_entry *peer; struct if_addr_entry *addr; char *edge_label; void *handler_private; int warnings; /* reverse fields needed by some frontends: */ struct if_list_entry *rev_master; struct if_entry *rev_link; }; #define IF_LOOPBACK 1 #define IF_UP 2 #define IF_HAS_LINK 4 #define IF_INTERNAL 8 #define IF_PEER_WEAK 16 int if_list(struct if_entry **result, struct netns_entry *ns); void if_list_free(struct if_entry *list); void if_append(struct if_entry **list, struct if_entry *item); int if_add_warning(struct if_entry *entry, char *fmt, ...); int if_list_build_rev(struct if_entry *list); #endif plotnetcfg-0.4.1/label.c000066400000000000000000000025421255537235400151220ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include "utils.h" #include "label.h" int label_add(struct label **list, char *fmt, ...) { va_list ap; struct label *new, *ptr = *list; int err = ENOMEM; va_start(ap, fmt); new = calloc(sizeof(*new), 1); if (!new) goto out; if (vasprintf(&new->text, fmt, ap) < 0) { free(new); goto out; } err = 0; if (!ptr) { *list = new; goto out; } while (ptr->next) ptr = ptr->next; ptr->next = new; out: va_end(ap); return err; } static void label_destruct(struct label *item) { free(item->text); } void label_free(struct label *list) { list_free(list, (destruct_f)label_destruct); } plotnetcfg-0.4.1/label.h000066400000000000000000000015071255537235400151270ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _LABEL_H #define _LABEL_H struct label { struct label *next; char *text; }; int label_add(struct label **list, char *fmt, ...); void label_free(struct label *list); #endif plotnetcfg-0.4.1/main.c000066400000000000000000000061141255537235400147660ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include "args.h" #include "netns.h" #include "utils.h" #include "version.h" #include "frontend.h" #include "frontends/dot.h" #include "frontends/json.h" #include "handler.h" #include "handlers/bridge.h" #include "handlers/master.h" #include "handlers/openvswitch.h" #include "handlers/veth.h" #include "handlers/vlan.h" static void register_frontends(void) { frontend_init(); frontend_dot_register(); frontend_json_register(); } static void register_handlers(void) { handler_master_register(); handler_ovs_register(); handler_veth_register(); handler_vlan_register(); handler_bridge_register(); } static int print_help(_unused char *arg) { printf("Usage: plotnetcfg [OPTION]...\n\n"); arg_get_help((arg_help_handler_t)puts); return 1; } static int print_version(_unused char *arg) { printf("%s\n", VERSION); return 1; } static struct arg_option options[] = { { .long_name = "help", .short_name = 'h', .type = ARG_CALLBACK, .action.callback = print_help, .help = "print help and exit", }, { .long_name = "version", .short_name = '\0', .type = ARG_CALLBACK, .action.callback = print_version, .help = "print version and exit", }, }; static int check_caps(void) { struct __user_cap_header_struct caps_hdr; struct __user_cap_data_struct caps; caps_hdr.version = _LINUX_CAPABILITY_VERSION_1; caps_hdr.pid = 0; if (syscall(__NR_capget, &caps_hdr, &caps) < 0) return 0; if (!(caps.effective & (1U << CAP_SYS_ADMIN)) || !(caps.effective & (1U << CAP_NET_ADMIN))) return 0; return 1; } int main(int argc, char **argv) { struct netns_entry *root; int netns_ok, err; arg_register_batch(options, ARRAY_SIZE(options)); register_frontends(); register_handlers(); if ((err = arg_parse(argc, argv))) exit(err); if (!check_caps()) { fprintf(stderr, "Must be run under root (or with enough capabilities).\n"); exit(1); } netns_ok = netns_switch_root(); if (netns_ok > 0) { fprintf(stderr, "Cannot change to the root name space: %s\n", strerror(netns_ok)); exit(1); } global_handler_init(); if ((err = netns_list(&root, netns_ok == 0))) { fprintf(stderr, "ERROR: %s\n", strerror(err)); exit(1); } if ((err = frontend_output(root))) { fprintf(stderr, "Invalid output format specified.\n"); exit(1); } global_handler_cleanup(root); netns_list_free(root); return 0; } plotnetcfg-0.4.1/match.c000066400000000000000000000043731255537235400151430ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014-2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include "netns.h" #include "if.h" #include "match.h" int match_if_heur(struct if_entry **found, struct netns_entry *root, int all_ns, struct if_entry *self, int (*callback)(struct if_entry *, void *), void *arg) { struct netns_entry *ns; struct if_entry *entry; int prio = 0, count = 0, res; for (ns = root; ns; ns = ns->next) { for (entry = ns->ifaces; entry; entry = entry->next) { if (entry == self) continue; res = callback(entry, arg); if (res < 0) return -res; if (res > prio) { *found = entry; prio = res; count = 1; } else if (res == prio) count++; } if (!all_ns) break; } if (!prio) { *found = NULL; return 0; } if (count > 1) return -1; return 0; } struct if_entry *match_if_netnsid(unsigned int ifindex, int netnsid, struct netns_entry *current) { struct netns_id *ptr; struct if_entry *entry; for (ptr = current->ids; ptr; ptr = ptr->next) { if (ptr->id == netnsid) { for (entry = ptr->ns->ifaces; entry; entry = entry->next) { if (entry->if_index == ifindex) return entry; } break; } } return NULL; } void match_all_netnsid(struct netns_entry *root) { struct netns_entry *ns; struct if_entry *entry; for (ns = root; ns; ns = ns->next) { for (entry = ns->ifaces; entry; entry = entry->next) { if (entry->link_netnsid >= 0) entry->link = match_if_netnsid(entry->link_index, entry->link_netnsid, ns); if (entry->peer_netnsid >= 0) entry->peer = match_if_netnsid(entry->peer_index, entry->peer_netnsid, ns); } } } plotnetcfg-0.4.1/match.h000066400000000000000000000026661255537235400151530ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014-2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _MATCH_H #define _MATCH_H #include "if.h" #include "netns.h" /* Find interface using a heuristic. * Callback returns 0 to ignore the interface, < 0 for error, > 0 for * priority. * The highest priority match is returned if exactly one highest priority * interface matches. Returns -1 if more highest priority interfaces match. * Returns 0 for success (*found will be NULL for no match) or error * code > 0. */ int match_if_heur(struct if_entry **found, struct netns_entry *root, int all_ns, struct if_entry *self, int (*callback)(struct if_entry *, void *), void *arg); /* Find interface using netnsid. */ struct if_entry *match_if_netnsid(unsigned int ifindex, int netnsid, struct netns_entry *current); void match_all_netnsid(struct netns_entry *root); #endif plotnetcfg-0.4.1/netlink.c000066400000000000000000000151471255537235400155140ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "netlink.h" static int nl_open(struct nl_handle *hnd, int family) { int bufsize; int err; struct sockaddr_nl sa; socklen_t sa_len; hnd->fd = socket(AF_NETLINK, SOCK_RAW, family); if (hnd->fd < 0) return -errno; hnd->seq = 0; bufsize = 32768; if (setsockopt(hnd->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) < 0) goto err_out; bufsize = 1048576; if (setsockopt(hnd->fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) < 0) goto err_out; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; if (bind(hnd->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) goto err_out; sa_len = sizeof(sa); if (getsockname(hnd->fd, (struct sockaddr *)&sa, &sa_len) < 0) goto err_out; hnd->pid = sa.nl_pid; return 0; err_out: err = -errno; close(hnd->fd); return err; } void nl_close(struct nl_handle *hnd) { close(hnd->fd); } void nlmsg_free(struct nlmsg_entry *entry) { list_free(entry, NULL); } int nl_send(struct nl_handle *hnd, struct iovec *iov, int iovlen) { struct sockaddr_nl sa = { .nl_family = AF_NETLINK, }; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = iov, .msg_iovlen = iovlen, }; struct nlmsghdr *src = iov->iov_base; src->nlmsg_seq = ++hnd->seq; if (sendmsg(hnd->fd, &msg, 0) < 0) return errno; return 0; } int nl_recv(struct nl_handle *hnd, struct nlmsg_entry **dest, int is_dump) { struct sockaddr_nl sa = { .nl_family = AF_NETLINK, }; struct iovec iov; struct msghdr msg = { .msg_name = &sa, .msg_namelen = sizeof(sa), .msg_iov = &iov, .msg_iovlen = 1, }; char buf[16384]; int len, err; struct nlmsghdr *n; struct nlmsg_entry *ptr = NULL; /* GCC false positive */ struct nlmsg_entry *entry; *dest = NULL; while (1) { iov.iov_base = buf; iov.iov_len = sizeof(buf); len = recvmsg(hnd->fd, &msg, 0); if (len < 0) return errno; if (!len) return EPIPE; if (sa.nl_pid) { /* not from the kernel */ continue; } for (n = (struct nlmsghdr *)buf; NLMSG_OK(n, len); n = NLMSG_NEXT(n, len)) { if (n->nlmsg_pid != hnd->pid || n->nlmsg_seq != hnd->seq) continue; if (is_dump && n->nlmsg_type == NLMSG_DONE) return 0; if (n->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *)NLMSG_DATA(n); err = -nlerr->error; goto err_out; } entry = malloc(n->nlmsg_len + sizeof(void *)); if (!entry) { err = ENOMEM; goto err_out; } entry->next = NULL; memcpy(&entry->h, n, n->nlmsg_len); if (!*dest) *dest = entry; else ptr->next = entry; ptr = entry; if (!is_dump) return 0; } } err_out: nlmsg_free(*dest); *dest = NULL; return err; } int nl_exchange(struct nl_handle *hnd, struct nlmsghdr *src, struct nlmsg_entry **dest) { struct iovec iov = { .iov_base = src, .iov_len = src->nlmsg_len, }; int is_dump; int err; is_dump = !!(src->nlmsg_flags & NLM_F_DUMP); err = nl_send(hnd, &iov, 1); if (err) return err; return nl_recv(hnd, dest, is_dump); } /* The original payload is not freed. Returns 0 in case of error, length * of *dest otherwise. *dest is newly allocated. */ int nla_add_str(void *orig, int orig_len, int nla_type, const char *str, void **dest) { struct nlattr *nla; int len = strlen(str) + 1; int size; size = NLA_ALIGN(orig_len) + NLA_HDRLEN + NLA_ALIGN(len); *dest = calloc(size, 1); if (!*dest) return 0; if (orig_len) memcpy(*dest, orig, orig_len); nla = *dest + NLA_ALIGN(orig_len); nla->nla_len = NLA_HDRLEN + len; nla->nla_type = nla_type; memcpy(nla + 1, str, len); return size; } int rtnl_open(struct nl_handle *hnd) { return nl_open(hnd, NETLINK_ROUTE); } int rtnl_dump(struct nl_handle *hnd, int family, int type, struct nlmsg_entry **dest) { struct { struct nlmsghdr n; struct ifinfomsg i; } req; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = sizeof(req); req.n.nlmsg_type = type; req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; req.i.ifi_family = family; return nl_exchange(hnd, &req.n, dest); } void rtnl_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len) { memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta, len); } } void rtnl_parse_nested(struct rtattr *tb[], int max, struct rtattr *rta) { rtnl_parse(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); } int genl_open(struct nl_handle *hnd) { return nl_open(hnd, NETLINK_GENERIC); } int genl_request(struct nl_handle *hnd, int type, int cmd, void *payload, int payload_len, struct nlmsg_entry **dest) { struct { struct nlmsghdr n; struct genlmsghdr g; } req; struct iovec iov[2]; int err; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = sizeof(req) + payload_len; req.n.nlmsg_type = type; req.n.nlmsg_flags = NLM_F_REQUEST; req.g.cmd = cmd; req.g.version = 1; iov[0].iov_base = &req; iov[0].iov_len = sizeof(req); iov[1].iov_base = payload; iov[1].iov_len = payload_len; err = nl_send(hnd, iov, 2); if (err) return err; return nl_recv(hnd, dest, 0); } unsigned int genl_family_id(struct nl_handle *hnd, const char *name) { unsigned int res = 0; struct nlattr *nla; int len; struct nlmsg_entry *dest; void *ptr; len = nla_add_str(NULL, 0, CTRL_ATTR_FAMILY_NAME, name, &ptr); if (!len) return 0; if (genl_request(hnd, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, ptr, len, &dest)) { free(ptr); return 0; } free(ptr); len = dest->h.nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; ptr = (void *)&dest->h + NLMSG_HDRLEN + GENL_HDRLEN; while (len > NLA_HDRLEN) { nla = ptr; if (nla->nla_type == CTRL_ATTR_FAMILY_ID && nla->nla_len >= NLA_HDRLEN + 2) { res = *(uint16_t *)(nla + 1); break; } ptr += NLMSG_ALIGN(nla->nla_len); len -= NLMSG_ALIGN(nla->nla_len); } nlmsg_free(dest); return res; } plotnetcfg-0.4.1/netlink.h000066400000000000000000000033071255537235400155140ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2015 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _NETLINK_H #define _NETLINK_H #include #include struct nl_handle { int fd; unsigned int pid; unsigned int seq; }; struct nlmsg_entry { struct nlmsg_entry *next; struct nlmsghdr h; }; /* all netlink families */ void nl_close(struct nl_handle *hnd); int nl_exchange(struct nl_handle *hnd, struct nlmsghdr *src, struct nlmsg_entry **dest); void nlmsg_free(struct nlmsg_entry *entry); int nla_add_str(void *orig, int orig_len, int nla_type, const char *str, void **dest); /* rtnetlink */ int rtnl_open(struct nl_handle *hnd); int rtnl_dump(struct nl_handle *hnd, int family, int type, struct nlmsg_entry **dest); void rtnl_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len); void rtnl_parse_nested(struct rtattr *tb[], int max, struct rtattr *rta); /* genetlink */ int genl_open(struct nl_handle *hnd); int genl_request(struct nl_handle *hnd, int type, int cmd, void *payload, int payload_len, struct nlmsg_entry **dest); unsigned int genl_family_id(struct nl_handle *hnd, const char *name); #endif plotnetcfg-0.4.1/netns.c000066400000000000000000000232121255537235400151670ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "handler.h" #include "if.h" #include "match.h" #include "netlink.h" #include "utils.h" #include "netns.h" #include "compat.h" #define NETNS_RUN_DIR "/var/run/netns" static long netns_get_kernel_id(const char *path) { char dest[PATH_MAX], *s, *endptr; ssize_t len; long result; len = readlink(path, dest, sizeof(dest)); if (len < 0) return -errno; dest[len] = 0; if (strncmp("net:[", dest, 5)) return -EINVAL; s = strrchr(dest, ']'); if (!s) return -EINVAL; *s = '\0'; s = dest + 5; result = strtol(s, &endptr, 10); if (!*s || *endptr) return -EINVAL; return result; } static struct netns_entry *netns_check_duplicate(struct netns_entry *root, long int kernel_id) { while (root) { if (root->kernel_id == kernel_id) return root; root = root->next; } return NULL; } static int netns_get_var_entry(struct netns_entry **result, struct netns_entry *root, const char *name) { struct netns_entry *entry; char path[PATH_MAX]; long kernel_id; int err; *result = entry = calloc(sizeof(struct netns_entry), 1); if (!entry) return ENOMEM; snprintf(path, sizeof(path), "%s/%s", NETNS_RUN_DIR, name); entry->fd = open(path, O_RDONLY); if (entry->fd < 0) return errno; /* to get the kernel_id, we need to switch to that ns and examine * /proc/self */ err = netns_switch(entry); if (err) return err; kernel_id = netns_get_kernel_id("/proc/self/ns/net"); if (kernel_id < 0) return -kernel_id; if (netns_check_duplicate(root, kernel_id)) { close(entry->fd); free(entry); return -1; } entry->kernel_id = kernel_id; entry->name = strdup(name); if (!entry->name) return ENOMEM; return netns_switch_root(); } static void netns_proc_entry_set_name(struct netns_entry *entry, const char *spid) { char path[PATH_MAX], buf[PATH_MAX]; ssize_t len; int commfd; snprintf(path, sizeof(path), "/proc/%s/comm", spid); commfd = open(path, O_RDONLY); len = -1; if (commfd >= 0) { len = read(commfd, path, sizeof(path)); if (len >= 0) { path[sizeof(path) - 1] = '\0'; if (path[len - 1] == '\n') path[len - 1] = '\0'; } close(commfd); } if (len >= 0) snprintf(buf, sizeof(buf), "PID %s (%s)", spid, path); else snprintf(buf, sizeof(buf), "PID %s", spid); entry->name = strdup(buf); if (!entry->name) entry->name = "?"; } static int netns_get_proc_entry(struct netns_entry **result, struct netns_entry *root, const char *spid) { struct netns_entry *entry, *dup; char path[PATH_MAX]; pid_t pid; long kernel_id; snprintf(path, sizeof(path), "/proc/%s/ns/net", spid); kernel_id = netns_get_kernel_id(path); if (kernel_id < 0) { /* ignore entries that cannot be read */ return -1; } pid = atol(spid); dup = netns_check_duplicate(root, kernel_id); if (dup) { if (dup->pid && (dup->pid > pid)) { dup->pid = pid; netns_proc_entry_set_name(dup, spid); } return -1; } *result = entry = calloc(sizeof(struct netns_entry), 1); if (!entry) return ENOMEM; entry->kernel_id = kernel_id; entry->pid = pid; entry->fd = open(path, O_RDONLY); if (entry->fd < 0) { /* ignore entries that cannot be read */ free(entry); return -1; } netns_proc_entry_set_name(entry, spid); return 0; } static int netns_new_list(struct netns_entry **root, int supported) { *root = calloc(sizeof(struct netns_entry), 1); if (!*root) return ENOMEM; if (supported) { (*root)->kernel_id = netns_get_kernel_id("/proc/1/ns/net"); if ((*root)->kernel_id < 0) return -(*root)->kernel_id; (*root)->fd = open("/proc/1/ns/net", O_RDONLY); if ((*root)->fd < 0) return errno; } return 0; } static int netns_add_var_list(struct netns_entry *root) { struct netns_entry *entry, *ptr; struct dirent *de; DIR *dir; int err; dir = opendir(NETNS_RUN_DIR); if (!dir) return 0; ptr = root; while (ptr->next) ptr = ptr->next; while ((de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; err = netns_get_var_entry(&entry, root, de->d_name); if (err < 0) { /* duplicate entry */ continue; } if (err) return err; ptr->next = entry; ptr = entry; } closedir(dir); return 0; } static int netns_add_proc_list(struct netns_entry *root) { struct netns_entry *entry, *ptr; struct dirent *de; DIR *dir; int err; dir = opendir("/proc"); if (!dir) return 0; ptr = root; while (ptr->next) ptr = ptr->next; while ((de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (de->d_name[0] < '0' || de->d_name[1] > '9') continue; err = netns_get_proc_entry(&entry, root, de->d_name); if (err < 0) { /* duplicate entry */ continue; } if (err) return err; ptr->next = entry; ptr = entry; } closedir(dir); return 0; } /* Returns -1 if netnsids are not supported. */ static int netns_get_id(struct nl_handle *hnd, struct netns_entry *entry) { struct { struct nlmsghdr n; struct rtgenmsg r; struct rtattr a __attribute__ ((aligned(NLMSG_ALIGNTO))); uint32_t fd; } src; struct nlmsg_entry *dst; struct rtattr *tb[NETNSA_MAX + 1]; int len, res; memset(&src, 0, sizeof(src)); src.n.nlmsg_len = sizeof(src); src.n.nlmsg_flags = NLM_F_REQUEST; src.n.nlmsg_type = RTM_GETNSID; src.r.rtgen_family = AF_UNSPEC; src.a.rta_type = NETNSA_FD; src.a.rta_len = RTA_LENGTH(sizeof(uint32_t)); src.fd = entry->fd; res = nl_exchange(hnd, &src.n, &dst); if (res || !dst) return -1; res = -1; len = dst->h.nlmsg_len - NLMSG_SPACE(sizeof(struct rtgenmsg)); if (len < 0) goto out; rtnl_parse(tb, NETNSA_MAX, NETNS_RTA(NLMSG_DATA(&dst->h)), len); if (tb[NETNSA_NSID]) res = *(int32_t *)RTA_DATA(tb[NETNSA_NSID]); out: nlmsg_free(dst); return res; } /* This is best effort only, if anything fails (e.g. netnsids are not * supported by kernel), we fail back to heuristics. */ static void netns_get_all_ids(struct netns_entry *current, struct netns_entry *root) { struct nl_handle hnd; struct netns_entry *entry; struct netns_id *nsid, *ptr = NULL; int id; if (netns_switch(current)) return; if (rtnl_open(&hnd) < 0) return; for (entry = root; entry; entry = entry->next) { id = netns_get_id(&hnd, entry); if (id < 0) continue; nsid = malloc(sizeof(*nsid)); if (!nsid) break; nsid->next = NULL; nsid->ns = entry; nsid->id = id; if (!ptr) current->ids = nsid; else ptr->next = nsid; ptr = nsid; } nl_close(&hnd); } static int netns_build_rev(struct netns_entry *root) { struct netns_entry *entry; int err; for (entry = root; entry; entry = entry->next) { if ((err = if_list_build_rev(entry->ifaces))) return err; } return 0; } int netns_list(struct netns_entry **result, int supported) { struct netns_entry *entry; int err; err = netns_new_list(result, supported); if (err) return err; if (supported) { err = netns_add_var_list(*result); if (err) return err; err = netns_add_proc_list(*result); if (err) return err; } for (entry = *result; entry; entry = entry->next) { if (entry->name) { /* Do not try to switch to the root netns, as we're * already there when processing the first entry, * and netns_switch fails hard if there's no netns * support available. */ if ((err = netns_switch(entry))) return err; } if ((err = if_list(&entry->ifaces, entry))) return err; } /* Walk all net name spaces again and gather all kernel assigned * netnsids. We don't assign netnsids ourselves to prevent assigning * them needlessly - the kernel assigns only those that are really * needed while doing netlinks dumps. Note also that netnsids are * per name space and this is O(n^2). */ for (entry = *result; entry; entry = entry->next) netns_get_all_ids(entry, *result); /* And finally, resolve netnsid+ifindex to the if_entry pointers. */ match_all_netnsid(*result); if ((err = global_handler_post(*result))) return err; if ((err = handler_post(*result))) return err; if ((err = netns_build_rev(*result))) return err; return 0; } static int do_netns_switch(int fd) { if (syscall(__NR_setns, fd, CLONE_NEWNET) < 0) return errno; return 0; } int netns_switch(struct netns_entry *dest) { return do_netns_switch(dest->fd); } /* Used also to detect whether netns support is available. * Returns 0 if everything went okay, -1 if there's no netns support, * positive error code in case of an error. */ int netns_switch_root(void) { int fd, res; fd = open("/proc/1/ns/net", O_RDONLY); if (fd < 0) { res = errno; if (res == ENOENT) res = -1; return res; } res = do_netns_switch(fd); close(fd); if (res == ENOENT) return -1; return res; } static void netns_list_destruct(struct netns_entry *entry) { list_free(entry->ids, NULL); if_list_free(entry->ifaces); free(entry->name); } void netns_list_free(struct netns_entry *list) { list_free(list, (destruct_f)netns_list_destruct); } plotnetcfg-0.4.1/netns.h000066400000000000000000000024511255537235400151760ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _NETNS_H #define _NETNS_H #include struct if_entry; struct label; struct netns_entry; struct netns_id { struct netns_id *next; struct netns_entry *ns; int id; }; struct netns_entry { struct netns_entry *next; struct if_entry *ifaces; struct label *warnings; long kernel_id; /* name is NULL for root name space, for other name spaces it * contains human recognizable identifier */ char *name; pid_t pid; int fd; struct netns_id *ids; }; int netns_list(struct netns_entry **result, int supported); void netns_list_free(struct netns_entry *list); int netns_switch(struct netns_entry *dest); int netns_switch_root(void); #endif plotnetcfg-0.4.1/plotnetcfg-json.5000066400000000000000000000101341255537235400170750ustar00rootroot00000000000000.TH plotnetcfg-json 5 "10 June 2015" .SH NAME plotnetcfg \- json output format .SH DESCRIPTION .SS Root object fields .TP format .I (number) Currently 1. Will be increased if incompatible changes are introduced. A tool parsing the json output should refuse any format it's not aware of. Note that adding of new fields is not considered to be an incompatible change. .TP version .I (string) Plotnetcfg version. .TP date .I (string) Time and date when the data were gathered, in .BR ctime (3) format. .TP namespaces .I (array) Array of name space objects. The first one is always the root name space. .SS Name space object fields .TP name .I (string) Name of the name space suitable for user consumption. This in general cannot be used for machine consumption, e.g. switching to the name space. The root name space has an empty name. .TP interfaces .I (array) Array of interface objects. .TP warnings .I (array) If present, an array of strings. Contains error messages encountered when gathering data in the given name space. .SS Interface object fields .TP id .I (string) Unique identifier of the interface. This is an arbitrary opaque string and the consumer should not make any assumption of its contents (apart of not containing null characters). It should not be displayed to the user, the sole purpose of this field is linking to other interfaces. The identifier is globally unique, it is safe to assume that interfaces with the same name in different name spaces have a different id. .TP name .I (string) User visible name of the interface. Usually (but not always) the name of the corresponding Linux interface. This is not unique between name spaces. .TP driver .I (string) The kernel module (driver) behind the interface. May be empty in some specific cases. .TP info .I (array) Array of strings. Contains additional information about the interface, formatted. An example is tunnel endpoints. The exact content is dependent on the type of the interface. .TP addresses .I (array) Array of address objects. .TP mtu .I (number) Interface MTU. .TP type .I (string) .RS "device": normal interface. Most interfaces are of this type. .P "internal": this interface is not backed up by a Linux interface. Can be often found with Open vSwitch. .P Further types are possible with future plotnetcfg versions. Adding them will not be considered a format change. .RE .TP state .I (string) .RS "down": the interface is administratively disabled. .P "up": the interface is up and operating. .P "up_no_link": the interface is up but has no link. .P "none": state cannot be determined or is not applicable to this kind of interface. .P More states are possible to be added in future plotnetcfg versions. Adding them will not be considered a format change. .RE .TP warning .I (bool) There was a problem gathering data about this interface. Details are in the name space warnings field. The purpose of this flag is for visual representation of this interface as not having complete data available. Not present if there was no error. .TP parent .I (object) The parent interface, as a connection object. Not present if there's no parent. .TP children .I (array) Array of children interfaces, as connection objects. Not present if there are no children. .TP peer .I (object) The peer interface, as a connection object. Not present if there's no peer. .SS Connection object fields .TP info .I (array) Array of strings. Contains additional information about the connection between the two interfaces, formatted. May be an empty array. .TP target .I (string) Id of the interface that is being linked to. .SS Address object fields .TP family .I (string) Currently only "INET" or "INET6". More types will be added in the future (without considering it a format change). .TP address .I (string) Address formatted for user consumption. May include net mask. This field should be generally machine parseable. .TP peer .I (object) If present, the peer address corresponding to this address. It's of the address object type but cannot contain futher peer field. .SH SEE ALSO .BR plotnetcfg (8) .SH AUTHOR .B plotnetcfg was written and is maintained by Jiri Benc . plotnetcfg-0.4.1/plotnetcfg.8000066400000000000000000000043401255537235400161330ustar00rootroot00000000000000.TH plotnetcfg 8 "9 April 2015" .SH NAME plotnetcfg \- plot diagram of network configuration .SH SYNOPSIS .B plotnetcfg .RB [ options ] .SH DESCRIPTION .B plotnetcfg is a tool that scans networking configuration on the machine and outputs a diagram of the configuration hierarchy. By default, the output is in the .B dot language. The output can be converted by the .BR dot (1) tool from .BR graphviz (7) package to various formats such as PDF, PostScript or SVG. .B plotnetcfg is able to scan different network name spaces and figure out relations that span name spaces. It supports all kinds of network interfaces and understands specifics of VLANs, bridges, veth pairs and Open vSwitch. The tool has to be run under root or with both CAP_SYS_ADMIN and CAP_NET_ADMIN capabilities. See .BR capabilities (7) for details. .B plotnetcfg is lightweight with as few library dependencies as possible. Currently, the only dependencies are .B libc and .BR libjansson . Optionally, it can be statically linked against the latter which allows the .B plotnetcfg binary to be copied to a target machine and run there (provided it has the same or newer version of .B glibc as .B plotnetcfg was compiled against). See EXAMPLES for typical .B plotnetcfg usage. .SH OPTIONS .TP \fB-f\fr, \fB--format\fR=\fIFORMAT\fR Set output format. When not specified, .B dot is used. .TP \fB-F\fr, \fB--list-formats\fR Print available output formats. .TP \fB-D\fr, \fB--ovs-db\fR=\fIPATH\fR Path to the socket used to communicate with Open vSwitch database server. Only UNIX sockets are supported. The default is .BR /var/run/openvswitch/db.sock . .TP \fB-h\fR, \fB--help\fR Print short help and exit. .TP \fB--version\fR Print program version and exit. .SH EXAMPLES Display network configuration of the local machine: .RS .B plotnetcfg | dot -Tpdf | okular - .RE Save network configuration to a file for later analysis: .RS .B plotnetcfg > .I file .RE Create PDF with network configuration of a remote machine: .RS .B scp /usr/sbin/plotnetcfg .BI root@ target : .br .B ssh .BI root@ target .B ./plotnetcfg | dot -Tpdf > .I file.pdf .SH SEE ALSO .BR dot (1), .BR graphviz (7), .BR plotnetcfg-json (5) .SH AUTHOR .B plotnetcfg was written and is maintained by Jiri Benc . plotnetcfg-0.4.1/tunnel.c000066400000000000000000000027671255537235400153610ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include "handler.h" #include "if.h" #include "match.h" #include "netns.h" #include "utils.h" #include "tunnel.h" struct search_arg { int af; char raw[16]; }; static int match_tunnel(struct if_entry *entry, void *arg) { struct search_arg *data = arg; struct if_addr_entry *addr; if (!(entry->flags & IF_UP)) return 0; for (addr = entry->addr; addr; addr = addr->next) { if (addr->addr.family != data->af) continue; if (!memcmp(addr->addr.raw, data->raw, data->af == AF_INET ? 4 : 16)) return 1; } return 0; } struct if_entry *tunnel_find_iface(struct netns_entry *ns, const char *addr) { struct search_arg data; struct if_entry *result; data.af = raw_addr(data.raw, addr); if (data.af < 0) return NULL; if (match_if_heur(&result, ns, 0, NULL, match_tunnel, &data)) return NULL; return result; } plotnetcfg-0.4.1/tunnel.h000066400000000000000000000014571255537235400153610ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _TUNNEL_H #define _TUNNEL_H struct if_entry; struct netns_entry; struct if_entry *tunnel_find_iface(struct netns_entry *ns, const char *addr); #endif plotnetcfg-0.4.1/utils.c000066400000000000000000000035321255537235400152030ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include "if.h" #include "netns.h" #include "utils.h" struct generic_list { struct generic_list *next; }; void list_free(void *list, destruct_f destruct) { struct generic_list *cur = list, *next; while (cur) { next = cur->next; if (destruct) destruct(cur); free(cur); cur = next; } } char *ifstr(struct if_entry *entry) { static char buf[IFNAMSIZ + NAME_MAX + 2]; if (!entry->ns->name) /* root ns */ snprintf(buf, sizeof(buf), "/%s", entry->if_name); else snprintf(buf, sizeof(buf), "%s/%s", entry->ns->name, entry->if_name); return buf; } char *ifid(struct if_entry *entry) { static char buf[IFNAMSIZ + 2 * NAME_MAX + 6 + 1]; char *ns; ns = entry->ns->name; if (!ns) /* root ns */ ns = ""; if (entry->internal_ns) snprintf(buf, sizeof(buf), "//%s/%s/%s", entry->internal_ns, ns, entry->if_name); else snprintf(buf, sizeof(buf), "%s/%s", ns, entry->if_name); return buf; } int raw_addr(void *dst, const char *src) { int af; if (strchr(src, ':')) af = AF_INET6; else af = AF_INET; if (inet_pton(af, src, dst) <= 0) return -1; return af; } plotnetcfg-0.4.1/utils.h000066400000000000000000000021271255537235400152070ustar00rootroot00000000000000/* * This file is a part of plotnetcfg, a tool to visualize network config. * Copyright (C) 2014 Red Hat, Inc. -- Jiri Benc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef _UTILS_H #define _UTILS_H struct if_entry; #define _unused __attribute__((unused)) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) typedef void (*destruct_f)(void *); void list_free(void *list, destruct_f destruct); /* Returns static buffer. */ char *ifstr(struct if_entry *entry); char *ifid(struct if_entry *entry); /* dst has to be allocated, at least 16 bytes. Returns the family or -1. */ int raw_addr(void *dst, const char *src); #endif plotnetcfg-0.4.1/version000066400000000000000000000000071255537235400153010ustar00rootroot00000000000000v0.4.1