pax_global_header00006660000000000000000000000064121514274730014520gustar00rootroot0000000000000052 comment=508db15f652eebd34a7d76e2384be48b2ebaa208 lldpad-0.9.46/000077500000000000000000000000001215142747300130605ustar00rootroot00000000000000lldpad-0.9.46/.gitignore000066400000000000000000000005441215142747300150530ustar00rootroot00000000000000parse_cli.c *.o *.lo *.tab.c .deps/ .libs/ .pc/ Makefile Makefile.in aclocal.m4 autom4te.cache/ autoscan.log config.guess config.log config.status config.sub configure configure.scan depcomp include/version.h install-sh liblldp_clif.* libtool lldpad.pc lldpad.spec ltmain.sh m4/ missing dcbtool lldpad lldptool nltest vdptest qbg22sim patches/ tags ar-lib lldpad-0.9.46/COPYING000066400000000000000000000443571215142747300141300ustar00rootroot00000000000000 "This software program is licensed subject to the GNU General Public License (GPL). Version 2, June 1991, available at " GNU General Public License Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 Library 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. 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. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. signature of Ty Coon, 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 Library General Public License instead of this License. lldpad-0.9.46/ChangeLog000066400000000000000000000102341215142747300146320ustar00rootroot00000000000000Changes from 0.9.45 tthrough 0.9.46 - Add IEEE802.1Qbg EVB module support - add and improve test code evbest/nltest - 8021qaz fix to work with hardware that does not support CEE DCBX - support lldpad running on bonded interfaces - pretty print support for VDP - new -p option to display lldpad process identifier Changes from 0.9.44 tthrough 0.9.45 - generate shared library liblldp_clif for applications to use - Add vdptest program - various link event fixes Changes from 0.9.43 through 0.9.44 - Support for multiple agent types Nearest non-TPMR, Nearest customer bridge, and Nearest Bridge (always supported). - New man pages for VDP, EVB, and MED have been added. - DCBX improvements to remap DCB attributes when the host has less traffic classes then the peer. - Support to parse 802.1AB Organizationally Specific TLVs, Cisco Specific TLVs and the LLINK attributes of the DCBX CEE TLVs. - Support to reconfigure and set the MSAP subtype. - Additional gcc warnings turned on and miscellaneous fixes. Changes from 0.9.40 through 0.9.43 - New client interface (abstract sockets) to support initrd stage - IEEE 802.1Qaz support has been added - EVB support has been added Changes from 0.9.39 through 0.9.40 - Add iSCSI TLV support Changes from 0.9.26 through 0.9.39 - Numerous bug fixes and code cleanup - Reworked netlink event handling to be more robust and handle dropped nlmsgs. - DCBx puts netdevice into linkmode until dcbx negotiation complete Changes from 0.9.21 through 0.9.26 - Fork dcbd into lldpad and make the daemon a generic LLDP agent deamon. Changes from 0.9.19 through 0.9.21 - Update libconfig to version 1.3.2. Fix code to match API changes. - Add dcbd -k -s options in README and manpage. - Fix dcbd seg fault when dcbd.conf version string is not 1.0. - Move the dcbd.conf out of the /etc/sysconfig/ directory into /var/lib/dcbd. Changes from 0.9.15 through 0.9.19 - Send App priority information to driver via netlink (if kernel supports) - dcbtool returns non-zero status on error - Allow user priority percentages (uppct) to be changed while using DCBX v2. - Improved dcbtool parameter parsing to detect invalid arguments. - Fixed segfault which occurred in some cases of the config file was removed. - Fixed dcbtool get app oper error. Changes from 0.9.7 through 0.9.15 - Set FeatureSeqNo to 1 in the case of peer expiration. - Use the MSG_DONTWAIT flag in the recv socket call to avoid potential of making a blocking call - which would hang dcbd. - Move initialization of control interface before call to init ports to avoid use of uninitialized variable. - Add support of SAN MAC address, get SAN MAC address through driver interface. - Add -s, -k command line options. - Dcbd, dcbtool man pages and README are updated. - Set the SO_PRIORITY of the LLDP socket to TC_PRIO_CONTROL (7). This will help the driver to know not to priority tag the LLDP packets. - Add shared memory segment to save some DCBX state information when dcbd stops. Used to restore DCBX state on restart of dcbd. - Change default dcbx version to 2. - Only send the version 2 DCBX TLV when in dcbx version 2 mode (instead of both version TLVs). - Fix dcbd init script so that it loads the dcbnl module for kernels which have the option of a dcbnl module. - A dcbtool error message was changed from "Device not found" to "Device not found, link down or DCB not enabled" - Each DCB feature (PFC, PG, ...) was modified in driver to indicate whether or not a hw reset is required when the feature configuration changes. If a HW reset will occur, then dcbd will not react to the next link bounce. - Ensure that HW flow control settings are synced with the DCBX operational state. - Runlevel 4 removed from the dcbd init script. - Fix error flag for willing==willing case. - Don't print out error message anymore in remove_adapter for the default config object for objects which aren't created for the default config. - Fixed potential memory leak in rare shutdown scenarios - Deleted line of unreachable dead code. - Remove BCN support from dcbd and dcbtool - Limit values for DcbxSubtype - Write Operational DcbxSubtype for query - For dcbx version 2, rebalance bandwidth % assignements on set of Priority Group feature lldpad-0.9.46/Makefile.am000066400000000000000000000131271215142747300151200ustar00rootroot00000000000000# target programs to be installed in ${sbindir} sbin_PROGRAMS = lldpad dcbtool lldptool # package nltest and vdptest, but do not install it anywhere if BUILD_DEBUG noinst_PROGRAMS = nltest vdptest qbg22sim endif # look for header files in the include directory # pass the value of ${sysconfdir} to the C pre-processor as SYSCONFDIR AM_CPPFLAGS = -I${srcdir}/include -DSYSCONFDIR="\"${sysconfdir}\"" \ -D_FORTIFY_SOURCE=2 ACLOCAL_AMFLAGS = -I m4 parse_cli.o: CFLAGS+=-U_FORTIFY_SOURCE -Wno-error ## system requires a shared libconfig AM_CFLAGS = -Wall -Werror -Wextra -Wformat=2 $(LIBCONFIG_CFLAGS) AM_LDFLAGS = $(LIBCONFIG_LIBS) ## header files to be installed, for programs using the client interface to lldpad lldpad_includedir= ${includedir}/lldpad lldpad_include_HEADERS = include/dcb_types.h \ include/clif.h include/lldp_dcbx_cmds.h \ include/lldpad.h include/lldp_mand_cmds.h \ include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \ include/lldp_med_cmds.h include/lldp_util.h \ include/lldp_evb_cmds.h include/lldp_vdp_cmds.h include/lldp_8021qaz_cmds.h \ include/clif_sock.h include/linux/dcbnl.h \ include/linux/netlink.h include/linux/rtnetlink.h \ include/lldpad_status.h noinst_HEADERS = include/config.h include/ctrl_iface.h \ include/dcb_driver_interface.h \ include/dcb_events.h include/dcb_persist_store.h include/dcb_protocol.h \ include/dcb_rule_chk.h include/lldp_dcbx_nl.h include/eloop.h \ include/lldpad_shm.h include/event_iface.h include/messages.h \ include/parse_cli.h include/version.h include/lldptool_cli.h include/list.h \ include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \ include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldp_evb_clif.h \ include/lldp_evb22_clif.h include/lldp_vdp_clif.h include/lldp_vdpnl.h \ include/lldp_8021qaz_clif.h \ include/lldp_orgspec_clif.h include/lldp_cisco_clif.h \ include/lldptool.h include/lldp_rtnl.h include/dcbtool.h include/lldp_dcbx_cfg.h lldpad_SOURCES = lldpad.c config.c lldp_dcbx_nl.c ctrl_iface.c \ event_iface.c eloop.c lldp_dcbx_cmds.c log.c lldpad_shm.c \ dcb_protocol.c dcb_rule_chk.c list.c lldp_rtnl.c \ $(lldpad_include_HEADERS) $(noinst_HEADERS) \ lldp/ports.c lldp/agent.c lldp/l2_packet_linux.c lldp/tx.c \ lldp/rx.c lldp/agent.h lldp/l2_packet.h lldp/mibdata.h lldp/ports.h \ lldp/states.h include/lldp.h include/lldp_mod.h \ lldp_dcbx.c include/lldp_dcbx.h tlv_dcbx.c include/tlv_dcbx.h \ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h lldp_util.c \ lldp_mand.c include/lldp_mand.h \ lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \ lldp_evb_cmds.c lldp_evb.c include/lldp_evb.h lldp_vdp_cmds.c \ include/lldp_vdp_cmds.h \ include/lldp_ecp.h include/lldp_qbg_utils.h lldp_ecp.c lldp_qbg_utils.c \ lldp_vdp.c include/lldp_vdp.h \ lldp_tlv.c include/lldp_tlv.h \ lldp_basman.c include/lldp_basman.h \ lldp_med.c include/lldp_med.h \ lldp_8023.c include/lldp_8023.h \ lldp_8021qaz.c include/lldp_8021qaz.h \ lldp_8021qaz_cmds.c include/lldp_8021qaz_cmds.h \ include/lldp_evb22.h qbg/lldp_evb22.c qbg/lldp_evb22_cmds.c \ include/lldp_qbg22.h include/lldp_ecp22.h qbg/lldp_ecp22.c \ include/lldp_vdp22.h qbg/lldp_vdp22.c qbg/lldp_vdpnl.c lib_LTLIBRARIES = liblldp_clif.la liblldp_clif_la_LDFLAGS = -version-info 1:0:0 liblldp_clif_includedir = ${srcdir}/include liblldp_clif_la_SOURCES = clif.c dcbtool_SOURCES = dcbtool.c dcbtool_cmds.c parse_cli.l \ weak_readline.c $(lldpad_include_HEADERS) $(noinst_HEADERS) dcbtool_LDADD = ${srcdir}/liblldp_clif.la dcbtool_LDFLAGS = -ldl -llldp_clif lldptool_SOURCES = lldptool.c lldptool_cmds.c lldp_rtnl.c \ lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c \ lldp_8023_clif.c lldp_dcbx_clif.c lldp_util.c \ lldp_8021qaz_clif.c lldp_evb_clif.c lldp_vdp_clif.c \ lldp_orgspec_clif.c lldp_cisco_clif.c qbg/lldp_evb22_clif.c \ weak_readline.c $(lldpad_include_HEADERS) $(noinst_HEADERS) lldptool_LDADD = ${srcdir}/liblldp_clif.la lldptool_LDFLAGS = -ldl -llldp_clif if BUILD_DEBUG nltest_SOURCES = test/nltest.c test/nltest.h vdptest_SOURCES = test/vdptest.c vdptest_LDFLAGS = -llldp_clif qbg22sim_SOURCES = test/qbg22sim.c qbg22sim_LDFLAGS = -lrt endif ## man pages dist_man_MANS = docs/lldpad.8 docs/dcbtool.8 docs/lldptool.8 \ docs/lldptool-ets.8 docs/lldptool-pfc.8 docs/lldptool-app.8 \ docs/lldptool-evb.8 docs/lldptool-vdp.8 docs/lldptool-med.8 \ docs/lldptool-dcbx.8 \ docs/lldptool-evb22.8 if BUILD_DEBUG nodist_man_MANS = test/qbg22sim.1 test/vdptest.1 endif ## force the creation of an empty configuration directory at install time lldpadconfigdir = /var/lib/lldpad installdirs-local: $(MKDIR_P) $(DESTDIR)$(lldpadconfigdir) install-data-hook: installdirs-local ## pkg-config data file, for client interface software to find headers pkgconfigdir = ${libdir}/pkgconfig pkgconfig_DATA = lldpad.pc liblldp_clif.pc ## put a spec file and documentation in the distribution archive dist_noinst_DATA = lldpad.spec README COPYING ChangeLog ## lldpad.init is listed here because it gets installed from install-data-local dist_noinst_SCRIPTS = lldpad.init ## special hooks to handle the init script install-data-local: lldpad.init $(MKDIR_P) $(DESTDIR)/etc/init.d $(INSTALL_SCRIPT) lldpad.init $(DESTDIR)/etc/init.d/lldpad BASH_COMPLETION_DIR=/etc/bash_completion.d/ install-data-hook: /sbin/chkconfig --add lldpad || true ## provide support for bash completion $(MKDIR_P) $(DESTDIR)/$(BASH_COMPLETION_DIR) $(INSTALL_SCRIPT) ${srcdir}/contrib/bash_completion/* $(DESTDIR)/$(BASH_COMPLETION_DIR) uninstall-local: /sbin/chkconfig --del lldpad || true rm -f '$(DESTDIR)/etc/init.d/lldpad' rm -f '$(includedir)/dcbd/clif_cmds.h' rm -f '$(includedir)/dcbd' lldpad-0.9.46/README000066400000000000000000000527021215142747300137460ustar00rootroot00000000000000Link Layer Discovery Protocol (LLDP) Agent with Data Center Bridging (DCB) for Intel(R) Network Connections =========================================================== April 7, 2010 Contents ======== - Background - Requirements - Functionality - How To Build a DCB capable system - Setup - Operation - Testing - dcbtool Overview - FAQ - Known Issues - License - Support Background on DCB support ========================= In the 2.4.x kernel, qdiscs were introduced. The rationale behind this effort was to provide QoS in software, as hardware did not provide the necessary interfaces to support it. In 2.6.23, Intel introduced the idea of multiqueue support into the qdisc layer. This provides a mechanism to map the software queues in the qdisc structure into multiple hardware queues in underlying devices. In the case of Intel adapters, this mechanism is used to map qdisc queues onto the queues within the hardware controllers. Within the Data Center, the perception is that traditional Ethernet is subject to high latency and is prone to losing frames, rendering it unacceptable for storage applications In an effort to address these issues, Intel and a host of industry leaders have been working on addressing these problems. Specifically, within the IEEE 802.1 standards body there are a number of task forces working on enhancements to address these concerns. Listed below are the applicable standards: Enhanced Transmission Selection: IEEE 802.1Qaz Lossless Traffic Class Priority Flow Control: IEEE 802.1Qbb Congestion Notification: IEEE 802.1Qau DCB Capability exchange protocol (DCBX): IEEE 802.1Qaz The software solution that is being released represents Intel's implementation of these efforts. It is worth noting that many of these standards have not been ratified - this is a pre-standards release, so users are advised to check Sourceforge often. While we have worked with some of the major ecosystem vendors in validating this release, there are many vendors which still have solutions in development. As these solutions become available and standards get ratified, we will work with ecosystem partners and the standards body to ensure that the Intel solution works as expected. Requirements ============ - Linux kernel version 2.6.29 or later. - 2.6.29 or newer version of the "iproute2" package should be downloaded and installed in order to obtain a multi-queue aware version of the 'tc' utility. Check for new versions at: http://www.linuxfoundation.org/en/Net:Iproute2 - Version 2.5.33 of Flex should be installed (to support iproute2). Flex source can be obtained from http://flex.sourceforge.net/ - An up to date netlink library needs to be installed in order to compile lldpad. - Version 1.3.2 or greater of libconfig must be installed. libconfig source can be obtained from http://www.hyperrealm.com/libconfig/ - For DCB features - a driver which supports the dcbnl rtnetlink interface in the kernel (e.g. the ixgbe driver for 82598-based or 82599-based Intel adapters). Functionality ============= lldpad - Executes the Link Layer Discovery Protocol (LLDP) over all supported interfaces. - Executes the DCB capabilities exchange protocol (DCBX) to exchange DCB configuration with the peer device using LLDP on support interfaces. - Supports the versions of the DCB capabilities exchange protocol described here: - version 1 and here: - version 2 - Retrieves and stores LLDP and DCB configuration to a configuration file. - Controls the DCB settings of the network driver based on the operation of the DCB capabilities exchange protocol. Interaction with a supporting network driver is achieved via DCB operations added to the rtnetlink interface in kernel 2.6.29. - Supports the following DCB features: Priority Group, Priority Flow Control, FCoE, and FCoE Logical Link Status. - Provides an interface for client applications to query and configure DCB features. Generates client interface events when the operational configuration or state of a feature changes. lldptool - dcbtool - Interacts with lldpad via the client interface. - Queries the state of the local, operational and peer configuration for the supported DCB features. - Supports configuring the supported DCB features. - Interactive mode allows multiple commands to be entered interactively, as well as displaying event messages. - Enables or disables DCB for an interface. How To Build a DCB-Capable System ================================= Linux kernel install: --------------------- 1. Requires 2.6.29 kernel. 2. Untar and make the kernel. Listed below are the required kernel options: Required configuration options: From make menuconfig a. In Networking support -> Networking options, enable Data Center Bridging b. In Networking support -> Networking options -> QoS and/or fair queuing, enable: Hardware Multiqueue-aware Multi Band Queuing (MULTIQ) Multi Band Priority Queueing (PRIO) Elementary classification ( BASIC ) Universal 32bit comparisons w/ hashing (U32) Extended Matches and make sure U32 key is selected Actions -> SKB Editing c. To enable ixgbe driver support for DCB, In Device Drivers -> Network device support -> Ethernet (10000 Mbit) enable Intel(R) 10GbE PCI Express adapters support and the Data Center Bridging (DCB) Support option 3. Build the kernel. 4. lldpad requires updated kernel header files to compile. Create a link from /usr/include/linux to the location of the kernel source include files. For example: ln -s /usr/src/kernels/linux-2.x.xx.x/include/linux /usr/include/linux lldpad Application Install -------------------------- 1. Download iproute2 from the web. See: http://www.linuxfoundation.org/en/Net:Iproute2 Please ensure that you use the version that corresponds to the kernel version that you are using. Follow the build/installation instructions in the README. Typically, the commands: ./configure; make; make install will work. 2. Download the latest version of the lldpad-x.y.z tarball from the e1000 project in Sourceforge and untar it. Check to see that the following libraries are installed; install them if they are not present. libtool; libconfig-devel; libnl-devel; readline-devel Go into the lldpad-x.y.z directory and run the following commands ./bootstrap.sh; ./configure --prefix=/usr; make; make install This will build and install 'lldpad', 'lldptool' and 'dcbtool' to /usr/sbin, make the '/var/lib/lldpad' directory (default location of the lldpad.conf file) and setup lldpad to run as a system service using the chkconfig program. Verify that the lldpad service is working as expected with the 'service lldpad status' command. If the service is not on, issue the command 'service lldpad start' Note (known issue): if the --prefix option is not supplied to the configure script, then the default install location of 'lldpad', 'lldptool' and 'dcbtool' is /usr/local/sbin. This does not match the 'lldpad' script installed in /etc/init.d, which is coded to start lldpad from /usr/sbin/lldpad. lldpad will create the lldpad.conf file if it does not exist. For development purposes, 'lldpad' can be run directly from the build directory. Options ------- lldpad has the following command line options: -h show usage information -f configfile use the specified file as the config file instead of the default file - /var/lib/lldpad/lldpad.conf -d run lldpad as a daemon -v show lldpad version -k terminate current running lldpad -s remove lldpad state records SETUP: ====== 1. Load the ixgbe module. 2. Verify lldpad service is functional. If lldpad was installed, do "service lldpad status" to check, "service lldpad start" to start. Or, run "lldpad -d" from the command line to start. 3. Enable DCB on the selected ixgbe port: dcbtool sc ethX dcb on 4. The dcbtool command can be used to query and change the DCB configuration (ie., various percentages to different queues). Use dcbtool -h to see a list of options. DCBX Operation ============== lldpad and dcbtool can be used to configure a DCB capable driver, such as the ixgbe driver, which supports the rtnetlink DCB interface. Once the DCB features are configured, the next step is to classify traffic to be identified with an 802.1p priority and the associated DCB features. This can be done using a couple different methods. 1. qdisc and filter method The skbedit action mechanism can be used in a tc filter to classify traffic patterns to a specific queue_mapping value from 0-7. The ixgbe driver will place traffic with a given queue_mapping value onto the corresponding hardware queue and tag the outgoing frames with the corresponding 802.1p priority value. Set up the multi-queue qdisc for the selected interface: # tc qdisc add dev ethX root handle 1: multiq Setting the queue_mapping in a TC filter allows the ixgbe driver to classify a packet into a queue. Here is an example of a filter to cause iSCSI traffic (running on the well known port number 3260) to be tagged with priority 4. # tc filter add dev ethX protocol ip parent 1: u32 match ip dport 3260 \ 0xffff action skbedit queue_mapping 4 # tc filter add dev ethX protocol ip parent 1: u32 match ip sport 3260 \ 0xffff action skbedit queue_mapping 4 Here is an example that sets up a filter based on EtherType. In this example, the EtherType is 0x8906. # tc filter add dev ethX protocol 802_3 parent 1: handle 0xfc0e basic match \ 'cmp(u16 at 12 layer 1 mask 0xffff eq 35078)' action skbedit queue_mapping 3 2. VLAN egress map method Alternatively, traffic can be classified by configuring the egress map of a VLAN interface. For example, if iSCSI traffic is desired to be on priority 4 and VLAN 101 is dedicated for iSCSI, then the following configuration could be performed to map the skb_priorities on VLAN 101 to the 802.1p priority 4. # vconfig set_egress_map ethX.101 0 4 # vconfig set_egress_map ethX.101 1 4 # vconfig set_egress_map ethX.101 2 4 ... # vconfig set_egress_map ethX.101 7 4 Note: since DCB features use the 802.1p priority in the VLAN tag to distinguish traffic, it is expected that traffic in a DCB network will need to be on a tagged VLAN. Testing ======= To test in a back-to-back setup, use the following tc commands to setup the qdisc and filters for TCP ports 5000 through 5007. Then use a tool, such as iperf, to generate UDP or TCP traffic on ports 5000-5007. Statistics for each queue of the ixgbe driver can be checked using the ethtool utility: ethtool -S ethX # tc qdisc add dev ethX root handle 1: multiq # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5000 0xffff action skbedit queue_mapping 0 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5000 0xffff action skbedit queue_mapping 0 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5001 0xffff action skbedit queue_mapping 1 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5001 0xffff action skbedit queue_mapping 1 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5002 0xffff action skbedit queue_mapping 2 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5002 0xffff action skbedit queue_mapping 2 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5003 0xffff action skbedit queue_mapping 3 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5003 0xffff action skbedit queue_mapping 3 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5004 0xffff action skbedit queue_mapping 4 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5004 0xffff action skbedit queue_mapping 4 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5005 0xffff action skbedit queue_mapping 5 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5005 0xffff action skbedit queue_mapping 5 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5006 0xffff action skbedit queue_mapping 6 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5006 0xffff action skbedit queue_mapping 6 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip dport 5007 0xffff action skbedit queue_mapping 7 # tc filter add dev ethX protocol ip parent 1: \ u32 match ip sport 5007 0xffff action skbedit queue_mapping 7 dcbtool Overview ================= dcbtool is used to query and set the DCB settings of a DCB capable Ethernet interface. It connects to the client interface of lldpad to perform these operations. dcbtool will operate in interactive mode if it is executed without a command. In interactive mode, dcbtool also functions as an event listener and will print out events received from lldpad as they arrive. SYNOPSIS dcbtool -h dcbtool -v dcbtool [-rR] dcbtool [-rR] [command] [command arguments] OPTIONS -h shows the dcbtool usage message -v shows dcbtool version information -r displays the raw lldpad client interface messages as well as the readable output. -R displays only the raw lldpad client interface messages COMMANDS help shows the dcbtool usage message ping test command. The lldpad daemon responds with "PONG" if the client interface is operational. license displays dcbtool license information quit exits from interactive mode The following commands interact with the lldpad daemon to manage the dae- mon and DCB features on DCB capable interfaces. lldpad general configuration commands: ------------------------------------ dcbx gets the configured or operational version of the DCB capabili- ties exchange protocol. If different, the configured version will take effect (and become the operational version) after lldpad is restarted. sc dcbx v:[1|2] sets the version of the DCB capabilities exchange protocol which will be used the next time lldpad is started. The default version is 2. Changes take effect on next lldpad restart. Information about version 1 can be found at: Information about version 2 can be found at: DCB per-interface commands: --------------------------- gc gets configuration of feature on interface ifname. go gets operational status of feature on interface ifname. gp gets peer configuration of feature on interface ifname. sc sets the configuration of feature on interface ifname. feature may be one of the following: ------------------------------------ dcb DCB state of the port pg priority groups pfc priority flow control app: application specific data ll: logical link status subtype can be: --------------- 0|fcoe Fiber Channel over Ethernet (FCoE) args can include: ----------------- e:<0|1> controls feature enable a:<0|1> controls whether the feature is advertised via DCBX to the peer w:<0|1> controls whether the feature is willing to change its opera- tional configuration based on what is received from the peer [feature specific args] arguments specific to a DCB feature Feature specific arguments for dcb: ----------------------------------- on|off enable or disable DCB for the interface. The go and gp commands are not needed for the dcb feature. Also, the enable, advertise and willing parameters are not required. Feature specific arguments for pg: ---------------------------------- pgid:xxxxxxxx Priority group ID for the 8 priorities. From left to right (priorities 0-7), x is the corresponding priority group ID value, which can be 0-7 for priority groups with bandwidth allo- cations or f (priority group ID 15) for the unrestricted prior- ity group. pgpct:x,x,x,x,x,x,x,x Priority group percentage of link bandwidth. From left to right (priority groups 0-7), x is the percentage of link bandwidth allocated to the corresponding priority group. The total band- width must equal 100%. uppct:x,x,x,x,x,x,x,x Priority percentage of priority group bandwidth. From left to right (priorities 0-7), x is the percentage of priority group bandwidth allocated to the corresponding priority. The sum of percentages for priorities which belong to the same priority group must total 100% (except for priority group 15). strict:xxxxxxxx Strict priority setting. From left to right (priorities 0-7), x is 0 or 1. 1 indicates that the priority may utilize all of the bandwidth allocated to its priority group. up2tc:xxxxxxxx Priority to traffic class mapping. From left to right (priori- ties 0-7), x is the traffic class (0-7) to which the priority is mapped. (This setting is ignored for Intel 82598-based adapters.) Feature specific arguments for pfc: ----------------------------------- pfcup:xxxxxxxx Enable/disable priority flow control. From left to right (pri- orities 0-7), x is 0 or 1. 1 indicates that the corresponding priority is configured to transmit priority pause. Feature specific arguments for app:< subtype>: ---------------------------------------------- appcfg:xx xx is a hexadecimal value representing an 8 bit bitmap where bits set to 1 indicate the priority which frames for the applications specified by subtype should use. The lowest order bit maps to priority 0. Feature specific arguments for ll:: -------------------------------------------- status:[0|1] For testing purposes, the logical link status may be set to 0 or 1. This setting is not persisted in the configuration file. EXAMPLES Enable DCB on interface eth2 dcbtool sc eth2 dcb on Assign priorities 0-3 to priority group 0, priorities 4-6 to priority group 1 and priority 7 to the unrestricted priority. Also, allocate 25% of link bandwidth to priority group 0 and 75% to group 1. dcbtool sc eth2 pg pgid:0000111f pgpct:25,75,0,0,0,0,0,0 Enable transmit of Priority Flow Control for priority 3 and assign FCoE to priority 3. dcbtool sc eth2 pfc pfcup:00010000 dcbtool sc eth2 app:0 appcfg:08 FAQ === - How did Intel verify their DCB solution? Answer - The Intel solution is continually evolving as the relevant standards become solidified and more vendors introduce DCB capable systems. That said, we initially used test automation to verify the DCB state machine. As the state machine became more robust and we had DCB capable hardware, we began to test back to back with our adapters. Finally, we introduced DCB capable switches in our test bed. Known Issues ============ - Prior to kernel 2.6.26, TSO will be disabled when the driver is put into DCB mode. - A TX unit hang may be observed when link strict priority is set when a large amount of traffic is transmitted on the link strict priority. License ======= lldpad, lldptool and dcbtool - LLDP daemon with DCB support and command line utilities for LLDP and DCB configuration Copyright(c) 2007-2010 Intel Corporation. Portions of lldpad, lldptool and dcbtool are based on: hostapd-0.5.7 Copyright (c) 2004-2007, Jouni Malinen This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Support ======= Contact Information: LLDP-devel Mailing List Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 lldpad-0.9.46/bootstrap.sh000077500000000000000000000000661215142747300154360ustar00rootroot00000000000000#! /bin/sh -e libtoolize exec autoreconf --install -s lldpad-0.9.46/clif.c000066400000000000000000000141411215142747300141420ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "clif.h" #include "clif_msgs.h" #if defined(CONFIG_CLIF_IFACE_UNIX) || defined(CONFIG_CLIF_IFACE_UDP) #define CLIF_IFACE_SOCKET #endif /* CONFIG_CLIF_IFACE_UNIX || CONFIG_CLIF_IFACE_UDP */ struct clif *clif_open() { struct clif *clif; socklen_t addrlen; clif = malloc(sizeof(*clif)); if (clif == NULL) return NULL; memset(clif, 0, sizeof(*clif)); clif->s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (clif->s < 0) { perror("socket"); free(clif); return NULL; } clif->local.sun_family = AF_LOCAL; clif->local.sun_path[0] = '\0'; snprintf(&clif->local.sun_path[1], sizeof(clif->local.sun_path) - 1, "%s/%d", LLDP_CLIF_SOCK, getpid()); addrlen = sizeof(sa_family_t) + strlen(clif->local.sun_path + 1) + 1; if (bind(clif->s, (struct sockaddr *) &clif->local, addrlen) < 0) { perror("bind"); close(clif->s); free(clif); return NULL; } clif->dest.sun_family = AF_LOCAL; clif->dest.sun_path[0] = '\0'; snprintf(&clif->dest.sun_path[1], sizeof(clif->dest.sun_path) - 1, "%s", LLDP_CLIF_SOCK); addrlen = sizeof(sa_family_t) + strlen(clif->dest.sun_path + 1) + 1; if (connect(clif->s, (struct sockaddr *) &clif->dest, addrlen) < 0) { perror("connect"); close(clif->s); free(clif); return NULL; } return clif; } void clif_close(struct clif *clif) { close(clif->s); free(clif); } int clif_request(struct clif *clif, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; int res; fd_set rfds; const char *_cmd; size_t _cmd_len; _cmd = cmd; _cmd_len = cmd_len; if (send(clif->s, _cmd, _cmd_len, 0) < 0) return -1; for (;;) { tv.tv_sec = CMD_RESPONSE_TIMEOUT; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(clif->s, &rfds); res = select(clif->s + 1, &rfds, NULL, NULL, &tv); if (FD_ISSET(clif->s, &rfds)) { res = recv(clif->s, reply, *reply_len, 0); if (res < 0) { printf("less then zero\n"); return res; } if ((res > 0 && reply[MSG_TYPE] == EVENT_MSG) || ((reply[MSG_TYPE] == MOD_CMD) && (res > MOD_MSG_TYPE) && (reply[MOD_MSG_TYPE] == EVENT_MSG))) { /* This is an unsolicited message from * lldpad, not the reply to the * request. Use msg_cb to report this to the * caller. */ if (msg_cb) { /* Make sure the message is nul * terminated. */ if ((size_t) res == *reply_len) res = (*reply_len) - 1; reply[res] = '\0'; msg_cb(reply, res); } continue; } *reply_len = res; break; } else { printf("timeout\n"); return -2; } } return 0; } static int clif_attach_helper(struct clif *clif, char *tlvs_hex, int attach) { char *buf; char rbuf[10]; int ret; size_t len = 10; /* Allocate maximum buffer usage */ if (tlvs_hex && attach) { buf = malloc(sizeof(char)*(strlen(tlvs_hex) + 2)); if (!buf) return -1; sprintf(buf, "%s%s","A",tlvs_hex); } else if (attach) { buf = malloc(sizeof(char) * 2); if (!buf) return -1; sprintf(buf, "A"); } else { buf = malloc(sizeof(char) * 2); if (!buf) return -1; sprintf(buf, "D"); } ret = clif_request(clif, buf, strlen(buf), rbuf, &len, NULL); free(buf); if (ret < 0) return ret; if (len == 4 && memcmp(rbuf, "R00", 3) == 0) return 0; return -1; } int clif_attach(struct clif *clif, char *tlvs_hex) { return clif_attach_helper(clif, tlvs_hex, 1); } int clif_detach(struct clif *clif) { return clif_attach_helper(clif, NULL, 0); } int clif_recv(struct clif *clif, char *reply, size_t *reply_len) { int res; res = recv(clif->s, reply, *reply_len, 0); if (res < 0) return res; *reply_len = res; return 0; } int clif_pending(struct clif *clif) { struct timeval tv; fd_set rfds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(clif->s, &rfds); select(clif->s + 1, &rfds, NULL, NULL, &tv); return FD_ISSET(clif->s, &rfds); } int clif_get_fd(struct clif *clif) { return clif->s; } /* * Get PID of lldpad from 'ping' command */ pid_t clif_getpid(void) { struct clif *clif_conn; char buf[MAX_CLIF_MSGBUF]; size_t len; char *ppong; int ret; pid_t lldpad = 0; /* LLDPAD process identifier */ clif_conn = clif_open(); if (!clif_conn) { fprintf(stderr, "couldn't connect to lldpad\n"); return 0; } if (clif_attach(clif_conn, NULL)) { fprintf(stderr, "failed to attach to lldpad\n"); clif_close(clif_conn); return 0; } ret = clif_request(clif_conn, "P", 1, buf, &len, NULL); if (ret == -2) { fprintf(stderr, "connection to lldpad timed out\n"); goto out; } if (ret < 0) { fprintf(stderr, "ping command failed\n"); goto out; } buf[len] = '\0'; ppong = strstr(buf, "PPONG"); /* Ignore leading chars */ if (!ppong || sscanf(ppong, "PPONG%d", &lldpad) != 1) { fprintf(stderr, "error parsing pid of lldpad\n"); lldpad = 0; } out: clif_detach(clif_conn); clif_close(clif_conn); return lldpad; } lldpad-0.9.46/config.c000066400000000000000000000500451215142747300144750ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "eloop.h" #include "lldpad.h" #include "lldp.h" #include "lldp/ports.h" #include "lldp/agent.h" #include "lldp/l2_packet.h" #include "lldp_util.h" #include "lldp_mod.h" #include "lldp_mand_clif.h" #include "lldp_med.h" #include "dcb_protocol.h" #include "dcb_persist_store.h" #include "messages.h" #include "config.h" #include "lldpad_status.h" #include "lldp_mod.h" #include "event_iface.h" config_t lldpad_cfg; /* * init_cfg - initialze the global lldpad_cfg via config_init * * Returns true (1) for succes and false (0) for failed * * check to see if lldpad_cfs is already initailzied */ int init_cfg(void) { const char *p; int err = 1; config_init(&lldpad_cfg); if (check_cfg_file()) { err = 0; LLDPAD_INFO("%s: failed to create config file\n", __func__); } else if (!config_read_file(&lldpad_cfg, cfg_file_name)) { err = 0; LLDPAD_INFO("%s: config file failed to load\n", __func__); } else if (config_lookup_string(&lldpad_cfg, "version", &p)) { LLDPAD_INFO("%s: config file version incorrect ", __func__); LLDPAD_INFO("rebuild file with correct version.\n"); destroy_cfg(); remove(cfg_file_name); if (check_cfg_file() || !config_read_file(&lldpad_cfg, cfg_file_name)) err = 0; } return err; } /* * destroy_cfg - destroy the initialzed global lldpad_cfg * * Sets the cfgReturn true (1) for succes and false (0) for failed * * check to see if lldpad_cfg is already initialized */ void destroy_cfg(void) { config_destroy(&lldpad_cfg); } void scan_port(UNUSED void *eloop_data, UNUSED void *user_ctx) { struct port *port; struct if_nameindex *nameidx, *p; LLDPAD_INFO("%s: NLMSG dropped, scan ports.\n", __func__); nameidx = if_nameindex(); if (nameidx == NULL) { LLDPAD_DBG("if_nameindex error try again later\n"); goto error_out; } p = nameidx; /* Walk port list looking for devices that are not in if_nameindex. * If the device is in the port list but not in the if_nameindex * list then we missed a RTM_DELLINK event and the device is no * longer available, possibly because the module has been unloaded. * For this case lets remove the device from the ports list if it * comes back online we should receive a RTM_NEWLINK event and can * readd it there. */ port = porthead; while (port != NULL) { int found = 0; struct port *del; p = nameidx; while (p->if_index != 0) { if (!strncmp(p->if_name, port->ifname, MAX_DEVICE_NAME_LEN)) { /* Good device exists continue port walk */ found = 1; break; } p++; } del = port; port = port->next; if (!found) remove_port(del->ifname); } /* Walk port list looking for devices that should have been added * to our port list but have not most likely due to a dropped nlmsg. * At this point we need to add the device and call ops ifup routines. * The port enable state needs to be set to match the real link state * multiple link events and the port state is no longer reliable. * This is required because we currently do not know if we missed * IF_OPER_UP, IF_OPER_DOWN or IF_OPER_DORMANT. */ p = nameidx; while (p->if_index != 0) { struct lldp_module *np; const struct lldp_mod_ops *ops; char *ifname = p->if_name; struct lldp_agent *agent; if (!is_valid_lldp_device(ifname)) { p++; continue; } port = port_find_by_name(p->if_name); if (!port) port = add_port(p->if_name); if (port && check_link_status(ifname)) { set_port_oper_delay(ifname); oper_add_device(ifname); } else if (port) { LIST_FOREACH(agent, &port->agent_head, entry) { LLDPAD_DBG("%s: calling ifdown for agent %p.\n", __func__, agent); LIST_FOREACH(np, &lldp_head, lldp) { ops = np->ops; if (ops->lldp_mod_ifdown) ops->lldp_mod_ifdown(ifname, agent); } } set_lldp_port_enable(ifname, 0); } p++; } if_freenameindex(nameidx); return; error_out: eloop_register_timeout(INI_TIMER, 0, scan_port, NULL, NULL); return; } void create_default_cfg_file(void) { config_write_file(&lldpad_cfg, cfg_file_name); } /* check for existence of cfg file. If it does not exist, * create it. * input: cfg_file: if NULL, use default cfg file name * else, use passed in name * output: 0: no error * !0: errno of the call which failed */ int check_cfg_file(void) { int fd; int retval = 0; if (access(cfg_file_name, R_OK | W_OK)) { if (access(cfg_file_name, F_OK)) { LLDPAD_INFO("config file failed to load, "); LLDPAD_INFO("create a new file.\n"); fd = open(cfg_file_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { retval = errno; LLDPAD_ERR("error creating %s !\n", cfg_file_name); } else { close(fd); create_default_cfg_file(); } } else { retval = errno; LLDPAD_ERR("%s is not readable and writeable", cfg_file_name); } } return (retval); } static bool check_bool(int bool_setting) { if (bool_setting ==1 || bool_setting==0) return true; else return false; } static bool check_percentage(int percentage_setting) { if (percentage_setting < 0 ||percentage_setting > 100) return false; else return true; } static bool check_char(int int_setting) { if (int_setting < 0 || int_setting > 255) return false; else return true; } static bool check_int(int int_setting) { if (int_setting < 0 || int_setting > 1000000) return false; else return true; } static bool check_priority(int priority_setting) { if (priority_setting < dcb_none || priority_setting >= dcb_invalid) return false; else return true; } int get_int_config(config_setting_t *s, char *attr, int int_type, int *result) { config_setting_t *setting = NULL; int rval = false; setting = config_setting_get_member(s, attr); if (setting) { *result = (int)config_setting_get_int(setting); switch(int_type) { case TYPE_BOOL: rval = check_bool(*result); break; case TYPE_PERCENT: rval = check_percentage(*result); break; case TYPE_CHAR: rval = check_char(*result); break; case TYPE_INT: rval = check_int(*result); break; default: break; } } if (!rval) LLDPAD_ERR("invalid value for %s", attr); return rval; } int get_array_config(config_setting_t *s, char *attr, int int_type, int *result) { config_setting_t *setting = NULL; config_setting_t *setting_value = NULL; int rval = false; int i; setting = config_setting_get_member(s, attr); if (setting) { for (i = 0; iif_index != 0) { int valid = is_valid_lldp_device(p->if_name); if (!valid) { p++; continue; } port = add_port(p->if_name); if (port == NULL) { LLDPAD_ERR("%s: Error adding device %s\n", __func__, p->if_name); } else if (check_link_status(p->if_name)) { lldp_add_agent(p->if_name, NEAREST_BRIDGE); lldp_add_agent(p->if_name, NEAREST_NONTPMR_BRIDGE); lldp_add_agent(p->if_name, NEAREST_CUSTOMER_BRIDGE); LIST_FOREACH(agent, &port->agent_head, entry) { LLDPAD_DBG("%s: calling ifup for agent %p.\n", __func__, agent); LIST_FOREACH(np, &lldp_head, lldp) { if (np->ops->lldp_mod_ifup) np->ops->lldp_mod_ifup(p->if_name, agent); } } set_lldp_port_enable(p->if_name, 1); } p++; } if_freenameindex(nameidx); } static int set_config_value(config_setting_t *setting, union cfg_set v, int type) { config_int_t tmp_int; switch (type) { case CONFIG_TYPE_INT: tmp_int = (config_int_t)*v.pint; return config_setting_set_int(setting, tmp_int); case CONFIG_TYPE_INT64: return config_setting_set_int64(setting, *v.p64); case CONFIG_TYPE_FLOAT: return config_setting_set_float(setting, *v.pfloat); case CONFIG_TYPE_STRING: return config_setting_set_string(setting, *v.ppchar); case CONFIG_TYPE_BOOL: return config_setting_set_bool(setting, *v.pint); default: return CONFIG_FALSE; } } static int lookup_config_value(char *path, union cfg_get v, int type) { config_int_t tmp_int; int rc; switch (type) { case CONFIG_TYPE_INT: rc = config_lookup_int(&lldpad_cfg, path, &tmp_int); *v.pint = (int)tmp_int; return rc; case CONFIG_TYPE_INT64: return config_lookup_int64(&lldpad_cfg, path, v.p64); case CONFIG_TYPE_FLOAT: return config_lookup_float(&lldpad_cfg, path, v.pfloat); case CONFIG_TYPE_STRING: return config_lookup_string(&lldpad_cfg, path, v.ppchar); case CONFIG_TYPE_BOOL: return config_lookup_bool(&lldpad_cfg, path, v.pint); default: return CONFIG_FALSE; } } /* * get_config_setting - get the setting from the given config file path by type * @ifname: interface name * @agenttype: type of agent this needs to be retrieved from * @path: relative to LLDP_COMMON or ifname section of LLDP configuration. * @value: pointer to the value to be retrieved * @type: libconfig value types * * Returns cmd_success(0) for success, otherwise for failure. * * This function assumes init_cfg() has been called. */ int get_config_setting(const char *ifname, int agenttype, char *path, union cfg_get v, int type) { char p[1024]; int rval = CONFIG_FALSE; const char *section = agent_type2section(agenttype); /* look for setting in section->ifname area first */ if (ifname) { snprintf(p, sizeof(p), "%s.%s.%s", section, ifname, path); rval = lookup_config_value(p, v, type); } /* if not found look for setting in section->common area */ if (rval == CONFIG_FALSE) { snprintf(p, sizeof(p), "%s.%s.%s", section, LLDP_COMMON, path); rval = lookup_config_value(p, v, type); } return (rval == CONFIG_FALSE) ? cmd_failed : cmd_success; } int remove_config_setting(const char *ifname, int agenttype, char *parent, char *name) { char p[1024]; int rval = CONFIG_FALSE; config_setting_t *setting = NULL; const char *section = agent_type2section(agenttype); /* look for setting in section->ifname area first */ if (ifname) { snprintf(p, sizeof(p), "%s.%s.%s", section, ifname, parent); setting = config_lookup(&lldpad_cfg, p); } /* if not found look for setting in section->common area */ if (setting == NULL) { snprintf(p, sizeof(p), "%s.%s.%s", section, LLDP_COMMON, parent); setting = config_lookup(&lldpad_cfg, p); } if (setting != NULL) { rval = config_setting_remove(setting, name); if ((rval == CONFIG_TRUE) && !config_write_file(&lldpad_cfg, cfg_file_name)) { LLDPAD_DBG("config write failed\n"); rval = CONFIG_FALSE; } } return (rval == CONFIG_FALSE) ? cmd_failed : cmd_success; } /* calling get_config_setting() w/ init_cfg()/destroy_cfg() */ int get_cfg(const char *ifname, int agenttype, char *path, union cfg_get v, int type) { int rval; rval = get_config_setting(ifname, agenttype, path, v, type); return rval; } /* must be initially invoked with a scalar or array type * drills down path until it finds a part that exists (or not) and then * creates the settings on the way back up */ config_setting_t *find_or_create_setting(char *p, int type) { config_setting_t *setting = NULL; char *s; int i; setting = config_lookup(&lldpad_cfg, p); /* if setting does not exist, then need to create it */ if (setting == NULL) { for (i = strlen(p); i > 0; i--) if (*(p+i) == '.') break; if (i) { *(p+i) = '\0'; setting = find_or_create_setting(p, CONFIG_TYPE_GROUP); *(p+i) = '.'; } else { setting = config_root_setting(&lldpad_cfg); } if (setting) { for (i = strlen(p); i > 0; i--) if (*(p+i) == '.') break; if (i) s = p+i+1; else s = p; return config_setting_add(setting, s, type); } } return setting; } /* * set_config_setting - set the setting to the given config file path by type * @ifname: interface name * @path: relative to LLDP_COMMON or ifname section of LLDP configuration. * @value: pointer to the value to be retrieved * @type: libconfig value types * * Returns cmd_success(0) for success, otherwise for failure. * * This function assumes init_cfg() has been called. */ int set_config_setting(const char *ifname, int agenttype, char *path, union cfg_set v, int type) { config_setting_t *setting = NULL; char p[1024]; int rval = cmd_success; const char *section = agent_type2section(agenttype); LLDPAD_DBG("%s(%i): \n", __func__, __LINE__); if (strlen(ifname)) snprintf(p, sizeof(p), "%s.%s.%s", section, ifname, path); else snprintf(p, sizeof(p), "%s.%s.%s", section, LLDP_COMMON, path); setting = find_or_create_setting(p, type); if (setting) { if (!set_config_value(setting, v, type)) { rval = cmd_failed; } else if (!config_write_file(&lldpad_cfg, cfg_file_name)) { LLDPAD_DBG("config write failed\n"); rval = cmd_failed; } } return rval; } int set_cfg(const char *ifname, int agenttype, char *path, union cfg_set v, int type) { int rval = cmd_failed; rval = set_config_setting(ifname, agenttype, path, v, type); return rval; } /* get_config_tlvfield - read one field from the tlvid * @ifname: the port name * @tlvid: oui + subtype * @field: name of the field to query * @value: output buffer * @size: size in bytes in the output buffer * @type: value type * * Returns 0 on success and -1 on failure * * Note: must have called init_cfg() before calling this. */ int get_config_tlvfield(const char *ifname, int agenttype, u32 tlvid, const char *field, union cfg_get v, int type) { char path[256]; memset(path, 0, sizeof(path)); snprintf(path, sizeof(path), "tlvid%08x.%s", tlvid, field); if (get_config_setting(ifname, agenttype, path, v, type)) return EIO; return 0; } int get_config_tlvfield_int(const char *ifname, int agenttype, u32 tlvid, const char *field, int *val) { return get_config_tlvfield(ifname, agenttype, tlvid, field, val, CONFIG_TYPE_INT); } int get_config_tlvfield_bool(const char *ifname, int agenttype, u32 tlvid, const char *field, int *val) { return get_config_tlvfield(ifname, agenttype, tlvid, field, val, CONFIG_TYPE_BOOL); } int get_config_tlvfield_bin(const char *ifname, int agenttype, u32 tlvid, const char *field, void *value, size_t size) { int rc; const char *str = NULL; rc = get_config_tlvfield(ifname, agenttype, tlvid, field, &str, CONFIG_TYPE_STRING); if (rc == 0 && str) rc = hexstr2bin(str, value, size); return rc; } int get_config_tlvfield_str(const char *ifname, int agenttype, u32 tlvid, const char *field, char *value, size_t size) { int rc; const char *str = NULL; rc = get_config_tlvfield(ifname, agenttype, tlvid, field, &str, CONFIG_TYPE_STRING); if (rc == 0 && str) strncpy(value, str, size); return rc; } int get_config_tlvinfo_bin(const char *ifname, int agenttype, u32 tlvid, void *value, size_t size) { return get_config_tlvfield_bin(ifname, agenttype, tlvid, ARG_TLVINFO, value, size); } int get_config_tlvinfo_str(const char *ifname, int agenttype, u32 tlvid, char *value, size_t size) { return get_config_tlvfield_str(ifname, agenttype, tlvid, ARG_TLVINFO, value, size); } int set_config_tlvfield(const char *ifname, int agenttype, u32 tlvid, const char *field, union cfg_set v, int type) { char path[256]; memset(path, 0, sizeof(path)); snprintf(path, sizeof(path), "tlvid%08x.%s", tlvid, field); return set_config_setting(ifname, agenttype, path, v, type); } int set_config_tlvfield_str(const char *ifname, int agenttype, u32 tlvid, const char *field, const char *str) { if (!str) return EINVAL; return set_config_tlvfield(ifname, agenttype, tlvid, field, &str, CONFIG_TYPE_STRING); } int set_config_tlvfield_bin(const char *ifname, int agenttype, u32 tlvid, const char *field, void *value, size_t size) { int rc; const size_t bsize = size * 2 + 1; /* 2 char per byte + '\0' */ char str[bsize]; const char *p; if (!value) return EINVAL; memset(str, 0, bsize); rc = bin2hexstr(value, size, str, bsize); if (rc) goto out; str[bsize - 1] = '\0'; p = str; rc = set_config_tlvfield(ifname, agenttype, tlvid, field, &p, CONFIG_TYPE_STRING); out: return rc; } int set_config_tlvinfo_bin(const char *ifname, int agenttype, u32 tlvid, void *value, size_t size) { return set_config_tlvfield_bin(ifname, agenttype, tlvid, "info", value, size); } int set_config_tlvinfo_str(const char *ifname, int agenttype, u32 tlvid, char *value) { return set_config_tlvfield_str(ifname, agenttype, tlvid, "info", value); } int set_config_tlvfield_int(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value) { return set_config_tlvfield(ifname, agenttype, tlvid, field, value, CONFIG_TYPE_INT); } int set_config_tlvfield_bool(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value) { return set_config_tlvfield(ifname, agenttype, tlvid, field, value, CONFIG_TYPE_BOOL); } int is_tlv_txdisabled(const char *ifname, int agenttype, u32 tlvid) { char arg[64]; int enabletx = true; snprintf(arg, sizeof(arg), "%s%08x.%s", TLVID_PREFIX, tlvid, ARG_TLVTXENABLE); get_config_setting(ifname, agenttype, arg, &enabletx, CONFIG_TYPE_BOOL); return !enabletx; } int is_tlv_txenabled(const char *ifname, int agenttype, u32 tlvid) { char arg[64]; int enabletx = false; snprintf(arg, sizeof(arg), "%s%08x.%s", TLVID_PREFIX, tlvid, ARG_TLVTXENABLE); get_config_setting(ifname, agenttype, arg, &enabletx, CONFIG_TYPE_BOOL); return enabletx; } int tlv_enabletx(const char *ifname, int agenttype, u32 tlvid) { int enabletx = true; return set_config_tlvfield_bool(ifname, agenttype, tlvid, ARG_TLVTXENABLE, &enabletx); } int tlv_disabletx(const char *ifname, int agenttype, u32 tlvid) { int enabletx = false; return set_config_tlvfield_bool(ifname, agenttype, tlvid, ARG_TLVTXENABLE, &enabletx); } void set_med_devtype(const char *ifname, int agenttype, int devtype) { if (LLDP_MED_DEVTYPE_INVALID(devtype)) return; set_config_tlvfield_int(ifname, agenttype, TLVID_MED(LLDP_MED_RESERVED), "devtype", &devtype); } int get_med_devtype(const char *ifname, int agenttype) { int devtype, err; err = get_config_tlvfield_int(ifname, agenttype, TLVID_MED(LLDP_MED_RESERVED), "devtype", &devtype); if (err) devtype = 0; return devtype; } lldpad-0.9.46/configure.ac000066400000000000000000000045571215142747300153610ustar00rootroot00000000000000AC_INIT([lldpad], [0.9.46], [lldp-devel@open-lldp.org]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) m4_pattern_allow([AM_PROG_AR]) AM_PROG_AR AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[compile debug programs]), [enable_debug=yes], [enable_debug=no]) if test "$enable_debug" = "yes" ; then AC_DEFINE_UNQUOTED([BUILD_DEBUG], 1, [whether debug code is enabled]) fi AM_CONDITIONAL([BUILD_DEBUG], [test "$enable_debug" = "yes"]) AC_PROG_CC AC_PROG_CXX AC_PROG_LEX AC_PROG_LIBTOOL LT_INIT([dlopen]) if test "x$LEX" = x: then AC_MSG_ERROR([no suitable flex found. Please install the 'flex' package.]) fi PKG_CHECK_MODULES([LIBCONFIG], [libconfig >= 1.3.2]) PKG_CHECK_MODULES([LIBNL], [libnl-1 >= 1.1]) AC_SUBST(LIBNL_CFLAGS) AC_SUBST(LIBNL_LIBS) AC_CHECK_LIB(nl, rtnl_link_get_by_name) AC_CHECK_FUNCS([alarm]) AC_CHECK_FUNCS([gettimeofday]) AC_CHECK_FUNCS([inet_ntoa]) AC_CHECK_FUNCS([localtime_r]) AC_CHECK_FUNCS([memmove]) AC_CHECK_FUNCS([memset]) AC_CHECK_FUNCS([mkdir]) AC_CHECK_FUNCS([rmdir]) AC_CHECK_FUNCS([select]) AC_CHECK_FUNCS([socket]) AC_CHECK_FUNCS([strcasecmp]) AC_CHECK_FUNCS([strchr]) AC_CHECK_FUNCS([strdup]) AC_CHECK_FUNCS([strerror]) AC_CHECK_FUNCS([strncasecmp]) AC_CHECK_FUNCS([strpbrk]) AC_CHECK_FUNCS([strrchr]) AC_CHECK_FUNCS([strstr]) AC_CHECK_FUNCS([strtol]) AC_CHECK_FUNCS([strtoul]) AC_CHECK_FUNCS([uname]) AC_CHECK_HEADER([libconfig.h], ,[AC_MSG_ERROR( [Could not find libconfig.h. Try installing libconfig-devel])] ) AC_CHECK_HEADER([readline/readline.h], , [AC_MSG_ERROR( [Could not find readline.h. Try installing readline-devel])] ) AC_CHECK_HEADER([readline/history.h], , [AC_MSG_ERROR( [Could not find history.h. Try installing readline-devel])] ) AC_CHECK_HEADERS([arpa/inet.h]) AC_CHECK_HEADERS([dlfcn.h]) AC_CHECK_HEADERS([fcntl.h]) AC_CHECK_HEADERS([limits.h]) AC_CHECK_HEADERS([netdb.h]) AC_CHECK_HEADERS([netinet/in.h]) AC_CHECK_HEADERS([sys/file.h]) AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADERS([sys/socket.h]) AC_CHECK_HEADERS([sys/time.h]) AC_CHECK_HEADERS([syslog.h]) AC_C_INLINE AC_FUNC_CHOWN AC_FUNC_MALLOC AC_FUNC_REALLOC AC_HEADER_STDBOOL AC_PREREQ AC_PROG_INSTALL AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UID_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T AC_CONFIG_FILES([Makefile include/version.h lldpad.spec lldpad.pc liblldp_clif.pc]) AC_CONFIG_MACRO_DIR([m4]) AC_OUTPUT lldpad-0.9.46/contrib/000077500000000000000000000000001215142747300145205ustar00rootroot00000000000000lldpad-0.9.46/contrib/bash_completion/000077500000000000000000000000001215142747300176665ustar00rootroot00000000000000lldpad-0.9.46/contrib/bash_completion/lldpad000066400000000000000000000021121215142747300210450ustar00rootroot00000000000000# bash completion for lldpad # ## lldpad --help # usage: lldpad [-hdksv] [-f configfile] # options: # -h show this usage # -f use configfile instead of default # -d run daemon in the background # -k terminate current running lldpad # -s remove lldpad state records # -v show version # -V set syslog level # This file must be updated with any changes to, or additions to the options. # Could not get numeric value to work for the --stats interval # Considered parsing output of --help to complete options have lldpad && _lldpad_options() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="-h -f -d -k -s -v -V" case "${cur}" in *) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) ;; esac case "${prev}" in -f) _filedir return 0 ;; esac return 0 } complete -F _lldpad_options lldpad # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=shlldpad-0.9.46/contrib/bash_completion/lldptool000066400000000000000000000050351215142747300214450ustar00rootroot00000000000000# bash completion for lldptool # ## lldptool --help # lldptool v0.9.41 # Copyright (c) 2007-2010, Intel Corporation # # Substantially modified from: hostapd_cli v 0.5.7 # Copyright (c) 2004-2007, Jouni Malinen and contributors # # Usage: # lldptool [options] [arg] general command line usage format # lldptool go into interactive mode # [options] [arg] general interactive command format # # Options: # -i [ifname] network interface # -V [tlvid] TLV identifier # may be numeric or keyword (see below) # -n "neighbor" option for command # -a "add" option for command # -d "remove" option for command # -r show raw message # -R show only raw messages # # Commands: # license show license information # -h|help show command usage information # -v|version show version # -q|quit exit lldptool (interactive mode) # -S|stats get LLDP statistics for ifname # -t|get-tlv get TLVs from ifname # -T|set-tlv set arg for tlvid to value # -l|get-lldp get the LLDP parameters for ifname # -L|set-lldp set the LLDP parameter for ifname # This file must be updated with any changes to, or additions to the options. # Could not get numeric value to work for the --stats interval # Considered parsing output of --help to complete options have lldptool && _lldptool_options() { local cur prev opts cmds opts_and_cmds COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" cmds="license -h help -v version -q quit -s stats -t get-tlv -T set-tlv -l get-lldp -L set-lldp" opts="-i -V -n -a -d -r -R" opts_and_cmds="$opts $cmds" case "${cur}" in *) COMPREPLY=( $(compgen -W "${opts_and_cmds}" -- ${cur}) ) ;; esac case "${prev}" in -i) _available_interfaces return 0 ;; esac return 0 } complete -F _lldptool_options lldptool # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh lldpad-0.9.46/ctrl_iface.c000066400000000000000000000322331215142747300153220ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "lldpad.h" #include "eloop.h" #include "ctrl_iface.h" #include "dcb_protocol.h" #include "list.h" #include "lldp_mod.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/ports.h" #include "lldp_dcbx.h" #include "lldp_util.h" #include "messages.h" extern struct lldp_head lldp_head; struct ctrl_dst { struct ctrl_dst *next; struct sockaddr_un addr; socklen_t addrlen; int debug_level; int errors; u32 *tlv_types; /*tlv event types to recv */ }; static char *hexlist = "0123456789abcdef"; struct clif_cmds { int cmd; int (*cmd_handler)(struct clif_data *cd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); }; static const struct clif_cmds cmd_tbl[] = { { DCB_CMD, clif_iface_module }, { MOD_CMD, clif_iface_module }, { ATTACH_CMD, clif_iface_attach }, { DETACH_CMD, clif_iface_detach }, { LEVEL_CMD, clif_iface_level }, { PING_CMD, clif_iface_ping }, { UNKNOWN_CMD, clif_iface_cmd_unknown } }; int clif_iface_module(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen) { u32 module_id; char *cmd_start; int cmd_len; struct lldp_module *mod = NULL; /* identify the module and start of command */ switch (*ibuf) { case DCB_CMD: /* Message does not contain a module id, therefore this * message is a DCBX message. Set module_id to correct value. */ module_id = 0x001b2101; cmd_start = ibuf; cmd_len = ilen; break; case MOD_CMD: hexstr2bin(ibuf+MOD_ID, (u8 *)&module_id, sizeof(module_id)); module_id = ntohl(module_id); cmd_start = ibuf + MOD_ID + 2*sizeof(module_id); cmd_len = ilen - MOD_ID - 2*sizeof(module_id); break; default: return cmd_invalid; } mod = find_module_by_id(&lldp_head, module_id); if (mod && mod->ops && mod->ops->client_cmd) return (mod->ops->client_cmd)(clifd, from, fromlen, cmd_start, cmd_len, rbuf+strlen(rbuf), rlen); else return cmd_device_not_found; } int clif_iface_cmd_unknown(UNUSED struct clif_data *clifd, UNUSED struct sockaddr_un *from, UNUSED socklen_t fromlen, UNUSED char *ibuf, UNUSED int ilen, UNUSED char *rbuf, UNUSED int rlen) { return cmd_invalid; } int clif_iface_ping(UNUSED struct clif_data *clifd, UNUSED struct sockaddr_un *from, UNUSED socklen_t fromlen, UNUSED char *ibuf, UNUSED int ilen, char *rbuf, int rlen) { snprintf(rbuf, rlen, "%cPONG%d", PING_CMD, getpid()); return 0; } int clif_iface_attach(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, UNUSED int ilen, char *rbuf, int rlen) { struct ctrl_dst *dst; char *tlv, *str, *tokenize; const char *delim = ","; int i, tlv_count = 0; u8 *ptr; dst = malloc(sizeof(*dst)); if (dst == NULL) return 1; memset(dst, 0, sizeof(*dst)); memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; dst->next = clifd->ctrl_dst; clifd->ctrl_dst = dst; /* * There are two cases here one, the user provided * no string in which case we must send DCBX events * to be compatible with legacy clients. Two the * user sent a comma seperated string of tlv module * ids it expects events from */ /* set default string to DCBX Events */ if (ibuf[1] == '\0') { u32 hex = LLDP_MOD_DCBX; tlv = malloc(sizeof(char) * (8 + 2)); if (!tlv) goto err_tlv; tlv[0] = 'A'; tlv[9] = 0; bin2hexstr((u8*)&hex, 4, &tlv[1], 8); } else tlv = strdup(ibuf); str = tlv; str++; /* Count number of TLV Modules */ tokenize = strtok(str, delim); tlv_count++; do { tokenize = strtok(NULL, delim); tlv_count++; } while (tokenize); dst->tlv_types = malloc(sizeof(u32) * tlv_count); if (!dst->tlv_types) goto err_types; memset(dst->tlv_types, 0, sizeof(u32) * tlv_count); /* Populate tlv_types from comma separated string */ tokenize = strtok(str, delim); for (i=0; tokenize; i++) { ptr = (u8*)&dst->tlv_types[i]; hexstr2bin(tokenize, ptr, 4); tokenize = strtok(NULL, delim); } /* Insert Termination Pattern */ dst->tlv_types[i] = ~0; free(tlv); LLDPAD_DBG("CTRL_IFACE monitor attached\n"); snprintf(rbuf, rlen, "%c", ATTACH_CMD); return 0; err_types: free(tlv); err_tlv: LLDPAD_DBG("CTRL_IFACE monitor attach error\n"); snprintf(rbuf, rlen, "%c", ATTACH_CMD); return cmd_failed; } static int detach_clif_monitor(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen) { struct ctrl_dst *dst, *prev = NULL; dst = clifd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && memcmp(from->sun_path, dst->addr.sun_path, fromlen-sizeof(from->sun_family)) == 0) { if (prev == NULL) clifd->ctrl_dst = dst->next; else prev->next = dst->next; free(dst->tlv_types); free(dst); dst = NULL; LLDPAD_DBG("CTRL_IFACE monitor detached\n"); return 0; } prev = dst; dst = dst->next; } return cmd_failed; } int clif_iface_detach(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, UNUSED char *ibuf, UNUSED int ilen, char *rbuf, int rlen) { snprintf(rbuf, rlen, "%c", DETACH_CMD); return detach_clif_monitor(clifd, from, fromlen); } int clif_iface_level(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, UNUSED int ilen, char *rbuf, int rlen) { struct ctrl_dst *dst; char *level; level = ibuf+1; snprintf(rbuf, rlen, "%c", LEVEL_CMD); LLDPAD_DBG("CTRL_IFACE LEVEL %s", level); dst = clifd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && memcmp(from->sun_path, dst->addr.sun_path, fromlen-sizeof(from->sun_family)) == 0) { LLDPAD_DBG("CTRL_IFACE changed monitor level\n"); return 0; } dst = dst->next; } return cmd_failed; } static int find_cmd_entry(int cmd) { int i; for (i = 0; cmd_tbl[i].cmd != cmd && cmd_tbl[i].cmd != UNKNOWN_CMD; i++) ; return (i); } /* process_clif_cmd - routine to pass command to correct module routine and * compute length and status. * * Note: ibuf and ilen must be verified by caller. */ static void process_clif_cmd( struct clif_data *cd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rsize, int *rlen) { int status; /* setup minimum command response message * status will be updated at end */ snprintf(rbuf, rsize, "%c%02x", CMD_RESPONSE, cmd_failed); status = cmd_tbl[find_cmd_entry((int)ibuf[0])].cmd_handler( cd, from, fromlen, ibuf, ilen, rbuf + strlen(rbuf), rsize - strlen(rbuf) - 1); /* update status and compute final length */ rbuf[CLIF_STAT_OFF] = hexlist[(status & 0x0f1) >> 4]; rbuf[CLIF_STAT_OFF+1] = hexlist[status & 0x0f]; *rlen = strlen(rbuf); } static void ctrl_iface_receive(int sock, void *eloop_ctx, UNUSED void *sock_ctx) { struct clif_data *clifd = eloop_ctx; char buf[MAX_CLIF_MSGBUF]; struct msghdr smsg; struct cmsghdr *cmsg; struct iovec iov; struct ucred *cred; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); char *reply; const int reply_size = MAX_CLIF_MSGBUF; int reply_len; memset(&buf, 0x00, sizeof(buf)); iov.iov_base = buf; iov.iov_len = sizeof(buf) - 1; memset(&smsg, 0x00, sizeof(struct msghdr)); smsg.msg_name = &from; smsg.msg_namelen = fromlen; smsg.msg_iov = &iov; smsg.msg_iovlen = 1; smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); res = recvmsg(sock, &smsg, 0); if (res < 0) { perror("recvfrom(ctrl_iface)"); return; } cmsg = CMSG_FIRSTHDR(&smsg); fromlen = smsg.msg_namelen; cred = (struct ucred *)CMSG_DATA(cmsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { LLDPAD_INFO("%s: No sender credentials, ignoring", __FUNCTION__); sprintf(buf,"R%02x", cmd_bad_params); sendto(sock, buf, 3, 0, (struct sockaddr *) &from, fromlen); return; } if (cred->uid != 0) { LLDPAD_INFO("%s: sender uid=%i, ignoring", __FUNCTION__, cred->uid); sprintf(buf,"R%02x", cmd_no_access); sendto(sock, buf, 3, 0, (struct sockaddr *) &from, fromlen); return; } buf[res] = '\0'; /* wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); */ reply = malloc(reply_size); if (reply == NULL) { sendto(sock, "R01", 3, 0, (struct sockaddr *) &from, fromlen); return; } memset(reply, 0, reply_size); process_clif_cmd(clifd, &from, fromlen, buf, res, reply, reply_size, &reply_len); /* wpa_hexdump_ascii(MSG_DEBUG, "TX ctrl_iface", (u8 *) reply, reply_len); */ sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); free(reply); } int ctrl_iface_register(struct clif_data *clifd) { return eloop_register_read_sock(clifd->ctrl_sock, ctrl_iface_receive, clifd, NULL); } int ctrl_iface_init(struct clif_data *clifd) { struct sockaddr_un addr; int s = -1; socklen_t addrlen; const int feature_on = 1; clifd->ctrl_sock = -1; clifd->ctrl_dst = NULL; s = socket(AF_LOCAL, SOCK_DGRAM, 0); if (s < 0) { perror("socket(AF_LOCAL)"); goto fail; } /* enable receiving of the sender credentials */ setsockopt(s, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; snprintf(&addr.sun_path[1], sizeof(addr.sun_path) - 1, "%s", LLDP_CLIF_SOCK); addrlen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1; if (bind(s, (struct sockaddr *) &addr, addrlen) < 0) { perror("bind(AF_LOCAL)"); goto fail; } /* enable receiving of the sender credentials */ setsockopt(s, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); LLDPAD_INFO("bound ctrl iface to %s\n", &addr.sun_path[1]); clifd->ctrl_sock = s; return 0; fail: if (s >= 0) close(s); return -1; } void ctrl_iface_deinit(struct clif_data *clifd) { struct ctrl_dst *dst, *prev; if (clifd->ctrl_sock > -1) { eloop_unregister_read_sock(clifd->ctrl_sock); close(clifd->ctrl_sock); clifd->ctrl_sock = -1; } dst = clifd->ctrl_dst; while (dst) { prev = dst; dst = dst->next; free(prev); } free(clifd); } int is_ctrl_listening(struct ctrl_dst *dst, u32 type) { int i; u32 term = ~0; u32 all = 0; u32 dcbx = LLDP_MOD_DCBX; if (!dst) return 0; for (i=0; dst->tlv_types[i] != term; i++) { if ((!type && dst->tlv_types[i] == dcbx) || dst->tlv_types[i] == type || dst->tlv_types[i] == all) return 1; } return 0; } void ctrl_iface_send(struct clif_data *clifd, int level, u32 moduleid, char *buf, size_t len) { struct ctrl_dst *dst, *next; struct msghdr msg; int idx, send; struct iovec io[3]; char levelstr[10] = ""; char modulestr[10] = ""; dst = clifd->ctrl_dst; if (clifd->ctrl_sock < 0 || dst == NULL) return; snprintf(levelstr, sizeof(levelstr), "%c%d", EVENT_MSG, level); if (moduleid) { snprintf(modulestr, sizeof(modulestr), "M%08x",moduleid); io[0].iov_base = modulestr; io[0].iov_len = strlen(modulestr); } else { io[0].iov_base = NULL; io[0].iov_len = 0; } io[1].iov_base = levelstr; io[1].iov_len = strlen(levelstr); io[2].iov_base = buf; io[2].iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 3; idx = 0; while (dst) { next = dst->next; send = 0; /* Does dst receive these event messages? */ send = is_ctrl_listening(dst, moduleid); /* Yes */ if (send && level >= dst->debug_level) { msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(clifd->ctrl_sock, &msg, 0) < 0) { fprintf(stderr, "CTRL_IFACE monitor[%d][%d] %d:%s: ", idx, clifd->ctrl_sock, dst->addrlen, dst->addr.sun_path); perror("sendmsg"); dst->errors++; if (dst->errors > 10) { detach_clif_monitor( clifd, &dst->addr, dst->addrlen); } } else { dst->errors = 0; } } idx++; dst = next; } } lldpad-0.9.46/dcb_protocol.c000066400000000000000000003351571215142747300157130ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldp/ports.h" #include "dcb_types.h" #include "dcb_protocol.h" #include "dcb_driver_interface.h" #include "dcb_persist_store.h" #include "dcb_rule_chk.h" #include "dcb_events.h" #include "messages.h" #include "lldp.h" #include "tlv_dcbx.h" #include "lldp_rtnl.h" #include "lldpad_shm.h" #include "linux/if.h" #include "linux/dcbnl.h" static void handle_opermode_true(char *device_name); u8 gdcbx_subtype = dcbx_subtype2; int set_configuration(char *device_name, u32 EventFlag); int pg_not_initted = true; struct pg_store1 { char ifname[MAX_DESCRIPTION_LEN]; pg_attribs *second; LIST_ENTRY(pg_store1) entries; }; typedef struct pg_store1 * pg_it; LIST_HEAD(pghead, pg_store1) pg, peer_pg, oper_pg; /*todo: check MAX_DESCRIPTION_LEN?*/ struct pg_store1 *pg_find(struct pghead *head, char *ifname) { struct pg_store1 *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname)) return p; return p; } void pg_insert(struct pghead *head, char *ifname, pg_attribs *store) { struct pg_store1 *entry = NULL; entry = (struct pg_store1 *)malloc(sizeof(struct pg_store1)); if (!entry) return; strncpy(entry->ifname, ifname, sizeof(entry->ifname)); entry->second = store; LIST_INSERT_HEAD(head, entry, entries); } void pg_erase(pg_it *p) { /* save the reserved memory here so we can free * it since erase wipes out the it structure * (but does not free the memory) */ void *itp = (void *)(*p)->second; /* this line frees the param of this function! */ LIST_REMOVE(*p, entries); if (itp) { free (itp); itp = NULL; } free(*p); *p = NULL; } int pfc_not_initted = true; struct pfc_store { char ifname[MAX_DESCRIPTION_LEN]; pfc_attribs *second; LIST_ENTRY(pfc_store) entries; }; typedef struct pfc_store * pfc_it; LIST_HEAD(pfchead, pfc_store) pfc, peer_pfc, oper_pfc; struct pfc_store *pfc_find(struct pfchead *head, char *ifname) { struct pfc_store *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname)) return p; return p; } void pfc_insert(struct pfchead *head, char *ifname, pfc_attribs *store) { struct pfc_store *entry; entry = (struct pfc_store *) malloc(sizeof(struct pfc_store)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; LIST_INSERT_HEAD(head, entry, entries); } void pfc_erase(pfc_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free(itp); itp = NULL; } free(*p); *p = NULL; } int pgdesc_not_initted = true; struct pg_desc_store { char ifname[MAX_DESCRIPTION_LEN]; pg_info *second; LIST_ENTRY(pg_desc_store) entries; }; typedef struct pg_desc_store * pg_desc_it; LIST_HEAD(pgdesc_head, pg_desc_store) pg_desc; struct pg_desc_store *pgdesc_find(struct pgdesc_head *head, char *ifname) { struct pg_desc_store *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname)) return p; return p; } void pgdesc_insert(struct pgdesc_head *head, char *ifname, pg_info *store) { struct pg_desc_store *entry; entry = (struct pg_desc_store *) malloc(sizeof(struct pg_desc_store)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; LIST_INSERT_HEAD(head, entry, entries); } void pgdesc_erase(pg_desc_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free(itp); itp = NULL; } free(*p); *p = NULL; } int app_not_initted = true; struct app_store { char ifname[MAX_DESCRIPTION_LEN]; u32 app_subtype; app_attribs *second; LIST_ENTRY(app_store) entries; }; typedef struct app_store * app_it; LIST_HEAD(apphead, app_store) apptlv, peer_apptlv, oper_apptlv; struct app_store *apptlv_find(struct apphead *head, char *ifname, u32 subtype) { struct app_store *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname) && (p->app_subtype == subtype)) return p; return p; } void apptlv_insert(struct apphead *head, char *ifname, u32 subtype, app_attribs *store) { struct app_store *entry; entry = (struct app_store *) malloc(sizeof(struct app_store)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; entry->app_subtype = subtype; LIST_INSERT_HEAD(head, entry, entries); } void apptlv_erase(app_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free (itp); itp = NULL; } free(*p); *p = NULL; } int llink_not_initted = true; struct llink_store { char ifname[MAX_DESCRIPTION_LEN]; u32 llink_subtype; llink_attribs *second; LIST_ENTRY(llink_store) entries; }; typedef struct llink_store * llink_it; LIST_HEAD(llinkhead, llink_store) llink, peer_llink, oper_llink; struct llink_store *llink_find(struct llinkhead *head, char *ifname, u32 subtype) { struct llink_store *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname) && p->llink_subtype == subtype) return p; return p; } void llink_insert(struct llinkhead *head, char *ifname, llink_attribs *store, u32 subtype) { struct llink_store *entry; entry = (struct llink_store *) malloc(sizeof(struct llink_store)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; entry->llink_subtype = subtype; LIST_INSERT_HEAD(head, entry, entries); } void llink_erase(llink_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free(itp); itp = NULL; } free(*p); *p = NULL; } int ctrl_not_initted = true; struct dcb_control_protocol { char ifname[MAX_DESCRIPTION_LEN]; control_protocol_attribs *second; LIST_ENTRY(dcb_control_protocol) entries; }; typedef struct dcb_control_protocol * control_prot_it; LIST_HEAD(control_prot_head, dcb_control_protocol)\ dcb_control_prot, dcb_peer_control_prot; struct dcb_control_protocol *ctrl_prot_find(struct control_prot_head *head, const char *ifname) { struct dcb_control_protocol *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname)) return p; return p; } void ctrl_prot_insert(struct control_prot_head *head, char *ifname, control_protocol_attribs *store) { struct dcb_control_protocol *entry; entry = (struct dcb_control_protocol *) malloc(sizeof(struct dcb_control_protocol)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; LIST_INSERT_HEAD(head, entry, entries); } void ctrl_prot_erase(control_prot_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free(itp); itp = NULL; } free(*p); *p = NULL; } int feature_not_initted = true; struct features_store { char ifname[MAX_DESCRIPTION_LEN]; feature_support *second; LIST_ENTRY(features_store) entries; }; typedef struct features_store * features_it; LIST_HEAD(featurehead, features_store) feature_struct; struct features_store *features_find(struct featurehead *head, char *ifname) { struct features_store *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->ifname, ifname)) return p; return p; } void features_insert(struct featurehead *head, char *ifname, feature_support *store) { struct features_store *entry; entry = (struct features_store *)malloc(sizeof(struct features_store)); if (!entry) return; strcpy(entry->ifname, ifname); entry->second = store; LIST_INSERT_HEAD(head, entry, entries); } void features_erase(features_it *p) { void *itp = (void *)(*p)->second; LIST_REMOVE(*p, entries); if (itp) { free(itp); itp = NULL; } free(*p); *p = NULL; } /* Add the store pointer to init_pg, i.e. memset store to 0, * then copy attribs to store */ void init_pg(pg_attribs *Attrib, pg_attribs *Store) { memset(Store,0,sizeof(*Store)); Attrib->protocol.Max_version = DCB_PG_MAX_VERSION; Attrib->protocol.State = DCB_INIT; Attrib->protocol.Advertise_prev = false; Attrib->protocol.tlv_sent = false; Attrib->protocol.dcbx_st = gdcbx_subtype & MASK_DCBX_FORCE; memcpy(Store, Attrib, sizeof (*Attrib)); } /* pass in the pointer to attrib */ bool add_pg(char *device_name, pg_attribs *Attrib) { pg_it it = pg_find(&pg, device_name); if (it == NULL) { /* Device not present: add */ pg_attribs *store = (pg_attribs*) malloc(sizeof(*store)); if (!store) return false; init_pg(Attrib, store); pg_insert(&pg, device_name, store); } else { /* already in data store, just update it */ it->second->protocol.Advertise_prev = (*it).second->protocol.Advertise; it->second->protocol.Advertise = Attrib->protocol.Advertise; it->second->protocol.Enable = Attrib->protocol.Enable; it->second->protocol.Willing = Attrib->protocol.Willing; memcpy(&(it->second->rx), &(Attrib->rx), sizeof(Attrib->rx)); memcpy(&(it->second->tx), &(Attrib->tx), sizeof(Attrib->tx)); } return true; } void init_oper_pg(pg_attribs *Attrib) { char sTmp[MAX_DESCRIPTION_LEN]; memset(Attrib,0,sizeof(*Attrib)); snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); pg_it itpg = pg_find(&pg, sTmp); if (itpg == NULL) return; memcpy(&(Attrib->rx), &(itpg->second->rx), sizeof(Attrib->rx)); memcpy(&(Attrib->tx), &(itpg->second->tx), sizeof(Attrib->tx)); } bool add_oper_pg(char *device_name) { pg_it it = pg_find(&oper_pg, device_name); if (it == NULL) { /* Device not present: add */ pg_attribs *store = (pg_attribs*) malloc(sizeof(*store)); if (!store) return false; init_oper_pg( store); pg_insert(&oper_pg, device_name, store); } return true; } void init_peer_pg(pg_attribs *Attrib) { memset(Attrib,0,sizeof(*Attrib)); Attrib->protocol.TLVPresent = false; } bool add_peer_pg(char *device_name) { pg_it it = pg_find(&peer_pg, device_name); if (it == NULL) { /* Device not present: add */ pg_attribs *store = (pg_attribs*) malloc(sizeof(*store)); if (!store) return false; init_peer_pg( store); pg_insert(&peer_pg, device_name, store); } return true; } void init_apptlv(app_attribs *Attrib, app_attribs *Store) { memset(Store,0,sizeof(*Store)); Attrib->protocol.Max_version = DCB_APPTLV_MAX_VERSION; Attrib->protocol.State = DCB_INIT; Attrib->protocol.Advertise_prev = false; Attrib->protocol.tlv_sent = false; Attrib->protocol.dcbx_st = gdcbx_subtype & MASK_DCBX_FORCE; memcpy(Store, Attrib, sizeof (*Attrib)); } bool valid_subtype(dcbx_state *state, u32 Subtype) { if (state == NULL) return 0; switch (Subtype) { case APP_FCOE_STYPE: return state->FCoEenable; case APP_ISCSI_STYPE: return state->iSCSIenable; case APP_FIP_STYPE: return state->FIPenable; default: return 0; } } bool add_apptlv(char *device_name, app_attribs *Attrib, u32 Subtype, dcbx_state *state) { full_dcb_attrib_ptrs attr_ptr; /* If FCoE is enabled in the DCBX state record, * then enable the FCoE App object and persist the change. */ if (valid_subtype(state, Subtype) && !Attrib->protocol.Enable) { Attrib->protocol.Enable = true; memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.app = Attrib; attr_ptr.app_subtype = (u8)Subtype; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in add_apptlv, " " subtype: %d\n", Subtype); return false; } } app_it it = apptlv_find(&apptlv, device_name, Subtype); if (it == NULL) { /* Device not present: add */ app_attribs *store = (app_attribs*)malloc(sizeof(*store)); if (!store) return false; init_apptlv(Attrib, store); apptlv_insert(&apptlv, device_name, Subtype, store); } else { /* already in data store, just update it */ it->second->protocol.Advertise_prev = it->second->protocol.Advertise; it->second->protocol.Advertise = Attrib->protocol.Advertise; it->second->protocol.Enable = Attrib->protocol.Enable; it->second->protocol.Willing = Attrib->protocol.Willing; it->second->Length = Attrib->Length; memcpy(it->second->AppData, Attrib->AppData, Attrib->Length); } return true; } void init_oper_apptlv(app_attribs *Store, u32 Subtype) { memset(Store,0,sizeof(*Store)); app_it itapp = apptlv_find(&oper_apptlv, DEF_CFG_STORE, Subtype); if (!itapp) return; Store->Length = itapp->second->Length; memcpy(&Store->AppData, &itapp->second->AppData, Store->Length); } bool add_oper_apptlv(char *device_name, u32 Subtype) { app_it it = apptlv_find(&oper_apptlv, device_name, Subtype); if (it == NULL) { /* Device not present: add */ app_attribs *store = (app_attribs*)malloc(sizeof(*store)); if (!store) return false; init_oper_apptlv(store, Subtype); apptlv_insert(&oper_apptlv, device_name, Subtype, store); } return true; } void init_peer_apptlv(app_attribs *Store) { memset(Store,0,sizeof(*Store)); } bool add_peer_apptlv(char *device_name, u32 Subtype) { app_it it = apptlv_find(&peer_apptlv, device_name, Subtype); if (it == NULL) { /* Device not present: add */ app_attribs *store = (app_attribs*)malloc(sizeof(*store)); if (!store) return false; init_peer_apptlv(store); apptlv_insert(&peer_apptlv, device_name, Subtype, store); } return true; } void init_control_prot(control_protocol_attribs *Attrib, dcbx_state *state) { memset(Attrib,0,sizeof(*Attrib)); Attrib->State = DCB_INIT; Attrib->Max_version = DCB_MAX_VERSION; Attrib->SeqNo = state->SeqNo; Attrib->AckNo = state->AckNo; Attrib->MyAckNo = state->SeqNo; } bool add_control_protocol(char *device_name, dcbx_state *state) { control_prot_it it = ctrl_prot_find(&dcb_control_prot, device_name); if (it == NULL) { /* Device not present: add */ control_protocol_attribs *store = (control_protocol_attribs*)malloc(sizeof(*store)); if (!store) return false; init_control_prot(store, state); ctrl_prot_insert(&dcb_control_prot, device_name, store); } else if (get_operstate(device_name) == IF_OPER_DORMANT) { init_control_prot(it->second, state); } return true; } void init_peer_control_prot(control_protocol_attribs *Attrib) { memset(Attrib,0,sizeof(*Attrib)); Attrib->Oper_version = DCB_MAX_VERSION; Attrib->Max_version = DCB_MAX_VERSION; Attrib->RxDCBTLVState = DCB_PEER_NONE; } bool add_peer_control_protocol(char *device_name) { control_prot_it it = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (it == NULL) { /* Device not present: add */ control_protocol_attribs *store = (control_protocol_attribs*)malloc(sizeof(*store)); if (!store) return false; init_peer_control_prot( store); ctrl_prot_insert(&dcb_peer_control_prot, device_name, store); } return true; } void init_pfc(pfc_attribs *Attrib, pfc_attribs *Store) { memset(Store,0,sizeof(*Store)); Attrib->protocol.Max_version = DCB_PFC_MAX_VERSION; Attrib->protocol.State = DCB_INIT; Attrib->protocol.Advertise_prev = false; Attrib->protocol.tlv_sent = false; Attrib->protocol.dcbx_st = gdcbx_subtype & MASK_DCBX_FORCE; memcpy(Store, Attrib, sizeof(*Attrib)); } bool add_pfc(char *device_name, pfc_attribs *Attrib) { pfc_it it = pfc_find(&pfc, device_name); if (it == NULL) { /* Device not present: add */ pfc_attribs *store = (pfc_attribs*)malloc(sizeof(*store)); if (!store) return false; init_pfc(Attrib, store); pfc_insert(&pfc, device_name, store); } else { /* already present in store, just update */ it->second->protocol.Advertise_prev = it->second->protocol.Advertise; it->second->protocol.Advertise = Attrib->protocol.Advertise; it->second->protocol.Enable = Attrib->protocol.Enable; it->second->protocol.Willing = Attrib->protocol.Willing; memcpy(it->second->admin, Attrib->admin, sizeof(Attrib->admin)); } return true; } void init_oper_pfc(pfc_attribs *Attrib) { char sTmp[MAX_DESCRIPTION_LEN]; memset(Attrib,0,sizeof(*Attrib)); snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); pfc_it itpfc = pfc_find(&oper_pfc, sTmp); if (itpfc == NULL) return; memcpy(&(Attrib->admin), &(itpfc->second->admin), sizeof(Attrib->admin)); } bool add_oper_pfc(char *device_name) { pfc_it it = pfc_find(&oper_pfc, device_name); if (it == NULL) { /* Device not present: add */ pfc_attribs *store = (pfc_attribs*)malloc(sizeof(*store)); if (!store) return false; init_oper_pfc(store); pfc_insert(&oper_pfc, device_name, store); } return true; } void init_peer_pfc(pfc_attribs *Attrib) { memset(Attrib,0,sizeof(*Attrib)); Attrib->protocol.TLVPresent = false; } bool add_peer_pfc(char *device_name) { pfc_it it = pfc_find(&peer_pfc, device_name); if (it == NULL) { /* Device not present: add */ pfc_attribs *store = (pfc_attribs*)malloc(sizeof(*store)); if (!store) return false; init_peer_pfc(store); pfc_insert(&peer_pfc, device_name, store); } return true; } void init_bwg_desc(pg_info *Attrib, pg_info *Store) { memset(Store,0,sizeof(*Store)); memcpy(Store, Attrib, sizeof(*Attrib)); } bool add_bwg_desc(char *device_name, pg_info *Attrib) { pg_desc_it it = pgdesc_find(&pg_desc, device_name); if (it == NULL) { /* Device not present: add */ pg_info *store = (pg_info*)malloc(sizeof(*store)); if (!store) return false; init_bwg_desc(Attrib, store); store->max_pgid_desc = 8; pgdesc_insert(&pg_desc, device_name, store); } return true; } /* Logical Link */ void init_llink(llink_attribs *Attrib, llink_attribs *Store) { memset(Store,0,sizeof(*Store)); Attrib->protocol.Max_version = DCB_LLINK_MAX_VERSION; Attrib->protocol.State = DCB_INIT; Attrib->protocol.Advertise_prev = false; Attrib->protocol.tlv_sent = false; Attrib->protocol.dcbx_st = gdcbx_subtype & MASK_DCBX_FORCE; memcpy(Store, Attrib, sizeof(*Attrib)); } bool add_llink(char *device_name, llink_attribs *Attrib, u32 subtype) { llink_it it = llink_find(&llink, device_name, subtype); if (it == NULL) { /* Device not present: add */ llink_attribs *store = (llink_attribs*)malloc(sizeof(*store)); if (!store) return false; init_llink(Attrib, store); llink_insert(&llink, device_name, store, subtype); } else { /* already present in store, just update */ it->second->protocol.Advertise_prev = it->second->protocol.Advertise; it->second->protocol.Advertise = Attrib->protocol.Advertise; it->second->protocol.Enable = Attrib->protocol.Enable; it->second->protocol.Willing = Attrib->protocol.Willing; memcpy(&(it->second->llink), &(Attrib->llink), sizeof(Attrib->llink)); } return true; } void init_oper_llink(llink_attribs *Attrib, u32 subtype) { memset(Attrib,0,sizeof(*Attrib)); llink_it itllink = llink_find(&oper_llink, DEF_CFG_STORE, subtype); if (itllink == NULL) return; memcpy(&(Attrib->llink), &(itllink->second->llink), sizeof(Attrib->llink)); } bool add_oper_llink(char *device_name, u32 subtype) { llink_it it = llink_find(&oper_llink, device_name, subtype); if (it == NULL) { /* Device not present: add */ llink_attribs *store = (llink_attribs*)malloc(sizeof(*store)); if (!store) return false; init_oper_llink(store, subtype); llink_insert(&oper_llink, device_name, store, subtype); } return true; } void init_peer_llink(llink_attribs *Attrib) { memset(Attrib,0,sizeof(*Attrib)); Attrib->protocol.TLVPresent = false; } bool add_peer_llink(char *device_name, u32 subtype) { llink_it it = llink_find(&peer_llink, device_name, subtype); if (it == NULL) { /* Device not present: add */ llink_attribs *store = (llink_attribs*)malloc(sizeof(*store)); if (!store) return false; init_peer_llink(store); llink_insert(&peer_llink, device_name, store, subtype); } return true; } bool init_dcb_support(char *device_name, full_dcb_attribs *attrib) { feature_support dcb_support; memset(&dcb_support, 0, sizeof(feature_support)); if (get_dcb_capabilities(device_name, &dcb_support) != 0) return false; /*if (!dcb_support.pg) { * attrib->pg.protocol.Enable = false; * attrib->pg.protocol.Advertise = false; *} *if (!dcb_support.pfc) { * attrib->pfc.protocol.Enable = false; * attrib->pfc.protocol.Advertise = false; *} */ if (get_dcb_numtcs(device_name, &attrib->pg.num_tcs, &attrib->pfc.num_tcs) != 0) { return false; } features_it it = features_find(&feature_struct, device_name); if (it == NULL) { /* Device not present: add */ feature_support *store = (feature_support*) malloc(sizeof(*store)); if (!store) return false; memcpy(store, (void *)&dcb_support, sizeof(*store)); features_insert(&feature_struct, device_name, store); } return true; } cmd_status get_dcb_support(char *device_name, feature_support *dcb_support) { cmd_status result = cmd_success; feature_support features; full_dcb_attribs attribs; memset(&attribs, 0, sizeof(attribs)); memset(&features, 0, sizeof(feature_support)); if (!dcb_support) return cmd_bad_params; features_it it = features_find(&feature_struct, device_name); if (it != NULL) { memcpy(dcb_support, it->second, sizeof(*dcb_support)); } else { if (get_persistent(device_name, &attribs) != cmd_success) { result = cmd_device_not_found; goto Exit; } if (!init_dcb_support(device_name, &attribs)) { result = cmd_device_not_found; goto Exit; } /*todo: this was called twice?*/ memset(&features, 0, sizeof(feature_support)); features_it it = features_find(&feature_struct, device_name); if (it != NULL) { memcpy(dcb_support, it->second, sizeof(*dcb_support)); } else { result = cmd_device_not_found; } } Exit: return result; } void remove_dcb_support(void) { while (feature_struct.lh_first != NULL) /* Delete. */ features_erase(&feature_struct.lh_first); } int dcbx_add_adapter(char *device_name) { u32 EventFlag = 0; full_dcb_attrib_ptrs attr_ptr; full_dcb_attribs attribs; feature_support dcb_support = { .pg = 0 }; cmd_status sResult = cmd_success; dcbx_state state; int i = 0; memset(&attribs, 0, sizeof(attribs)); sResult = get_persistent(device_name, &attribs); if (sResult != cmd_success) { LLDPAD_DBG("get_persistent returned error %d\n", sResult); sResult = cmd_failed; goto add_adapter_error; } LLDPAD_DBG(" dcbx subtype = %d\n", gdcbx_subtype); memset (&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pg = &(attribs.pg); attr_ptr.pfc = &(attribs.pfc); memset(&state, 0, sizeof(state)); get_dcbx_state(device_name, &state); /* Create data stores for the device. */ if (!init_dcb_support(device_name, &attribs)) { sResult = cmd_failed; goto add_adapter_error; } sResult = dcb_check_config(&attr_ptr); if (sResult != cmd_success) LLDPAD_WARN("Rule checker returned error %d\n", sResult); if (!add_pg(device_name, &attribs.pg)) { LLDPAD_DBG("add_pg error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_pfc(device_name, &attribs.pfc)) { LLDPAD_DBG("add_pfc error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_bwg_desc(device_name, &attribs.descript)) { LLDPAD_DBG("add_bwg_desc error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_control_protocol(device_name, &state)) { LLDPAD_DBG("add_control_protocol error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_peer_pg(device_name)) { LLDPAD_DBG("add_peer_pg error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_peer_pfc(device_name)) { LLDPAD_DBG("add_peer_pfc error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_peer_control_protocol(device_name)) { LLDPAD_DBG("add_peer_control_protocol error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_oper_pg(device_name)) { LLDPAD_DBG("add_oper_pg error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_oper_pfc(device_name)) { LLDPAD_DBG("add_oper_pfc error.\n"); sResult = cmd_failed; goto add_adapter_error; } /* Add APPTLV for supported Subtypes. */ for (i = 0; i < DCB_MAX_APPTLV; i++) { if (!add_apptlv(device_name, &attribs.app[i], i, &state)) { LLDPAD_DBG("add_apptlv error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_oper_apptlv(device_name, i)) { LLDPAD_DBG("add_oper_apptlv error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_peer_apptlv(device_name, i)) { LLDPAD_DBG("add_peer_apptlv error.\n"); sResult = cmd_failed; goto add_adapter_error; } } for (i = 0; i < DCB_MAX_LLKTLV; i++) { if (!add_llink(device_name, &attribs.llink[i], i)) { LLDPAD_DBG("%s add_llink error.\n", device_name); sResult = cmd_failed; goto add_adapter_error; } if (!add_oper_llink(device_name, i)) { LLDPAD_DBG("add_oper_llink error.\n"); sResult = cmd_failed; goto add_adapter_error; } if (!add_peer_llink(device_name, i)) { LLDPAD_DBG("add_peer_llink error.\n"); sResult = cmd_failed; goto add_adapter_error; } } if (get_dcb_support(device_name, &dcb_support) != cmd_success) { sResult = cmd_failed; goto add_adapter_error; } /* Initialize features state machines for PG and PFC and * APPTLVs. */ if (run_feature_protocol(device_name, DCB_LOCAL_CHANGE_PG, SUBTYPE_DEFAULT) != cmd_success) { LLDPAD_DBG("run_feature_protocol error (PG)\n"); sResult = cmd_failed; goto add_adapter_error; } if (run_feature_protocol(device_name, DCB_LOCAL_CHANGE_PFC, SUBTYPE_DEFAULT) != cmd_success) { LLDPAD_DBG("run_feature_protocol error (PFC)\n"); sResult = cmd_failed; goto add_adapter_error; } /* If APPTLV subtypes are supported then run the * feat_prot for those supported subtypes. */ for (i = 0; i < DCB_MAX_APPTLV ; i++) { if (run_feature_protocol(device_name, DCB_LOCAL_CHANGE_APPTLV(i), i) != cmd_success) { LLDPAD_DBG("run_feature_protocol error (APP)\n"); sResult = cmd_failed; goto add_adapter_error; } } for (i = 0; i < DCB_MAX_LLKTLV ; i++) { if (run_feature_protocol(device_name, DCB_LOCAL_CHANGE_LLINK, i) != cmd_success) { LLDPAD_DBG("run_feature_protocol error (LLINK)\n"); sResult = cmd_failed; goto add_adapter_error; } } EventFlag = 0; DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG | DCB_LOCAL_CHANGE_PFC | DCB_LOCAL_CHANGE_LLINK); for (i = 0; i < DCB_MAX_APPTLV; i++) DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(i)); /* Initialize control state machine */ if (run_control_protocol(device_name, EventFlag) != cmd_success) { LLDPAD_DBG("run_control_protocol error.\n"); sResult = cmd_failed; goto add_adapter_error; } add_adapter_error: if (sResult != cmd_success) { LLDPAD_DBG("add_adapter: Service unable to use network adapter\n"); return false; } return true; } int dcbx_remove_adapter(char *device_name) { char devName[MAX_DEVICE_NAME_LEN]; void *itp = NULL; int not_default = 1; int i = 0; assert(device_name); not_default = memcmp(DEF_CFG_STORE, device_name, strlen(DEF_CFG_STORE)); strncpy (devName, device_name, MAX_DEVICE_NAME_LEN); if (not_default) handle_opermode_true(device_name); features_it itfeat = features_find(&feature_struct, devName); if (itfeat != NULL) { LLDPAD_DBG("free: dcb support %p\n", itp); features_erase(&itfeat); } else { LLDPAD_DBG("remove_adapter: dcb support not found\n"); } pg_it itpg = pg_find(&pg, devName); if (itpg != NULL) { /* this line frees the param of this function! */ pg_erase(&itpg); } else { LLDPAD_DBG("remove_adapter: pg not found\n"); } pfc_it itpfc = pfc_find(&pfc,devName); if (itpfc != NULL) { pfc_erase(&itpfc); } else { LLDPAD_DBG("remove_adapter: pfc not found\n"); } pg_desc_it itbwg = pgdesc_find(&pg_desc, devName); if (itbwg != NULL) { pgdesc_erase(&itbwg); } else if (not_default) { LLDPAD_DBG("remove_adapter: pgid not found\n"); } control_prot_it itcp = ctrl_prot_find(&dcb_control_prot, devName); if (itcp != NULL) { ctrl_prot_erase(&itcp); } else if (not_default) { LLDPAD_DBG("remove_adapter: ctrl not found\n"); } pg_it itprpg = pg_find(&peer_pg, devName); if (itprpg != NULL) { pg_erase(&itprpg); } else if (not_default) { LLDPAD_DBG("remove_adapter: peer pg not found\n"); } pfc_it itprpfc = pfc_find(&peer_pfc, devName); if (itprpfc != NULL) { pfc_erase(&itprpfc); } else if (not_default) { LLDPAD_DBG("remove_adapter: peer pfc not found\n"); } control_prot_it itpcp = ctrl_prot_find(&dcb_peer_control_prot, devName); if (itpcp != NULL) { ctrl_prot_erase(&itpcp); } else if (not_default) { LLDPAD_DBG("remove_adapter: peer ctrl not found\n"); } pg_it itopg = pg_find(&oper_pg, devName); if (itopg != NULL) { pg_erase(&itopg); } else if (not_default) { LLDPAD_DBG("remove_adapter: oper pg not found\n"); } pfc_it itopfc = pfc_find(&oper_pfc, devName); if (itopfc != NULL) { pfc_erase(&itopfc); } else if (not_default) { LLDPAD_DBG("remove_adapter: oper pfc not found\n"); } /* Get the APP TLV and erase. */ for (i = 0; i < DCB_MAX_APPTLV ; i++) { app_it itapp = apptlv_find(&apptlv, devName, i); if (itapp != NULL) apptlv_erase(&itapp); itapp = apptlv_find(&oper_apptlv, devName, i); if (itapp != NULL) apptlv_erase(&itapp); itapp = apptlv_find(&peer_apptlv, devName, i); if (itapp != NULL) apptlv_erase(&itapp); } for (i = 0; i < DCB_MAX_LLKTLV ; i++) { /* Release LLINK data store */ llink_it itllink = llink_find(&llink, devName, i); if (itllink != NULL) llink_erase(&itllink); else LLDPAD_DBG("remove_adapter: llink not found\n"); llink_it itprllink = llink_find(&peer_llink, devName, i); if (itprllink != NULL) llink_erase(&itprllink); else if (not_default) LLDPAD_DBG("remove_adapter: peer llink not found\n"); llink_it itollink = llink_find(&oper_llink, devName, i); if (itollink != NULL) llink_erase(&itollink); else if (not_default) LLDPAD_DBG("remove_adapter: oper llink not found\n"); } lldpad_shm_set_dcbx(device_name, dcbx_subtype0); return true; } cmd_status save_dcbx_state(const char *device_name) { dcbx_state state; app_attribs app_data; control_prot_it ctrl_prot = ctrl_prot_find(&dcb_control_prot, device_name); if (ctrl_prot == NULL) return cmd_device_not_found; state.SeqNo = ctrl_prot->second->SeqNo; state.AckNo = ctrl_prot->second->AckNo; if (get_app((char *)device_name, 0, &app_data) == cmd_success) state.FCoEenable = app_data.protocol.Enable; else return cmd_bad_params; if (get_app((char *)device_name, 1, &app_data) == cmd_success) state.iSCSIenable = app_data.protocol.Enable; else return cmd_bad_params; if (set_dcbx_state(device_name, &state)) return cmd_success; else return cmd_failed; } static int dcbx_free_app_config(char *device_name) { app_it Oper, Local; appgroup_attribs app_data; /* Free FCoE APP data */ Oper = apptlv_find(&oper_apptlv, device_name, APP_FCOE_STYPE); Local = apptlv_find(&apptlv, device_name, APP_FCOE_STYPE); if (Oper || Local) { app_data.dcb_app_idtype = DCB_APP_IDTYPE_ETHTYPE; app_data.dcb_app_id = APP_FCOE_ETHTYPE; app_data.dcb_app_priority = 0; set_hw_app(device_name, &app_data); } /* Free iSCSI APP data */ Oper = apptlv_find(&oper_apptlv, device_name, APP_ISCSI_STYPE); Local = apptlv_find(&apptlv, device_name, APP_ISCSI_STYPE); if (Oper || Local) { app_data.dcb_app_idtype = DCB_APP_IDTYPE_PORTNUM; app_data.dcb_app_id = APP_ISCSI_PORT; app_data.dcb_app_priority = 0; set_hw_app(device_name, &app_data); } /* Free FIP APP data */ Oper = apptlv_find(&oper_apptlv, device_name, APP_FIP_STYPE); Local = apptlv_find(&apptlv, device_name, APP_FIP_STYPE); if (Oper || Local) { app_data.dcb_app_idtype = DCB_APP_IDTYPE_ETHTYPE; app_data.dcb_app_id = APP_FIP_ETHTYPE; app_data.dcb_app_priority = 0; set_hw_app(device_name, &app_data); } return 0; } int dcbx_remove_all(void) { pg_it it; clear_dcbx_state(); for (it = pg.lh_first; it != NULL; it = it->entries.le_next) { if (!memcmp(DEF_CFG_STORE, it->ifname, strlen(DEF_CFG_STORE))) { continue; } save_dcbx_state(it->ifname); /* Remove kernel APP entries */ dcbx_free_app_config(it->ifname); } return 0; } bool add_pg_defaults() { pg_attribs pg_data; char sTmp[MAX_DESCRIPTION_LEN]; bool result = true; int index, portion, rmndr, temp; /* todo: - must be a better way and place to do this */ if (pg_not_initted) { LIST_INIT(&pg); LIST_INIT(&oper_pg); LIST_INIT(&peer_pg); pg_not_initted = false; } memset(&pg_data, 0, sizeof(pg_attribs)); pg_data.protocol.Enable = 1; pg_data.protocol.Willing = 1; pg_data.protocol.Advertise = 1; portion = BW_PERCENT/MAX_BANDWIDTH_GROUPS; rmndr = BW_PERCENT % MAX_BANDWIDTH_GROUPS; temp = rmndr; for (index=0; index < MAX_BANDWIDTH_GROUPS; index++) { pg_data.tx.pg_percent[index] = (u8)portion; if (temp >0) { pg_data.tx.pg_percent[index] += 1; temp--; } } for (index=0; index < MAX_USER_PRIORITIES; index++) { pg_data.tx.up[index].pgid = (u8)(index); pg_data.tx.up[index].bwgid = (u8)index; pg_data.tx.up[index].percent_of_pg_cap = BW_PERCENT; pg_data.tx.up[index].strict_priority = dcb_none; } temp = rmndr; for (index=0; index < MAX_BANDWIDTH_GROUPS; index++) { pg_data.rx.pg_percent[index] = (u8)portion; if (temp >0) { pg_data.rx.pg_percent[index]++; temp--; } } for (index=0; index < MAX_USER_PRIORITIES; index++) { pg_data.rx.up[index].pgid = (u8)(index); pg_data.rx.up[index].bwgid = (u8)index; pg_data.rx.up[index].percent_of_pg_cap = BW_PERCENT; pg_data.rx.up[index].strict_priority = dcb_none; } snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); /* Create pg default data store for the device. */ if (!add_pg(sTmp, &pg_data)) result = false; return result; } bool add_pfc_defaults() { pfc_attribs pfc_data; char sTmp[MAX_DESCRIPTION_LEN]; bool result = true; int index; /* todo: - must be a better way and place to do this */ if (pfc_not_initted) { LIST_INIT(&pfc); LIST_INIT(&oper_pfc); LIST_INIT(&peer_pfc); pfc_not_initted = false; } memset (&pfc_data, 0, sizeof(pfc_attribs)); pfc_data.protocol.Enable = 1; pfc_data.protocol.Willing = 1; pfc_data.protocol.Advertise = 1; for (index=0; index < MAX_TRAFFIC_CLASSES; index++) pfc_data.admin[index] = pfc_disabled; snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); /* Create pfc default data store for the device. */ if (!add_pfc(sTmp, &pfc_data)) result = false; return result; } bool add_app_defaults(u32 subtype) { app_attribs app_data; char sTmp[MAX_DESCRIPTION_LEN]; bool result = true; /* todo: - must be a better way and place to do this */ if (app_not_initted) { LIST_INIT(&apptlv); LIST_INIT(&oper_apptlv); LIST_INIT(&peer_apptlv); app_not_initted = false; } memset(&app_data, 0, sizeof(app_attribs)); app_data.protocol.Enable = 1; app_data.protocol.Willing = 1; app_data.protocol.Advertise = 1; snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); switch (subtype) { case APP_FCOE_STYPE: /* FCoE subtype */ app_data.Length = 1; app_data.AppData[0] = APP_FCOE_DEFAULT_DATA; break; case APP_ISCSI_STYPE: /* iSCSI subtype */ app_data.Length = 1; app_data.AppData[0] = APP_ISCSI_DEFAULT_DATA; break; case APP_FIP_STYPE: /* FIP subtype */ app_data.Length = 1; app_data.AppData[0] = APP_FIP_DEFAULT_DATA; default: break; } /* Create app default data store for the device and app subtype. */ if (!add_apptlv(sTmp, &app_data, subtype, NULL)) result = false; return result; } bool add_llink_defaults(u32 subtype) { llink_attribs llink_data; char sTmp[MAX_DESCRIPTION_LEN]; bool result = true; /* todo: - must be a better way and place to do this */ if (llink_not_initted) { LIST_INIT(&llink); LIST_INIT(&oper_llink); LIST_INIT(&peer_llink); llink_not_initted = false; } memset(&llink_data, 0, sizeof(llink_attribs)); llink_data.protocol.Enable = 1; llink_data.protocol.Willing = 1; llink_data.protocol.Advertise = 1; llink_data.llink.llink_status = 0; snprintf(sTmp, MAX_DESCRIPTION_LEN, DEF_CFG_STORE); /* Create llink default data store for the device. */ if (!add_llink(sTmp, &llink_data, subtype)) result = false; return result; } cmd_status get_pg(char *device_name, pg_attribs *pg_data) { cmd_status result = cmd_success; full_dcb_attribs attribs; if (!pg_data) return cmd_bad_params; memset(&attribs, 0, sizeof(attribs)); pg_it it = pg_find(&pg, device_name); if (it != NULL) { memcpy(pg_data, it->second, sizeof(*pg_data)); } else { result = get_persistent(device_name, &attribs); if (result == cmd_success) memcpy(pg_data, &attribs.pg, sizeof(*pg_data)); else result = cmd_device_not_found; } return result; } cmd_status get_oper_pg(char *device_name, pg_attribs *pg_data) { cmd_status result = cmd_success; if (!pg_data) return cmd_bad_params; pg_it it = pg_find(&oper_pg, device_name); if (it != NULL) memcpy(pg_data, it->second, sizeof(*pg_data)); else result = cmd_device_not_found; return result; } cmd_status get_peer_pg(char *device_name, pg_attribs *pg_data) { cmd_status result = cmd_success; if (!pg_data) return cmd_bad_params; pg_it it = pg_find(&peer_pg, device_name); if (it != NULL) memcpy(pg_data, it->second, sizeof(*pg_data)); else result = cmd_device_not_found; return result; } void mark_pg_sent(char *device_name) { pg_it it = pg_find(&pg, device_name); if (it != NULL) it->second->protocol.tlv_sent = true; } void mark_pfc_sent(char *device_name) { pfc_it it = pfc_find(&pfc, device_name); if (it != NULL) it->second->protocol.tlv_sent = true; } void mark_app_sent(char *device_name) { app_it it; int i; for (i = 0; i < DCB_MAX_APPTLV; i++) { it = apptlv_find(&apptlv, device_name, i); if (it != NULL) it->second->protocol.tlv_sent = true; } } void mark_llink_sent(char *device_name, u32 subtype) { llink_it it = llink_find(&llink, device_name, subtype); if (it != NULL) it->second->protocol.tlv_sent = true; } cmd_status put_pg(char *device_name, pg_attribs *pg_data, pfc_attribs *pfc_data) { full_dcb_attribs attribs; full_dcb_attrib_ptrs attr_ptr; u32 EventFlag = 0; cmd_status result = cmd_success; if (!pg_data) return cmd_bad_params; memset(&attribs, 0, sizeof(attribs)); pg_it it = pg_find(&pg, device_name); if (it != NULL) { /* Lock the data first */ /* detect no config change */ if (memcmp(it->second, pg_data, sizeof(*pg_data)) == 0) goto Exit; /* Check the rules */ memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pg = pg_data; attr_ptr.pfc = pfc_data; if (dcb_check_config(&attr_ptr) != cmd_success) { LLDPAD_DBG("Rule checking failed in put_pg()\n"); result = cmd_bad_params; goto Exit; } if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed put_pg()\n"); result = cmd_device_not_found; goto Exit; } /* Copy the writable protocol * variables */ feature_protocol_attribs *dStore = &(it->second->protocol); if (dStore->Enable && !(pg_data->protocol.Enable)) LLDPAD_INFO("%s PG disabled", device_name); else if (!(dStore->Enable) && pg_data->protocol.Enable) LLDPAD_INFO("%s PG enabled", device_name); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = pg_data->protocol.Advertise; dStore->Enable = pg_data->protocol.Enable; dStore->Willing = pg_data->protocol.Willing; dStore->tlv_sent = false; memcpy(&(it->second->rx), &(pg_data->rx), sizeof(pg_data->rx)); memcpy(&(it->second->tx), &(pg_data->tx), sizeof(pg_data->tx)); if (it->second->protocol.dcbx_st == dcbx_subtype2) it->second->num_tcs = pg_data->num_tcs; DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG); /* Run the protocol */ result = run_dcb_protocol(device_name, EventFlag, SUBTYPE_DEFAULT); } else { /* Not in DCB data store, so store in persistent storage */ if (get_persistent(device_name, &attribs) == cmd_success) { memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pg = pg_data; attr_ptr.pgid = &attribs.descript; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in put_pg()\n"); result = cmd_device_not_found; } } else { result = cmd_device_not_found; } } Exit: return result; } cmd_status put_peer_pg(char *device_name, pg_attribs *peer_pg_data) { /* this function relies on the caller to acquire the DCB lock */ cmd_status result = cmd_success; feature_protocol_attribs *dStore; if (!peer_pg_data) return cmd_bad_params; pg_it peer_it = pg_find(&peer_pg, device_name); if (peer_it == NULL) { LLDPAD_DBG("could not find peer_pg data for %s\n", device_name); result = cmd_device_not_found; goto Exit; } if (peer_pg_data->protocol.dcbx_st == dcbx_subtype2) rebalance_uppcts(peer_pg_data); /* detect config change */ if (memcmp(peer_it->second, peer_pg_data, sizeof(*peer_pg_data)) == 0) goto Exit; /* Copy the writable protocol variables. */ dStore = &(peer_it->second->protocol); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = peer_pg_data->protocol.Advertise; dStore->Enable = peer_pg_data->protocol.Enable; dStore->Willing = peer_pg_data->protocol.Willing; dStore->Oper_version = peer_pg_data->protocol.Oper_version; dStore->Max_version = peer_pg_data->protocol.Max_version; dStore->TLVPresent = peer_pg_data->protocol.TLVPresent; dStore->Error = peer_pg_data->protocol.Error; dStore->dcbx_st = peer_pg_data->protocol.dcbx_st; dStore->Error_Flag = peer_pg_data->protocol.Error_Flag; memcpy(&(peer_it->second->rx), &(peer_pg_data->rx), sizeof(peer_pg_data->rx)); memcpy(&(peer_it->second->tx), &(peer_pg_data->tx), sizeof(peer_pg_data->tx)); if (peer_it->second->protocol.dcbx_st == dcbx_subtype2) peer_it->second->num_tcs = peer_pg_data->num_tcs; Exit: return result; } cmd_status get_pfc(char *device_name, pfc_attribs *pfc_data) { cmd_status result = cmd_success; full_dcb_attribs attribs; memset(&attribs, 0, sizeof(attribs)); if (!pfc_data) return cmd_bad_params; pfc_it it = pfc_find(&pfc, device_name); if (it != NULL) { memcpy(pfc_data, it->second, sizeof(*pfc_data)); } else { result = get_persistent(device_name, &attribs); if (result == cmd_success) memcpy(pfc_data, &attribs.pfc, sizeof(*pfc_data)); else result = cmd_device_not_found; } return result; } cmd_status get_oper_pfc(char *device_name, pfc_attribs *pfc_data) { cmd_status result = cmd_success; if (!pfc_data) return cmd_bad_params; pfc_it it = pfc_find(&oper_pfc, device_name); if (it != NULL) memcpy(pfc_data, it->second, sizeof(*pfc_data)); else result = cmd_device_not_found; return result; } cmd_status get_peer_pfc(char *device_name, pfc_attribs *pfc_data) { cmd_status result = cmd_success; if (!pfc_data) return cmd_bad_params; pfc_it it = pfc_find(&peer_pfc, device_name); if (it != NULL) memcpy(pfc_data, it->second, sizeof(*pfc_data)); else result = cmd_device_not_found; return result; } cmd_status put_pfc(char *device_name, pfc_attribs *pfc_data) { u32 EventFlag = 0; cmd_status result = cmd_success; full_dcb_attribs attribs; bool bChange = false; full_dcb_attrib_ptrs attr_ptr; if (!pfc_data) return cmd_bad_params; memset(&attribs, 0, sizeof(attribs)); pfc_it it = pfc_find(&pfc, device_name); if (it != NULL) { /* detect no config change */ if (memcmp(it->second, pfc_data, sizeof(*pfc_data)) == 0) goto Exit; bChange = true; memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pfc = pfc_data; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in put_pfc()\n"); result = cmd_device_not_found; goto Exit; } feature_protocol_attribs *dStore = &(it->second->protocol); if (dStore->Enable && !(pfc_data->protocol.Enable)) LLDPAD_INFO("%s PFC disabled", device_name); else if (!(dStore->Enable) && pfc_data->protocol.Enable) LLDPAD_INFO("%s PFC enabled", device_name); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = pfc_data->protocol.Advertise; dStore->Enable = pfc_data->protocol.Enable; dStore->Willing = pfc_data->protocol.Willing; dStore->tlv_sent = false; memcpy(it->second->admin, pfc_data->admin, sizeof(pfc_data->admin)); if (it->second->protocol.dcbx_st == dcbx_subtype2) it->second->num_tcs = pfc_data->num_tcs; /* Run the protocol */ DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PFC); if (bChange) result = run_dcb_protocol(device_name, EventFlag, SUBTYPE_DEFAULT); } else { /* Store in persistent storage - not in DCB data store */ if (get_persistent(device_name, &attribs) == cmd_success) { memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pfc = pfc_data; attr_ptr.pgid = &attribs.descript; if (set_persistent(device_name, &attr_ptr) != cmd_success) { result = cmd_device_not_found; } } else result = cmd_device_not_found; } Exit: return result; } cmd_status put_peer_pfc(char *device_name, pfc_attribs *peer_pfc_data) { /* this function relies on the caller to acquire the DCB lock */ cmd_status result = cmd_success; feature_protocol_attribs *dStore; if (!peer_pfc_data) return cmd_bad_params; pfc_it peer_it = pfc_find(&peer_pfc, device_name); if (peer_it == NULL){ LLDPAD_DBG("putting peer_pfc data - bad device name\n"); result = cmd_device_not_found; goto Exit; } /* detect config change */ if (memcmp(peer_it->second, peer_pfc_data, sizeof(*peer_pfc_data)) == 0) goto Exit; dStore = &(peer_it->second->protocol); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = peer_pfc_data->protocol.Advertise; dStore->Enable = peer_pfc_data->protocol.Enable; dStore->Willing = peer_pfc_data->protocol.Willing; dStore->Oper_version = peer_pfc_data->protocol.Oper_version; dStore->Max_version = peer_pfc_data->protocol.Max_version; dStore->TLVPresent = peer_pfc_data->protocol.TLVPresent; dStore->Error = peer_pfc_data->protocol.Error; dStore->dcbx_st = peer_pfc_data->protocol.dcbx_st; dStore->Error_Flag = peer_pfc_data->protocol.Error_Flag; memcpy(peer_it->second->admin, &peer_pfc_data->admin, sizeof(peer_pfc_data->admin)); if (peer_it->second->protocol.dcbx_st == dcbx_subtype2) peer_it->second->num_tcs = peer_pfc_data->num_tcs; Exit: return result; } cmd_status get_app(char *device_name, u32 subtype, app_attribs *app_data) { cmd_status result = cmd_success; full_dcb_attribs attribs; memset(&attribs, 0, sizeof(attribs)); app_it it = apptlv_find(&apptlv, device_name, subtype); if (it != NULL) { memcpy(app_data, it->second, sizeof(*app_data)); } else { result = get_persistent(device_name, &attribs); if (result == cmd_success) { memcpy(app_data, &attribs.app[subtype], sizeof(*app_data)); } else result = cmd_device_not_found; } return result; } cmd_status get_oper_app(char *device_name, u32 subtype, app_attribs *app_data) { cmd_status result = cmd_success; app_it it = apptlv_find(&oper_apptlv, device_name, subtype); if (it != NULL) memcpy(app_data, it->second, sizeof(*app_data)); else result = cmd_device_not_found; return result; } cmd_status get_peer_app(char *device_name, u32 subtype, app_attribs *app_data) { cmd_status result = cmd_success; app_it it = apptlv_find(&peer_apptlv, device_name, subtype); if (it != NULL) memcpy(app_data, it->second, sizeof(*app_data)); else result = cmd_device_not_found; return result; } cmd_status put_app(char *device_name, u32 subtype, app_attribs *app_data) { full_dcb_attribs attribs; full_dcb_attrib_ptrs attr_ptr; u32 EventFlag = 0; cmd_status result = cmd_success; unsigned i; if (!app_data) return cmd_bad_params; memset(&attribs, 0, sizeof(attribs)); app_it it = apptlv_find(&apptlv, device_name, subtype); if (it != NULL) { /* detect no config change */ if (memcmp(it->second, app_data, sizeof(*app_data)) == 0) { goto Exit; } /* Store in persistent storage */ memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.app = app_data; attr_ptr.app_subtype = (u8)subtype; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in put_app()\n"); return cmd_device_not_found; } for (i = 0; i < DCB_MAX_APPTLV; i++) { if (i == subtype) continue; app_it ait = apptlv_find(&apptlv, device_name, i); memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.app = ait->second; attr_ptr.app->protocol.Enable = app_data->protocol.Enable; attr_ptr.app->protocol.Willing = app_data->protocol.Willing; attr_ptr.app_subtype = i; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed put_app()\n"); return cmd_device_not_found; } } feature_protocol_attribs *dStore = &(it->second->protocol); if (dStore->Enable && !(app_data->protocol.Enable)) LLDPAD_INFO("%s APP disabled", device_name); else if (!(dStore->Enable) && app_data->protocol.Enable) LLDPAD_INFO("%s APP enabled", device_name); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = app_data->protocol.Advertise; dStore->Enable = app_data->protocol.Enable; dStore->Willing = app_data->protocol.Willing; dStore->tlv_sent = false; if (app_data->Length) { it->second->Length = app_data->Length; memcpy(&(it->second->AppData), &(app_data->AppData), app_data->Length); } DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(subtype)); result = run_dcb_protocol(device_name, EventFlag, subtype); } else { /* Not in DCB data store, store in persistent storage */ if (get_persistent(device_name, &attribs) == cmd_success) { memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.app = app_data; attr_ptr.app_subtype = (u8)subtype; attr_ptr.pgid = &attribs.descript; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in put_app()\n"); result = cmd_device_not_found; } } else { result = cmd_device_not_found; } } Exit: return result; } cmd_status put_peer_app(char *device_name, u32 subtype, app_attribs *peer_app_data) { /* this function relies on the caller to acquire the DCB lock */ feature_protocol_attribs *dStore; cmd_status result = cmd_success; if (!peer_app_data) return cmd_bad_params; app_it peer_it = apptlv_find(&peer_apptlv, device_name, subtype); if (peer_it == NULL) { LLDPAD_DBG("putting peer_app data - bad device name\n"); result = cmd_device_not_found; goto Exit; } if (memcmp(peer_it->second, peer_app_data, sizeof(*peer_app_data)) == 0) goto Exit; dStore = &(peer_it->second->protocol); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = peer_app_data->protocol.Advertise; dStore->Enable = peer_app_data->protocol.Enable; dStore->Willing = peer_app_data->protocol.Willing; dStore->Oper_version = peer_app_data->protocol.Oper_version; dStore->Max_version = peer_app_data->protocol.Max_version; dStore->TLVPresent = peer_app_data->protocol.TLVPresent; dStore->Error = peer_app_data->protocol.Error; dStore->dcbx_st = peer_app_data->protocol.dcbx_st; dStore->Error_Flag = peer_app_data->protocol.Error_Flag; peer_it->second->Length = peer_app_data->Length; memcpy(&(peer_it->second->AppData), &(peer_app_data->AppData), peer_app_data->Length); Exit: return result; } cmd_status put_llink(char *device_name, u32 subtype, llink_attribs *llink_data) { full_dcb_attrib_ptrs attr_ptr; full_dcb_attribs attribs; u32 EventFlag = 0; cmd_status result = cmd_success; bool bChange = false; if (!llink_data) return cmd_bad_params; memset(&attribs, 0, sizeof(attribs)); llink_it it = llink_find(&llink, device_name, subtype); if (it != NULL) { /* Lock the data first */ /* detect no config change */ if (memcmp(it->second, llink_data, sizeof(*llink_data)) == 0) goto Exit; bChange = true; memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.llink = llink_data; attr_ptr.llink_subtype = LLINK_FCOE_STYPE; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed in put_llink()\n"); result = cmd_device_not_found; goto Exit; } feature_protocol_attribs *dStore = &(it->second->protocol); if (dStore->Enable && !(llink_data->protocol.Enable)) LLDPAD_INFO("%s LLINK disabled", device_name); else if (!(dStore->Enable) && llink_data->protocol.Enable) LLDPAD_INFO("%s LLINK enabled", device_name); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = llink_data->protocol.Advertise; dStore->Enable = llink_data->protocol.Enable; dStore->Willing = llink_data->protocol.Willing; dStore->tlv_sent = false; it->second->llink.llink_status = llink_data->llink.llink_status; memcpy(&(it->second->llink), &(llink_data->llink), sizeof(llink_data->llink)); /* Run the protocol */ DCB_SET_FLAGS(EventFlag, DCB_LOCAL_CHANGE_LLINK); if (bChange) { result = run_dcb_protocol(device_name, EventFlag, SUBTYPE_DEFAULT); } } else { /* Store in persistent storage - though not in DCB data store*/ if (get_persistent(device_name, &attribs) == cmd_success) { memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.llink = llink_data; attr_ptr.llink_subtype = LLINK_FCOE_STYPE; attr_ptr.pgid = &attribs.descript; if (set_persistent(device_name, &attr_ptr) != cmd_success) { result = cmd_device_not_found; } } else result = cmd_device_not_found; } Exit: return result; } cmd_status put_peer_llink(char *device_name, u32 subtype, llink_attribs *peer_llink_data) { /* this function relies on the caller to acquire the DCB lock */ feature_protocol_attribs *dStore; cmd_status result = cmd_success; if (!peer_llink_data) return cmd_bad_params; llink_it peer_it = llink_find(&peer_llink, device_name, subtype); if (peer_it == NULL){ LLDPAD_DBG("putting peer_llink data - bad device name\n"); result = cmd_device_not_found; goto Exit; } /* detect config change */ if (memcmp(peer_it->second, peer_llink_data, sizeof(*peer_llink_data)) == 0) { goto Exit; } dStore = &(peer_it->second->protocol); dStore->Advertise_prev = dStore->Advertise; dStore->Advertise = peer_llink_data->protocol.Advertise; dStore->Enable = peer_llink_data->protocol.Enable; dStore->Willing = peer_llink_data->protocol.Willing; dStore->Oper_version = peer_llink_data->protocol.Oper_version; dStore->Max_version = peer_llink_data->protocol.Max_version; dStore->TLVPresent = peer_llink_data->protocol.TLVPresent; dStore->Error = peer_llink_data->protocol.Error; dStore->dcbx_st = peer_llink_data->protocol.dcbx_st; dStore->Error_Flag = peer_llink_data->protocol.Error_Flag; memcpy(&(peer_it->second->llink), &(peer_llink_data->llink), sizeof(peer_llink_data->llink)); Exit: return result; } cmd_status get_llink(char *device_name, u32 subtype, llink_attribs *llink_data) { full_dcb_attribs attribs; cmd_status result = cmd_success; memset(&attribs, 0, sizeof(attribs)); if (!llink_data) return cmd_bad_params; llink_it it = llink_find(&llink, device_name, subtype); if (it != NULL) { memcpy(llink_data, it->second, sizeof(*llink_data)); } else { result = get_persistent(device_name, &attribs); if (result == cmd_success) memcpy(llink_data, &attribs.llink[subtype], sizeof(*llink_data)); else result = cmd_device_not_found; } return result; } cmd_status get_oper_llink(char *device_name, u32 subtype, llink_attribs *llink_data) { cmd_status result = cmd_success; if (!llink_data) return cmd_bad_params; llink_it it = llink_find(&oper_llink, device_name, subtype); if (it != NULL) memcpy(llink_data, it->second, sizeof(*llink_data)); else result = cmd_device_not_found; return result; } cmd_status get_peer_llink(char *device_name, u32 subtype, llink_attribs *llink_data) { cmd_status result = cmd_success; if (!llink_data) return cmd_bad_params; llink_it it = llink_find(&peer_llink, device_name, subtype); if (it != NULL) memcpy(llink_data, it->second, sizeof(*llink_data)); else result = cmd_device_not_found; return result; } cmd_status get_control(char *device_name, control_protocol_attribs *control_data) { cmd_status result = cmd_success; if (!control_data) return cmd_bad_params; control_prot_it it = ctrl_prot_find(&dcb_control_prot, device_name); if (it != NULL) memcpy( control_data, it->second, sizeof(*control_data)); else result = cmd_device_not_found; return result; } cmd_status get_peer_control(char *device_name, control_protocol_attribs *peer_control_data) { cmd_status result = cmd_success; if (!peer_control_data) return cmd_bad_params; control_prot_it it = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (it != NULL) memcpy(peer_control_data, it->second, sizeof(*peer_control_data)); else result = cmd_device_not_found; return result; } cmd_status put_peer_control(char *device_name, control_protocol_attribs *peer_control_data) { /* this function relies on the caller to acquire the DCB lock */ control_protocol_attribs *dStore = NULL; cmd_status result = cmd_success; if (!peer_control_data) return cmd_bad_params; control_prot_it peer_ctrl_prot = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (peer_ctrl_prot) dStore = peer_ctrl_prot->second; if (peer_ctrl_prot && ((peer_control_data->Error_Flag & DUP_DCBX_TLV_CTRL) || (peer_control_data->Error_Flag & TOO_MANY_NGHBRS))) { dStore->Error_Flag = peer_control_data->Error_Flag; } else if (peer_ctrl_prot != NULL) { dStore->SeqNo = peer_control_data->SeqNo; dStore->AckNo = peer_control_data->AckNo; dStore->Max_version = peer_control_data->Max_version; dStore->Oper_version = peer_control_data->Oper_version; dStore->RxDCBTLVState = peer_control_data->RxDCBTLVState; dStore->Error_Flag = peer_control_data->Error_Flag; } else { result = cmd_device_not_found; } return result; } cmd_status get_bwg_descrpt(char *device_name, u8 bwgid, char **name) { full_dcb_attribs attribs; cmd_status result = cmd_success; int size; if (*name != NULL) { free(*name); *name = NULL; } memset(&attribs, 0, sizeof(attribs)); pg_desc_it it = pgdesc_find(&pg_desc, device_name); if ((it != NULL) && (bwgid < it->second->max_pgid_desc)) { size = (int)strlen(it->second->pgid_desc[bwgid]) + sizeof(char); /* Localization OK */ *name = (char*)malloc(size); if (*name != NULL) { strncpy(*name, it->second->pgid_desc[bwgid], size); /* Localization OK */ } else { goto Error; } } else { result = get_persistent(device_name, &attribs); if (result == cmd_success) { size = (int)strlen( attribs.descript.pgid_desc[bwgid]) + sizeof(char); *name = (char*)malloc(size); if (*name != NULL) { strncpy(*name, attribs.descript.pgid_desc[bwgid], size); /* Localization OK */ } else { goto Error; } } else { result = cmd_device_not_found; } } return result; Error: LLDPAD_DBG("get_bwg_descrpt: Failed memory alloc\n"); return cmd_failed; } cmd_status put_bwg_descrpt(char *device_name, u8 bwgid, char *name) { full_dcb_attribs attribs; full_dcb_attrib_ptrs attr_ptr; cmd_status result = cmd_success; unsigned int size; if (!name) return cmd_bad_params; size = (unsigned int)strlen(name); /* Localization OK */ memset(&attribs, 0, sizeof(attribs)); pg_desc_it it = pgdesc_find(&pg_desc, device_name); if ((it != NULL) && (bwgid < it->second->max_pgid_desc)) { /* Only take as many characters as can be held */ if (!(size < sizeof(it->second->pgid_desc[bwgid]))) size = sizeof(it->second->pgid_desc[bwgid])-1; memcpy(it->second->pgid_desc[bwgid], name, size); /* Put a null at the end incase it was truncated */ it->second->pgid_desc[bwgid][size] = '\0'; memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pgid = it->second; if (set_persistent(device_name, &attr_ptr) != cmd_success) return cmd_device_not_found; } else { /* Store in persistent storage - though not in * DCB data store */ if (get_persistent(device_name, &attribs) == cmd_success) { if (!(size < sizeof(attribs.descript.pgid_desc[bwgid]))) size = sizeof( attribs.descript.pgid_desc[bwgid]) - 1; memcpy(attribs.descript.pgid_desc[bwgid], name, size); /* Put a null at the end in case it was * truncated */ attribs.descript.pgid_desc[bwgid][size] = '\0'; memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pgid = &attribs.descript; if (set_persistent(device_name, &attr_ptr) != cmd_success) { LLDPAD_DBG("Set persistent failed " "in put_bwg_descrpt()\n"); result = cmd_device_not_found; } } else result = cmd_device_not_found; } return result; } /****************************************************************************** ** ** Method: CopyConfigToOper ** ** Description: Function to copy local or peer PG or PFC or APPTLV ** configurations to oper configuration. ** ** Arguments: char *device_name ** u32 SrcFlag - Tells where to copy from (local or peer) ** u32 EventFlag ** u32 SubType - This is valid only for DCB_LOCAL_CHANGE_APPTLV and ** DCB_REMOTE_CHANGE_APPTLV ** ** Returns: true if successful, failure code otherwise. ** ******************************************************************************/ void CopyConfigToOper(char *device_name, u32 SrcFlag, u32 EventFlag, u32 Subtype) { int i = 0; /* this function relies on the caller to acquire the DCB lock */ LLDPAD_DBG(" CopyConfigToOper %s\n", device_name); if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG, DCB_LOCAL_CHANGE_PG) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG, DCB_REMOTE_CHANGE_PG)) { /* Get the Local or Peer store */ pg_it Src; pg_it localSrc = NULL; if (SrcFlag == LOCAL_STORE) { Src = localSrc = pg_find(&pg, device_name); if (localSrc == NULL) return; } else if (SrcFlag == PEER_STORE) { Src = pg_find(&peer_pg, device_name); if (Src == NULL) return; localSrc = pg_find(&pg, device_name); if (localSrc == NULL) return; } else { /* We don't support */ return; } /* Get the Oper store */ pg_it Oper = pg_find(&oper_pg, device_name); if (Oper == NULL) return; /* Copy Src to Oper. */ for (i = 0; i < MAX_USER_PRIORITIES; i++) { Oper->second->tx.up[i].bwgid = Src->second->tx.up[i].bwgid; Oper->second->rx.up[i].bwgid = Src->second->rx.up[i].bwgid; Oper->second->tx.up[i].strict_priority = Src->second->tx.up[i].strict_priority; Oper->second->rx.up[i].strict_priority = Src->second->rx.up[i].strict_priority; Oper->second->tx.up[i].percent_of_pg_cap = Src->second->tx.up[i].percent_of_pg_cap; Oper->second->rx.up[i].percent_of_pg_cap = Src->second->rx.up[i].percent_of_pg_cap; if (SrcFlag == PEER_STORE) { Oper->second->tx.up[i].pgid = Src->second->tx.up[i].pgid; Oper->second->rx.up[i].pgid = Src->second->rx.up[i].pgid; } else { Oper->second->tx.up[i].pgid = localSrc->second->tx.up[i].pgid; Oper->second->rx.up[i].pgid = localSrc->second->rx.up[i].pgid; } } for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { Oper->second->tx.pg_percent[i] = Src->second->tx.pg_percent[i]; Oper->second->rx.pg_percent[i] = Src->second->rx.pg_percent[i]; } } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PFC, DCB_LOCAL_CHANGE_PFC) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC, DCB_REMOTE_CHANGE_PFC)) { /* Get the Local or Peer store */ pfc_it Src; if (SrcFlag == LOCAL_STORE) { Src = pfc_find(&pfc, device_name); if (Src == NULL) return; } else if (SrcFlag == PEER_STORE) { Src = pfc_find(&peer_pfc, device_name); if (Src == NULL) return; } else { /* We don't support */ return; } /* Get Oper store */ pfc_it Oper = pfc_find(&oper_pfc, device_name); if (Oper == NULL) return; /* Copy Src to Oper. */ memcpy(&Oper->second->admin, &Src->second->admin, sizeof(Src->second->admin)); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(Subtype), DCB_LOCAL_CHANGE_APPTLV(Subtype))|| DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(Subtype), DCB_REMOTE_CHANGE_APPTLV(Subtype))) { /* Get the Local or Peer store */ app_it Src; if (SrcFlag == LOCAL_STORE) { Src = apptlv_find(&apptlv, device_name, Subtype); if (Src == NULL) return; } else if (SrcFlag == PEER_STORE) { Src = apptlv_find(&peer_apptlv, device_name, Subtype); if (Src == NULL) return; } else { /* We don't support */ return; } /* Get Oper store */ app_it Oper = apptlv_find(&oper_apptlv, device_name, Subtype); if (Oper != NULL) { /* Copy Src to Oper. */ LLDPAD_DBG(" Changing app data from %02x to %02x\n", Oper->second->AppData[0], Src->second->AppData[0]); Oper->second->Length = Src->second->Length; memcpy(Oper->second->AppData, Src->second->AppData, Src->second->Length); } } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_LLINK, DCB_LOCAL_CHANGE_LLINK) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK, DCB_REMOTE_CHANGE_LLINK)) { /* Get the Local or Peer store */ /* Get the Local or Peer store */ llink_it Src; if (SrcFlag == LOCAL_STORE) { Src = llink_find(&llink, device_name, Subtype); if (Src == NULL) return; } else if (SrcFlag == PEER_STORE) { Src = llink_find(&peer_llink, device_name, Subtype); if (Src == NULL) return; } else { /* We don't support */ return; } /* Get Oper store */ llink_it Oper = llink_find(&oper_llink, device_name, Subtype); if (Oper == NULL) { return; } /* Copy Src to Oper. */ memset(&Oper->second->llink, 0, sizeof(Oper->second->llink)); memcpy( &Oper->second->llink, &Src->second->llink, sizeof(Src->second->llink)); } } /****************************************************************************** ** ** Method: LocalPeerCompatible ** ** Description: Function to check if local and peer configurations matches. ** ** Arguments: char *device_name ** ULONG EventFlag ** ULONG SubType - This is valid only for DCB_LOCAL_CHANGE_APPTLV ** and DCB_REMOTE_CHANGE_APPTLV ** ** Returns: true if successful, failure code otherwise. ** ******************************************************************************/ bool LocalPeerCompatible(char *device_name, u32 EventFlag, u32 Subtype) { int i = 0; if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG, DCB_LOCAL_CHANGE_PG) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG, DCB_REMOTE_CHANGE_PG)) { /* Get the Local and Peer PG store */ pg_it Local = pg_find(&pg, device_name); pg_attribs *lpg; pg_attribs *ppg; bool match = false; if (Local == NULL) { goto Error; } pg_it Peer = pg_find(&peer_pg, device_name); if (Peer == NULL) { goto Error; } lpg = Local->second; ppg = Peer->second; match = true; if (ppg->protocol.dcbx_st == dcbx_subtype1) { for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (lpg->tx.up[i].bwgid != ppg->tx.up[i].bwgid) match = false; if (lpg->tx.up[i].strict_priority != ppg->tx.up[i].strict_priority) match = false; if (lpg->tx.up[i].percent_of_pg_cap != ppg->tx.up[i].percent_of_pg_cap) match = false; } for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { if (lpg->tx.pg_percent[i] != ppg->tx.pg_percent[i]) match = false; } } if (match) { LLDPAD_DBG(" COMPAT PG - passed\n"); return true; } LLDPAD_DBG(" COMPAT PG - failed\n"); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PFC, DCB_LOCAL_CHANGE_PFC)|| DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC, DCB_REMOTE_CHANGE_PFC)) { /* Get the Local and Peer PFC store */ pfc_it Local = pfc_find(&pfc, device_name); if (Local == NULL) { goto Error; } pfc_it Peer = pfc_find(&peer_pfc, device_name); if (Peer == NULL) { goto Error; } if (!memcmp(&Local->second->admin, &Peer->second->admin, sizeof(Local->second->admin))) { LLDPAD_DBG(" COMPAT PFC - passed\n"); return true; } LLDPAD_DBG(" COMPAT PFC - failed\n"); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(Subtype), DCB_LOCAL_CHANGE_APPTLV(Subtype))|| DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(Subtype), DCB_REMOTE_CHANGE_APPTLV(Subtype))) { /* Get the Local and Peer APPTLV store */ app_it Local = apptlv_find(&apptlv, device_name, Subtype); if (Local == NULL) goto Error; app_it Peer = apptlv_find(&peer_apptlv, device_name, Subtype); if (Peer == NULL) goto Error; if (Local->second->Length == Peer->second->Length) { if (!memcmp(Local->second->AppData, Peer->second->AppData, Local->second->Length)) { return true; } } LLDPAD_DBG(" COMPAT APP - failed\n"); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_LLINK, DCB_LOCAL_CHANGE_LLINK)|| DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK, DCB_REMOTE_CHANGE_LLINK)) { LLDPAD_DBG(" COMPAT LLINK - failed\n"); return false; } return false; Error: LLDPAD_DBG(" LocalPeerCompatible: device not found\n"); return false; } /* returns: 0 on success * 1 on failure */ int set_configuration(char *device_name, u32 EventFlag) { cmd_status sResult; full_dcb_attrib_ptrs attr_ptr; if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG, DCB_LOCAL_CHANGE_PG) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG, DCB_REMOTE_CHANGE_PG)) { /* Get Oper store */ pg_it Oper = pg_find(&oper_pg, device_name); pg_it Local = pg_find(&pg, device_name); if (Oper == NULL || Local == NULL) return cmd_failed; Oper->second->num_tcs = Local->second->num_tcs; pgroup_attribs pg_data; if (DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG, DCB_REMOTE_CHANGE_PG)) { pfc_it op_pfc = pfc_find(&oper_pfc, device_name); memset(&attr_ptr, 0, sizeof(attr_ptr)); attr_ptr.pg = (Oper->second); attr_ptr.pfc = (op_pfc->second); if ((sResult = dcb_check_config(&attr_ptr)) != cmd_success) { LLDPAD_DBG(" PG rule check returned error %d\n", sResult); /* Localization OK */ return sResult; } } memcpy(&(pg_data.rx), &(Oper->second->rx), sizeof(pg_data.rx)); memcpy(&(pg_data.tx), &(Oper->second->tx), sizeof(pg_data.tx)); return set_hw_pg(device_name, &pg_data, Local->second->protocol.OperMode); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PFC, DCB_LOCAL_CHANGE_PFC) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC, DCB_REMOTE_CHANGE_PFC)) { /* Get Oper store */ pfc_it Oper = pfc_find(&oper_pfc, device_name); pfc_it Local = pfc_find(&pfc, device_name); if (Oper == NULL || Local == NULL) return cmd_failed; return set_hw_pfc(device_name, Oper->second->admin, Local->second->protocol.OperMode); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_LLINK, DCB_LOCAL_CHANGE_LLINK) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK, DCB_REMOTE_CHANGE_LLINK)) { return cmd_success; } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(APP_FCOE_STYPE), DCB_LOCAL_CHANGE_APPTLV(APP_FCOE_STYPE)) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(APP_FCOE_STYPE), DCB_REMOTE_CHANGE_APPTLV(APP_FCOE_STYPE))) { appgroup_attribs app_data; /* Get Oper store */ app_it Oper = apptlv_find(&oper_apptlv, device_name, APP_FCOE_STYPE); if (Oper == NULL) return cmd_success; app_data.dcb_app_idtype = DCB_APP_IDTYPE_ETHTYPE; app_data.dcb_app_id = APP_FCOE_ETHTYPE; app_data.dcb_app_priority = Oper->second->AppData[0]; return set_hw_app(device_name, &app_data); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(APP_FIP_STYPE), DCB_LOCAL_CHANGE_APPTLV(APP_FIP_STYPE)) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(APP_FIP_STYPE), DCB_REMOTE_CHANGE_APPTLV(APP_FIP_STYPE))) { appgroup_attribs app_data; /* Get Oper store */ app_it Oper = apptlv_find(&oper_apptlv, device_name, APP_FIP_STYPE); /* FIP subtype is only sent to kernel if Operational this * way the FCoE stack and applications use the FCoE APP * entry until FIP is operational. */ if (Oper == NULL || (Oper->second && Oper->second->protocol.OperMode == false)) return cmd_success; app_data.dcb_app_idtype = DCB_APP_IDTYPE_ETHTYPE; app_data.dcb_app_id = APP_FIP_ETHTYPE; app_data.dcb_app_priority = Oper->second->AppData[0]; return set_hw_app(device_name, &app_data); } else if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_APPTLV(APP_ISCSI_STYPE), DCB_LOCAL_CHANGE_APPTLV(APP_ISCSI_STYPE)) || DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(APP_ISCSI_STYPE), DCB_REMOTE_CHANGE_APPTLV(APP_ISCSI_STYPE))) { appgroup_attribs app_data; /* Get Oper store */ app_it Oper = apptlv_find(&oper_apptlv, device_name, APP_ISCSI_STYPE); app_it Local = apptlv_find(&apptlv, device_name, APP_ISCSI_STYPE); if (Oper == NULL || Local == NULL) { return cmd_failed; } app_data.dcb_app_idtype = DCB_APP_IDTYPE_PORTNUM; app_data.dcb_app_id = APP_ISCSI_PORT; app_data.dcb_app_priority = Oper->second->AppData[0]; return set_hw_app(device_name, &app_data); } return cmd_success; } /****************************************************************************** ** ** Method: handle_opermode_true ** ** Description: This routine is called by remove_adapter. ** For any feature whose OperMode is true, send an ** event since the port is going away - indicating an ** OperMode change. ** ** Arguments: char *device_name ** ** Returns: cmd_success if successful, failure code otherwise. ** ******************************************************************************/ static void handle_opermode_true(char *device_name) { pg_attribs pg_data; pfc_attribs pfc_data; app_attribs app_data; llink_attribs llink_data; int i = 0; if (get_pg(device_name, &pg_data) == cmd_success) if (pg_data.protocol.OperMode) pg_event(device_name, EVENT_OPERMODE); if (get_pfc(device_name, &pfc_data) == cmd_success) if (pfc_data.protocol.OperMode) pfc_event(device_name, EVENT_OPERMODE); for (i = 0; i < DCB_MAX_APPTLV ; i++) if (get_app(device_name, i, &app_data) == cmd_success) if (app_data.protocol.OperMode) app_event(device_name, i, EVENT_OPERMODE); for (i = 0; i < DCB_MAX_LLKTLV ; i++) if (get_llink(device_name, i, &llink_data) == cmd_success) if (llink_data.protocol.OperMode) llink_event(device_name, i, EVENT_OPERMODE); } /****************************************************************************** ** ** Method: run_feature_protocol ** ** Description: This function runs feature state machine for a local or remote ** change. ** The function caller should acquire lock before calling this function. ** Caller must call this function per event. ** ** Arguments: char *device_name ** u32 EventFlag ** u32 SubType - This is valid only for DCB_LOCAL_CHANGE_APPTLV and ** DCB_REMOTE_CHANGE_APPTLV ** ** Returns: cmd_success if successful, failure code otherwise. ** ******************************************************************************/ cmd_status run_feature_protocol(char *device_name, u32 EventFlag, u32 Subtype) { feature_protocol_attribs *feat_prot = NULL; feature_protocol_attribs *peer_feat_prot = NULL; control_prot_it ctrl_prot = NULL; control_prot_it peer_ctrl_prot = NULL; bool ErrorChanged = false; bool Err, local_change; bool just_added = false; pg_attribs old_pg_opcfg; int old_pg_opmode = 0; u32 pg_events = 0; pfc_attribs old_pfc_opcfg; int old_pfc_opmode = 0; u32 pfc_events = 0; app_attribs old_app_opcfg; int old_app_opmode = 0; u32 app_events = 0; llink_attribs old_llink_opcfg; int old_llink_opmode = 0; u32 llink_events = 0; int i, mask; if (!dcbx_check_active(device_name)) return cmd_success; memset(&old_pg_opcfg, 0, sizeof(pg_attribs)); memset(&old_pfc_opcfg, 0, sizeof(pfc_attribs)); memset(&old_app_opcfg, 0, sizeof(app_attribs)); memset(&old_llink_opcfg, 0, sizeof(llink_attribs)); /* Get the protocol store */ if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_PG) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_PG)) { /* Get the local feature protocol */ pg_it it = pg_find(&pg, device_name); if (it != NULL) { feat_prot = &it->second->protocol; old_pg_opmode = feat_prot->OperMode; } else { goto ErrNoDevice; } /* Get the remote feature protocol */ pg_it it1 = pg_find(&peer_pg, device_name); if (it1 != NULL) { peer_feat_prot = &it1->second->protocol; } else { goto ErrNoDevice; } if ((peer_feat_prot->Error_Flag & DUP_DCBX_TLV_CTRL) || (peer_feat_prot->Error_Flag & DUP_DCBX_TLV_PG)) { LLDPAD_DBG("** FLAG: MISSING PG TLV \n"); feat_prot->Error_Flag |= FEAT_ERR_MULTI_TLV; } else { feat_prot->Error_Flag &= ~FEAT_ERR_MULTI_TLV; } pg_it Oper = pg_find(&oper_pg, device_name); if (Oper != NULL) { memcpy(&(old_pg_opcfg.rx), &(Oper->second->rx), sizeof(old_pg_opcfg.rx)); memcpy(&(old_pg_opcfg.tx), &(Oper->second->tx), sizeof(old_pg_opcfg.tx)); } else { goto ErrNoDevice; } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_PFC) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_PFC)) { /* Get the local feature protocol */ pfc_it it = pfc_find(&pfc, device_name); if (it != NULL) { feat_prot = &it->second->protocol; old_pfc_opmode = feat_prot->OperMode; } else { goto ErrNoDevice; } /* Get the remote feature protocol */ pfc_it it1 = pfc_find(&peer_pfc, device_name); if (it1 != NULL) { peer_feat_prot = &it1->second->protocol; } else { goto ErrNoDevice; } if ((peer_feat_prot->Error_Flag & DUP_DCBX_TLV_CTRL) || (peer_feat_prot->Error_Flag & DUP_DCBX_TLV_PFC)) { LLDPAD_DBG("** FLAG: MISSING PFC TLV \n"); feat_prot->Error_Flag |= FEAT_ERR_MULTI_TLV; } else { feat_prot->Error_Flag &= ~FEAT_ERR_MULTI_TLV; } pfc_it Oper = pfc_find(&oper_pfc, device_name); if (Oper != NULL) { memcpy(&old_pfc_opcfg.admin, &Oper->second->admin, sizeof(old_pfc_opcfg.admin)); } else { goto ErrNoDevice; } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_APPTLV(Subtype)) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_APPTLV(Subtype))) { /* Get the local feature protocol */ app_it it = apptlv_find(&apptlv, device_name, Subtype); if (it != NULL) { feat_prot = &it->second->protocol; old_app_opmode = feat_prot->OperMode; } else { goto ErrNoDevice; } /* Get the remote feature protocol */ app_it it1 = apptlv_find(&peer_apptlv, device_name, Subtype); if (it1 != NULL) { peer_feat_prot = &it1->second->protocol; } else { goto ErrNoDevice; } if ((peer_feat_prot->Error_Flag & DUP_DCBX_TLV_CTRL) || (peer_feat_prot->Error_Flag & DUP_DCBX_TLV_APP)) { LLDPAD_DBG("** FLAG: MISSING APP TLV \n"); feat_prot->Error_Flag |= FEAT_ERR_MULTI_TLV; } else { feat_prot->Error_Flag &= ~FEAT_ERR_MULTI_TLV; } app_it Oper = apptlv_find(&oper_apptlv, device_name, Subtype); if (Oper != NULL) { old_app_opcfg.Length = Oper->second->Length; memcpy(&old_app_opcfg.AppData[0], &((*Oper).second->AppData[0]), old_app_opcfg.Length); } else { goto ErrNoDevice; } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_LLINK) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_LLINK)) { /* Get the local feature protocol */ llink_it it = llink_find(&llink, device_name, Subtype); if (it != NULL) { feat_prot = &it->second->protocol; old_llink_opmode = feat_prot->OperMode; } else { goto ErrNoDevice; } /* Get the remote feature protocol */ llink_it it1 = llink_find(&peer_llink, device_name, Subtype); if (it1 != NULL) { peer_feat_prot = &it1->second->protocol; } else { goto ErrNoDevice; } if ((peer_feat_prot->Error_Flag & DUP_DCBX_TLV_CTRL) || (peer_feat_prot->Error_Flag & DUP_DCBX_TLV_LLINK)) { LLDPAD_DBG("** FLAG: MISSING LLINK TLV \n"); feat_prot->Error_Flag |= FEAT_ERR_MULTI_TLV; } else { feat_prot->Error_Flag &= ~FEAT_ERR_MULTI_TLV; } llink_it Oper = llink_find(&oper_llink, device_name, Subtype); if (Oper != NULL) { memcpy(&old_llink_opcfg.llink, &Oper->second->llink, sizeof(old_llink_opcfg.llink)); } else { goto ErrNoDevice; } } /* Get the local control protocol variables. */ ctrl_prot = ctrl_prot_find(&dcb_control_prot, device_name); if (ctrl_prot == NULL) goto ErrNoDevice; /* Get the remote control protocol variables. */ peer_ctrl_prot = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (peer_ctrl_prot == NULL) goto ErrNoDevice; if ((feat_prot == NULL) || (peer_feat_prot == NULL)) goto ErrNoDevice; if (peer_ctrl_prot->second->Error_Flag & TOO_MANY_NGHBRS) { peer_feat_prot->TLVPresent = false; LLDPAD_DBG("** Set Flag: TOO MANY NEIGHBORS \n"); feat_prot->Error_Flag |= FEAT_ERR_MULTI_PEER; } else { feat_prot->Error_Flag &= ~FEAT_ERR_MULTI_PEER; } if (feat_prot->State == DCB_INIT) { feat_prot->Oper_version = feat_prot->Max_version; feat_prot->OperMode = false; feat_prot->Error = false; /* Set the parameters. */ feat_prot->FeatureSeqNo = ctrl_prot->second->SeqNo + 1; /* If Syncd false, then control state machine will * TX LLDP message with local config. */ feat_prot->Syncd = !(feat_prot->Advertise); LLDPAD_DBG("Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); feat_prot->State = DCB_LISTEN; /* Ensure PFC settings are synced up on initialization */ if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_PFC)) just_added = true; } if (feat_prot->State == DCB_LISTEN) { LLDPAD_DBG("Feature state machine (flags %x)\n", EventFlag); local_change = false; mask = DCB_SET_ALL_FLAGS(LOCAL); if (EventFlag & mask) { local_change = true; LLDPAD_DBG(" Local change*0x%x:", EventFlag); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_PG) ? "PG" : ""); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_PFC) ? "PFC" : ""); for (i = 0; i < DCB_MAX_APPTLV; i++) if (EventFlag & DCB_LOCAL_CHANGE_APPTLV(i)) LLDPAD_DBG(" APP%d", i); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_LLINK) ? "LLINK" : ""); LLDPAD_DBG("\n"); } /* If local changed and we are already synched... */ if (local_change && feat_prot->Syncd) { LLDPAD_DBG(" Local feature already synced\n"); /* If we are not synched, we won't be able * to accept new local changes until we get * back remote changes for previous local * change. */ /* Set the parameters. */ feat_prot->FeatureSeqNo = ctrl_prot->second->SeqNo + 1; /* If Syncd false, then control state machine * will TX LLDP message with local config. */ if ((feat_prot->Advertise == true) || (feat_prot->Advertise_prev == true)) { feat_prot->Syncd = false; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); } else { feat_prot->Syncd = true; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); feat_prot->tlv_sent = true; } } /* F4 If don't advertise, then copy the local config to * Oper config. */ if (!feat_prot->Advertise) { LLDPAD_DBG(" F5 - Advertise mode OFF:"); LLDPAD_DBG(" %s", (EventFlag&(DCB_LOCAL_CHANGE_PG | DCB_REMOTE_CHANGE_PG)) ? "PG" : ""); LLDPAD_DBG(" %s", (EventFlag&(DCB_LOCAL_CHANGE_PFC | DCB_REMOTE_CHANGE_PFC)) ? "PFC" : ""); if (EventFlag & DCB_LOCAL_CHANGE_APPTLV(Subtype)) LLDPAD_DBG(" APP%d", Subtype); LLDPAD_DBG(" %s", (EventFlag&(DCB_LOCAL_CHANGE_LLINK | DCB_REMOTE_CHANGE_LLINK)) ? "LLINK" : ""); LLDPAD_DBG("\n"); /* copy the local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); /* State already in Listen so don't have to * change. */ feat_prot->Error = false; // maintain TOO_MANY_NGHBRS & FEAT_ERR_MULTI_TLV errors Err = feat_prot->Error_Flag; feat_prot->Error_Flag = FEAT_ERR_NONE; if (Err & FEAT_ERR_MULTI_PEER) { feat_prot->Error_Flag |= FEAT_ERR_MULTI_PEER; } if (Err & FEAT_ERR_MULTI_TLV) { feat_prot->Error_Flag |= (Err & FEAT_ERR_MULTI_TLV); } feat_prot->OperMode = feat_prot->Enable; set_configuration(device_name, EventFlag); goto OperChange; } /* On first call from add_port() ensure that the HW * configuration is synced with the DCBX operational state. */ if (just_added) { CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); set_configuration(device_name, EventFlag); } /* Process remote change. */ mask = DCB_SET_ALL_FLAGS(REMOTE); if (EventFlag & mask) { LLDPAD_DBG(" Remote change: "); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_PG) ? "PG" : ""); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_PFC) ? "PFC" : ""); if (EventFlag & DCB_REMOTE_CHANGE_APPTLV(Subtype)) LLDPAD_DBG(" App%d", Subtype); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_LLINK) ? "LLINK" : ""); LLDPAD_DBG("\n"); /* This version check is part of the Control * protocol state machine that must be * complete before proceeding with feature * protocol state machine */ if (!((ctrl_prot->second->Oper_version == MIN(peer_ctrl_prot->second->Max_version, ctrl_prot->second->Max_version)) && (ctrl_prot->second->Oper_version == peer_ctrl_prot->second->Oper_version) )) { goto ErrBadVersion; } if (feat_prot->dcbx_st == dcbx_subtype2) { /* Handle Peer expiration */ if (peer_ctrl_prot->second->RxDCBTLVState == DCB_PEER_EXPIRED) { LLDPAD_DBG(" F6.2 - Peer DCBX TLV Expired\n"); CopyConfigToOper(device_name, LOCAL_STORE,EventFlag,Subtype); feat_prot->OperMode = false; feat_prot->Syncd = false; feat_prot->Error = true; feat_prot->FeatureSeqNo = 1; feat_prot->Error_Flag |= FEAT_ERR_NO_TLV; set_configuration(device_name, EventFlag); goto OperChange; } } /* Handle feature TLV not present */ if (!peer_feat_prot->TLVPresent) { LLDPAD_DBG(" F8 - Feature not present\n"); /* copy the local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->OperMode = false; feat_prot->Syncd = true; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); feat_prot->Oper_version = feat_prot->Max_version; if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->Error = true; } else { feat_prot->Error = false; } feat_prot->Error_Flag |= FEAT_ERR_NO_TLV; set_configuration(device_name, EventFlag); goto OperChange; } else { feat_prot->Error_Flag &= ~FEAT_ERR_NO_TLV; } if (!feat_prot->Syncd && (peer_ctrl_prot->second->AckNo != feat_prot->FeatureSeqNo)) { /* Wait for the Peer to synch up. */ LLDPAD_DBG(" Wait for Peer to synch: " "Peer AckNo %d, FeatureSeqNo %d \n", peer_ctrl_prot->second->AckNo, feat_prot->FeatureSeqNo); goto OperChange; } if (feat_prot->Error_Flag & FEAT_ERR_MULTI_TLV) { LLDPAD_DBG(" F9.1 - Rcvd Multiple DCBX TLVs\n"); /* Copy Local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->OperMode = false; Err = feat_prot->Error; feat_prot->Error = true; feat_prot->force_send = true; /* Set_configuration to driver. */ if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; if (Err != feat_prot->Error) { ErrorChanged = true; } goto ErrProt; } /* Check for the Oper version */ if (feat_prot->Oper_version != MIN(peer_feat_prot->Max_version, feat_prot->Max_version)) { /* Update Oper version and signal LLDP send. */ feat_prot->Oper_version = MIN(peer_feat_prot->Max_version, feat_prot->Max_version); LLDPAD_DBG(" Update feature oper version to %d " "and signal send\n", feat_prot->Oper_version); feat_prot->Syncd = false; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); feat_prot->FeatureSeqNo = ctrl_prot->second->SeqNo + 1; goto OperChange; } feat_prot->Syncd = true; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); /* F13/F14 */ if (feat_prot->Oper_version != peer_feat_prot->Oper_version ) { /* Wait for Peer to synch up with * version */ LLDPAD_DBG(" Wait for the Peer to synch up "); LLDPAD_DBG("with feature version.\n"); goto OperChange; } feat_prot->PeerWilling = peer_feat_prot->Willing; /* F15 If feature is disabled on any side, * then make Opermode false. */ if (!feat_prot->Enable || !peer_feat_prot->Enable) { LLDPAD_DBG(" F16 - Feature is disabled\n"); /* Copy Local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->OperMode = false; feat_prot->Error_Flag = FEAT_ERR_NONE; Err = feat_prot->Error; /* Set_configuration to driver. */ if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->Syncd = !(feat_prot->Error); feat_prot->Error = false; if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; } else { feat_prot->Error = (set_configuration( device_name, EventFlag) != cmd_success); if (feat_prot->Error) feat_prot->Error_Flag |= FEAT_ERR_CFG; } if (Err != feat_prot->Error) { ErrorChanged = true; } goto ErrProt; } /* F17 */ if (feat_prot->Willing && !feat_prot->PeerWilling) { LLDPAD_DBG(" F18 - local willing, " "peer NOT willing\n"); feat_prot->Error_Flag = FEAT_ERR_NONE; Err = feat_prot->Error; if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->OperMode = !(peer_feat_prot->Error); if (feat_prot->OperMode) { /* Copy Peer cfg to Oper cfg */ CopyConfigToOper(device_name, PEER_STORE, EventFlag, Subtype); } else { /* Copy local cfg to Oper cfg*/ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); } feat_prot->Syncd = !(feat_prot->Error); feat_prot->Error = false; /* Set_configuration to driver. */ if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; } else { feat_prot->OperMode = true; /* Copy Peer config to Oper config. */ CopyConfigToOper(device_name, PEER_STORE, EventFlag,Subtype); /* Set_configuration to driver. */ feat_prot->Error = (set_configuration( device_name, EventFlag) != cmd_success); if (feat_prot->Error) feat_prot->Error_Flag |= FEAT_ERR_CFG; } if (Err != feat_prot->Error) { ErrorChanged = true; } goto ErrProt; } /* F19 */ if (!feat_prot->Willing && feat_prot->PeerWilling) { LLDPAD_DBG(" F20 - local NOT willing, " "peer willing\n"); /* Copy Local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->Error_Flag = FEAT_ERR_NONE; Err = feat_prot->Error; /* Set_configuration to driver. */ if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->OperMode = !peer_feat_prot->Error; feat_prot->Syncd = !(feat_prot->Error); feat_prot->Error = false; if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; } else { feat_prot->OperMode = true; feat_prot->Error = (set_configuration( device_name, EventFlag) != cmd_success); if (feat_prot->Error) feat_prot->Error_Flag |= FEAT_ERR_CFG; } if (Err != feat_prot->Error) ErrorChanged = true; goto ErrProt; } /* F21 */ if ((feat_prot->Willing == feat_prot->PeerWilling) && (LocalPeerCompatible(device_name, EventFlag, Subtype))) { LLDPAD_DBG(" F22 - local willing == peer willing\n"); /* Copy Local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->Error_Flag = FEAT_ERR_NONE; Err = feat_prot->Error; /* Set_configuration to driver. */ if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->OperMode = !peer_feat_prot->Error; feat_prot->Syncd = !(feat_prot->Error); feat_prot->Error = false; if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; } else { feat_prot->OperMode = true; feat_prot->Error = (set_configuration( device_name, EventFlag) != cmd_success); if (feat_prot->Error) feat_prot->Error_Flag |= FEAT_ERR_CFG; } if (Err != feat_prot->Error) ErrorChanged = true; } else { LLDPAD_DBG(" F23 - Local & Peer config not" " compatible\n"); /* Copy Local config to Oper config. */ CopyConfigToOper(device_name, LOCAL_STORE, EventFlag, Subtype); feat_prot->OperMode = false; Err = feat_prot->Error; /* Set default configuration */ if (feat_prot->dcbx_st == dcbx_subtype2) { feat_prot->Syncd = feat_prot->Error; feat_prot->Error = true; if (set_configuration(device_name, EventFlag)) feat_prot->Error_Flag |= FEAT_ERR_CFG; } else { feat_prot->Error = true; if (set_configuration(device_name, EventFlag) != cmd_success) feat_prot->Error_Flag |= FEAT_ERR_CFG; } feat_prot->Error_Flag |= FEAT_ERR_MISMATCH; if (Err != feat_prot->Error) ErrorChanged = true; } ErrProt: if (peer_feat_prot->Error) feat_prot->Error_Flag |= FEAT_ERR_PEER; if (feat_prot->dcbx_st == dcbx_subtype1) { if (feat_prot->Error || peer_feat_prot->Error){ LLDPAD_DBG(" ## FEATURE ERROR: " "%d, %d (Error_Flag 0x%x" " EventFlag 0x%x)\n", feat_prot->Error, peer_feat_prot->Error, feat_prot->Error_Flag, EventFlag); if (feat_prot->OperMode) { feat_prot->OperMode = false; /* Set default configuration */ set_configuration(device_name, EventFlag); } } } if (ErrorChanged) { LLDPAD_DBG(" ErrorChanged \n"); if (feat_prot->dcbx_st == dcbx_subtype1) { feat_prot->Syncd = false; LLDPAD_DBG(" Set Syncd to %u [%u]\n", feat_prot->Syncd, __LINE__); } feat_prot->FeatureSeqNo = ctrl_prot->second->SeqNo+ 1; } } OperChange: if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_PG) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_PG)) { pg_it Oper = pg_find(&oper_pg, device_name); if (Oper == NULL) goto ErrNoDevice; if (memcmp(&(old_pg_opcfg.tx), &(Oper->second->tx), sizeof(old_pg_opcfg.tx)) != 0) pg_events = pg_events | EVENT_OPERATTR; if (feat_prot->OperMode != old_pg_opmode) { pg_events = pg_events | EVENT_OPERMODE; if (feat_prot->OperMode) { LLDPAD_INFO("%s PG oper mode true", device_name); } else { LLDPAD_INFO("%s PG oper mode false", device_name); } } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_PFC)|| DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_PFC)) { pfc_it Oper = pfc_find(&oper_pfc, device_name); if (Oper == NULL) goto ErrNoDevice; if (memcmp(&(old_pfc_opcfg.admin), &(Oper->second->admin), sizeof(old_pfc_opcfg.admin)) != 0) pfc_events = pfc_events | EVENT_OPERATTR; if (feat_prot->OperMode != old_pfc_opmode) { pfc_events = pfc_events | EVENT_OPERMODE; if (feat_prot->OperMode) { LLDPAD_INFO("%s PFC oper mode true", device_name); } else { LLDPAD_INFO("%s PFC oper mode false", device_name); } } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_APPTLV(Subtype)) || DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_APPTLV(Subtype))) { app_it Oper = apptlv_find(&oper_apptlv, device_name, Subtype); if (Oper == NULL) goto ErrNoDevice; if ((old_app_opcfg.Length != Oper->second->Length) || (old_app_opcfg.Length && (memcmp(old_app_opcfg.AppData, Oper->second->AppData, old_app_opcfg.Length) != 0))) { app_events = app_events | EVENT_OPERATTR; } if (feat_prot->OperMode != old_app_opmode) { app_events = app_events | EVENT_OPERMODE; if (feat_prot->OperMode) { LLDPAD_INFO("%s APP oper mode true", device_name); } else { LLDPAD_INFO("%s APP oper mode false", device_name); } } } if (DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_LOCAL_CHANGE_LLINK)|| DCB_TEST_FLAGS(EventFlag, DCB_EVENT_FLAGS, DCB_REMOTE_CHANGE_LLINK)) { llink_it Oper = llink_find(&oper_llink, device_name, Subtype); if (Oper == NULL) goto ErrNoDevice; if (memcmp(&(old_llink_opcfg.llink), &(Oper->second->llink), sizeof(old_llink_opcfg.llink)) != 0) { llink_events = llink_events | EVENT_OPERATTR; LLDPAD_DBG("llink opcfg changed \n"); } if (feat_prot->OperMode != old_llink_opmode) { llink_events = llink_events | EVENT_OPERMODE; if (feat_prot->OperMode) { LLDPAD_DBG("llink opmode = true\n"); } else { LLDPAD_DBG("llink opmode = false\n"); } } } } if (pg_events) pg_event(device_name, pg_events); if (pfc_events) pfc_event(device_name, pfc_events); if (app_events) app_event(device_name, Subtype, app_events); if (llink_events) llink_event(device_name, Subtype, llink_events); return cmd_success; ErrNoDevice: return cmd_device_not_found; ErrBadVersion: LLDPAD_DBG(" Versions not compatible\n"); return cmd_ctrl_vers_not_compatible; } cmd_status GetDCBTLVState(char *device_name, u8 *State) { /* Get the remote control protocol variables. */ control_prot_it peer_ctrl_prot = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (peer_ctrl_prot == NULL) return cmd_device_not_found; *State = (u8)peer_ctrl_prot->second->RxDCBTLVState; return cmd_success; } bool FeaturesSynched(char *device_name) { int i = 0; pg_it it = pg_find(&pg, device_name); if (it == NULL) return false; if (it->second->protocol.State == DCB_LISTEN) { if ((it->second->protocol.Syncd == false) || (it->second->protocol.tlv_sent == false)) return false; } /* Get the local PFC feature protocol */ pfc_it it1 = pfc_find(&pfc, device_name); if (it1 == NULL) return false; if (it1->second->protocol.State == DCB_LISTEN) { if (it1->second->protocol.Syncd == false || it1->second->protocol.tlv_sent == false) return false; } /* Get the APP TLV feature protocol. */ for (i = 0; i < DCB_MAX_APPTLV ; i++) { app_it it2 = apptlv_find(&apptlv, device_name, i); if (it2 == NULL) return false; if (it2->second->protocol.State == DCB_LISTEN) { if (it2->second->protocol.Syncd == false || it2->second->protocol.tlv_sent ==false) return false; } } for (i = 0; i < DCB_MAX_LLKTLV ; i++) { /* Get the local LLINK feature protocol */ llink_it it4 = llink_find(&llink, device_name, i); if (it4 == NULL) return false; if (it4->second->protocol.State == DCB_LISTEN) { if (it4->second->protocol.Syncd == false || it4->second->protocol.tlv_sent == false) return false; } } return true; } /* Set the Syncd value to true for features which are not advertising. */ void update_feature_syncd(char *device_name) { int i = 0; /* Get the local PG feature protocol */ pg_it it = pg_find(&pg, device_name); if (it != NULL) { if (it->second->protocol.Advertise == false) it->second->protocol.Syncd = true; if (it->second->protocol.force_send == true) it->second->protocol.Syncd = true; } /* Get the local PFC feature protocol */ pfc_it it1 = pfc_find(&pfc, device_name); if (it1 != NULL) { if (it1->second->protocol.Advertise == false) it1->second->protocol.Syncd = true; if (it1->second->protocol.force_send == true) it1->second->protocol.Syncd = true; } /* Get the APP TLV feature protocol. */ for (i = 0; i < DCB_MAX_APPTLV ; i++) { app_it it2 = apptlv_find(&apptlv, device_name, i); if (it2 != NULL) { if (it2->second->protocol.Advertise == false) it2->second->protocol.Syncd = true; if (it2->second->protocol.force_send == true) it2->second->protocol.Syncd = true; } } for (i = 0; i < DCB_MAX_LLKTLV ; i++) { /* Get the local LLINK feature protocol */ llink_it it4 = llink_find(&llink, device_name, i); if (it4 != NULL) { if (it4->second->protocol.Advertise == false) it4->second->protocol.Syncd = true; if (it4->second->protocol.force_send == true) it4->second->protocol.Syncd = true; } } } /****************************************************************************** ** ** Method: run_control_protocol ** ** Description: This function runs control state machine for a local or ** remote change. ** The function caller should acquire lock before calling this function. ** Caller must call this function for local or remote change but not both. ** ** Arguments: char *device_name ** u32 EventFlag ** Returns: cmd_success if successful, failure code otherwise. ** *******************************************************************************/ cmd_status run_control_protocol(char *device_name, u32 EventFlag) { pg_attribs pg_dstore; int i, mask; if (!dcbx_check_active(device_name)) return cmd_success; /* Get the local control protocol variables. */ control_prot_it ctrl_prot = ctrl_prot_find(&dcb_control_prot, device_name); if (ctrl_prot == NULL) return cmd_device_not_found; /* Get the remote control protocol variables. */ control_prot_it peer_ctrl_prot = ctrl_prot_find(&dcb_peer_control_prot, device_name); if (peer_ctrl_prot == NULL) return cmd_device_not_found; if (ctrl_prot->second->State == DCB_INIT) { /* Set the parameters. */ ctrl_prot->second->Oper_version = ctrl_prot->second->Max_version; ctrl_prot->second->State = DCB_LISTEN; } if (ctrl_prot->second->State == DCB_LISTEN) { LLDPAD_DBG("DCB Ctrl in LISTEN \n"); /* Process local change if any. */ mask = DCB_SET_ALL_FLAGS(LOCAL); if (EventFlag & mask) { LLDPAD_DBG(" Local change detected: "); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_PG) ? "PG" : ""); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_PFC) ? "PFC" : ""); for (i = 0; i < DCB_MAX_APPTLV; i++) if (EventFlag & DCB_LOCAL_CHANGE_APPTLV(i)) LLDPAD_DBG(" APP%d", i); LLDPAD_DBG(" %s", (EventFlag & DCB_LOCAL_CHANGE_LLINK) ? "LLINK" : ""); LLDPAD_DBG("\n"); if (ctrl_prot->second->SeqNo == ctrl_prot->second->MyAckNo) { LLDPAD_DBG(" Local SeqNo == Local AckNo\n"); if (!FeaturesSynched(device_name)) { update_feature_syncd(device_name); ctrl_prot->second->SeqNo++; LLDPAD_DBG(" *** Sending packet -- "); LLDPAD_DBG("SeqNo = %d \t AckNo = %d \n", ctrl_prot->second->SeqNo, ctrl_prot->second->AckNo); /* Send new DCB ctrl & feature TLVs */ somethingChangedLocal(device_name, NEAREST_BRIDGE); } } return cmd_success; } /* Process remote change if any. */ mask = DCB_SET_ALL_FLAGS(REMOTE); if (EventFlag & mask) { bool SendDCBTLV = false; LLDPAD_DBG(" Remote change detected(0x%x): ", EventFlag); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_PG) ? "PG" : ""); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_PFC) ? "PFC" : ""); for (i = 0; i < DCB_MAX_APPTLV; i++) if (EventFlag & DCB_REMOTE_CHANGE_APPTLV(i)) LLDPAD_DBG(" APP%d", i); LLDPAD_DBG(" %s", (EventFlag & DCB_REMOTE_CHANGE_LLINK) ? "LLINK" : ""); LLDPAD_DBG("\n"); u8 State; if (GetDCBTLVState(device_name, &State) == cmd_success) { if (State == DCB_PEER_EXPIRED) { ctrl_prot->second->SeqNo = 0; ctrl_prot->second->AckNo = 0; ctrl_prot->second->MyAckNo = 0; ctrl_prot->second->Oper_version = ctrl_prot->second->Max_version; peer_ctrl_prot->second->RxDCBTLVState = DCB_PEER_RESET; LLDPAD_DBG(" Ctrl_prot Peer expired\n"); if (get_pg(device_name, &pg_dstore) != cmd_success) { LLDPAD_DBG("unable to get local pg" " cfg from data store\n"); return cmd_device_not_found; } if (pg_dstore.protocol.dcbx_st == dcbx_subtype2) { return cmd_success; } else { /* Send the updated DCB TLV */ SendDCBTLV = true; goto send; } } } if (peer_ctrl_prot->second->Error_Flag & DUP_DCBX_TLV_CTRL) { LLDPAD_DBG("** HANDLE: DUP CTRL TLVs \n"); goto send; } if (ctrl_prot->second->Oper_version != MIN(peer_ctrl_prot->second->Max_version, ctrl_prot->second->Max_version)) { ctrl_prot->second->Oper_version = MIN(peer_ctrl_prot->second->Max_version, ctrl_prot->second->Max_version); /* Send the updated DCB TLV */ SendDCBTLV = true; LLDPAD_DBG(" Change Oper Version \n"); goto send; } if (ctrl_prot->second->Oper_version != peer_ctrl_prot->second->Oper_version) { /* Wait for peer to synch up. */ LLDPAD_DBG(" Wait for Peer to synch \n"); goto send; } /* Update MyAck */ ctrl_prot->second->MyAckNo = peer_ctrl_prot->second->AckNo; /* If received new Peer TLV, then acknowledge the * Peer TLV * MyAckNo == 0 means peer has started over, so * also acknowledge in this case. */ if ((ctrl_prot->second->AckNo != peer_ctrl_prot->second->SeqNo) || (ctrl_prot->second->MyAckNo == 0)) { if (!(peer_ctrl_prot->second->Error_Flag & TOO_MANY_NGHBRS)) { ctrl_prot->second->AckNo = peer_ctrl_prot->second->SeqNo; SendDCBTLV = true; } } /* If changes in feature then send message with latest * DCB and FeatureTLV */ send: LLDPAD_DBG(" Current -- SeqNo = %d \t MyAckNo = %d \n", ctrl_prot->second->SeqNo, ctrl_prot->second->MyAckNo); if ((ctrl_prot->second->SeqNo == ctrl_prot->second->MyAckNo) && (!FeaturesSynched(device_name))) { LLDPAD_DBG(" Features not synced \n"); update_feature_syncd(device_name); /* Send LLDP message. */ ctrl_prot->second->SeqNo++; LLDPAD_DBG(" *** Sending Packet -- "); LLDPAD_DBG("SeqNo = %d \t AckNo = %d \n", ctrl_prot->second->SeqNo, ctrl_prot->second->AckNo); /* Send new DCB control & feature TLVs*/ somethingChangedLocal(device_name, NEAREST_BRIDGE); return cmd_success; } if (SendDCBTLV) { LLDPAD_DBG(" SendDCBTLV is set \n"); /* if you didn't send LLDP message above then * send one without changing feature TLVs. */ LLDPAD_DBG(" *** Sending Packet -- "); LLDPAD_DBG("SeqNo = %d \t AckNo = %d \n", ctrl_prot->second->SeqNo, ctrl_prot->second->AckNo); /* Send new DCB TLVs with old feature TLVs. */ somethingChangedLocal(device_name, NEAREST_BRIDGE); } } } return cmd_success; } /****************************************************************************** ** ** Method: run_dcb_protocol ** ** Description: This function runs both feature and control state machines ** for the features that are specified in the event flag. The function ** caller should acquire lock per port before calling this function. ** Caller can only club together local PG*PFC*APPTLV or ** remote PG*PFC*APPTLV eventflags and call this function. ** ** Arguments: char *device_name ** u32 EventFlag ** u32 SubType - This is valid for APPTLV event flags only. ** If >= DCB_MAX_APPTLV, then we process all Subtypes ** for APPTLV flags. ** Returns: cmd_success if successful, failure code otherwise. ** *******************************************************************************/ cmd_status run_dcb_protocol(char *device_name, u32 EventFlag, u32 Subtype) { cmd_status result = cmd_success; bool LocalChange = false; u32 i, SubTypeMin, SubTypeMax; struct dcbx_tlvs *tlvs; int mask; LLDPAD_DBG("running DCB protocol for %s, flags:%04x\n", device_name, EventFlag); if (!dcbx_check_active(device_name)) return result; /* if valid use SubType param, otherwise process all SubTypes */ if (Subtype < DCB_MAX_APPTLV) { SubTypeMin = Subtype; SubTypeMax = Subtype+1; } else { SubTypeMin = 0; SubTypeMax = DCB_MAX_APPTLV; } /* Run the feature state machines: * * Order is important PFC must be run before PG features to * allow up2tc remappings to account for PFC attributes. */ if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PFC, DCB_LOCAL_CHANGE_PFC) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_LOCAL_CHANGE_PFC, SUBTYPE_DEFAULT); LocalChange = true; } if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_PG, DCB_LOCAL_CHANGE_PG) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_LOCAL_CHANGE_PG, SUBTYPE_DEFAULT); LocalChange = true; } mask = 0; for (i = 0; i < DCB_MAX_APPTLV; i++) mask |= DCB_LOCAL_CHANGE_APPTLV(i); if ((EventFlag & mask) && (result != cmd_ctrl_vers_not_compatible)) { for (i = SubTypeMin; i < SubTypeMax; i++) { result = run_feature_protocol(device_name, DCB_LOCAL_CHANGE_APPTLV(i), i); } LocalChange = true; } if (DCB_TEST_FLAGS(EventFlag, DCB_LOCAL_CHANGE_LLINK, DCB_LOCAL_CHANGE_LLINK) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_LOCAL_CHANGE_LLINK, i); LocalChange = true; } /* Only allow local or remote change at a time: * * Order is important PFC must be run before PG features to * allow up2tc remappings to account for PFC attributes. */ if (!LocalChange) { if (DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC, DCB_REMOTE_CHANGE_PFC) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_REMOTE_CHANGE_PFC, SUBTYPE_DEFAULT); } if (DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG, DCB_REMOTE_CHANGE_PG) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_REMOTE_CHANGE_PG, SUBTYPE_DEFAULT); } mask = 0; for (i = 0; i < DCB_MAX_APPTLV; i++) mask |= DCB_REMOTE_CHANGE_APPTLV(i); if ((EventFlag & mask) && (result != cmd_ctrl_vers_not_compatible)) { for (i = SubTypeMin; i < SubTypeMax; i++) { result = run_feature_protocol(device_name, DCB_REMOTE_CHANGE_APPTLV(i), i); } } if (DCB_TEST_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK, DCB_REMOTE_CHANGE_LLINK) && (result != cmd_ctrl_vers_not_compatible)) { result = run_feature_protocol(device_name, DCB_REMOTE_CHANGE_LLINK, SUBTYPE_DEFAULT); } } /* apply all feature setting to the driver: linux only */ tlvs = dcbx_data(device_name); if (tlvs && tlvs->operup) { LLDPAD_DBG("%s: %s: Managed DCB device coming online, program HW\n", __func__, device_name); set_hw_all(device_name); } /* Run the control state machine. */ result = run_control_protocol(device_name, EventFlag); return result; } lldpad-0.9.46/dcb_rule_chk.c000066400000000000000000000376171215142747300156460ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include "dcb_protocol.h" #include "dcb_rule_chk.h" #include "messages.h" #include "dcb_types.h" /** * dcb_fixup_pg - resolves mismatch in number of traffic classes * @fixpg: pg attributes or resolve * * Resolve mismatch in number of traffic classes by * grouping traffic types. Requires at minimum at * least as many classes as traffic types (e.g. Best * Effort, PFC, and link strict). * * Strategy: This takes two passes over the attribs on * the first pass the pg attribs are packed into a * matrix with row index equal to pgid (pgid_up_list). * This allows identifying user priorities that map * to the same traffic class and traffic types. * * For example * * pgid: 0 0 3 1 2 0 4 4 * pfcup: 0 0 1 1 1 0 1 1 * * Maps to an pgid_up_list as follows, * * ------------------------------------ * up0|up1|up2|up3|up4|up5|up6|up7| * ------------------------------------ * pg0| x | x | | | | x | | | * ------------------------------------ * pg1| | | | x | | | | | * ------------------------------------ * pg2| | | | | x | | | | * ------------------------------------ * pg3| | | x | | | | | | * ------------------------------------ * pg4| | | | | | | x | x | * ------------------------------------ * pg5| | | | | | | | | * ------------------------------------ * pg6| | | | | | | | | * ------------------------------------ * pg7| | | | | | | | | * ------------------------------------ * * Then on the second pass the rows are collapsed onto * the correct number of pgid values (result). Finally, * the new pgid and bandwidth percents can be tabulated. * * Above example collapses pg4 onto pg1 as follows. * * ------------------------------------ * up0|up1|up2|up3|up4|up5|up6|up7| * ------------------------------------ * pg0| x | x | | | | x | | | * ------------------------------------ * pg1| | | | x | | | x | x | * ------------------------------------ * pg2| | | | | x | | | | * ------------------------------------ * pg3| | | x | | | | | | * ------------------------------------ * * This _should_ happen infrequently so we use up * arrays and variables freely. Any simpler suggestions * would be welcome. */ static int dcb_fixup_pg(struct pg_attribs *fixpg, struct pfc_attribs *fixpfc) { dcb_user_priority_attribs_type * pgid_up_list[8][8] = { {0} }; dcb_user_priority_attribs_type * result[8][8] = { {0} }; dcb_user_priority_attribs_type *entry; int i, j, pgid; int be, pfc, strict, cbe, cpfc, cstrict; int tcbw[8] = {0}; bool pg_done[8] = { 0 }; int totalbw = 0; LLDPAD_INFO("%s : fixup\n", __func__); /* Calculate number of pgids used by attributes */ for (pgid = 0, i = 0; i < MAX_USER_PRIORITIES; i++) { if (fixpg->tx.up[i].pgid > pgid) pgid = fixpg->tx.up[i].pgid; } pgid++; /* If the PGIDs can be mapped onto the number of existing traffic * classes do it mapping pgs 1:1 with traffic classes via bwgid. */ if (pgid <= fixpg->num_tcs) { for (i = 0; i < MAX_USER_PRIORITIES; i++) { fixpg->tx.up[i].bwgid = i; fixpg->rx.up[i].bwgid = i; } return 0; } /* Build matrix with rows per pgid and columns per user priorities. * Also count type of UP classes for PFC, best effort, and link * strict. */ for (i = 0; i < MAX_USER_PRIORITIES; i++) { pgid = fixpg->tx.up[i].pgid; pgid_up_list[pgid][i] = &fixpg->tx.up[i]; } strict = pfc = be = 0; for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { for (j = 0; j < MAX_USER_PRIORITIES; j++) { entry = pgid_up_list[i][j]; if (!entry) continue; if (entry->strict_priority == dcb_link) strict++; else if (fixpfc && fixpfc->admin[j] == pfc_enabled) pfc++; else be++; break; } } /* Require at least as many traffic classes as traffic types */ if (fixpg->num_tcs < (!!be + !!strict + !!pfc)) return -1; /* Map traffic class counts onto devices max number traffic classes */ if (strict > fixpg->num_tcs - !!be - !!pfc) strict = fixpg->num_tcs - !!be - !!pfc; if (pfc > fixpg->num_tcs - strict - !!be) pfc = fixpg->num_tcs - strict - !!be; if (be > fixpg->num_tcs - strict - pfc) be = fixpg->num_tcs - strict - pfc; cbe = be; cstrict = strict; cpfc = pfc; totalbw = be = strict = pfc = 0; LLDPAD_INFO("%s -- tc types pfc %i be %i strict %i\n", __func__, cpfc, cbe, cstrict); /* Do REMAPPING into result matrix */ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { pgid = -1; for (j = 0; j < MAX_USER_PRIORITIES; j++) { entry = pgid_up_list[i][j]; if (!entry) continue; if (pgid < 0) { if (entry->strict_priority == dcb_link) { pgid = cbe + cpfc + strict; strict++; } else if (fixpfc && fixpfc->admin[j] == pfc_enabled) { pgid = cbe + pfc; pfc++; } else { pgid = be; be++; } if (pfc == cpfc) pfc = 0; if (strict == cstrict) strict = 0; if (be == cbe) be = 0; } if (pg_done[i] == false) { tcbw[pgid] += fixpg->tx.pg_percent[i]; totalbw += fixpg->tx.pg_percent[i]; pg_done[i] = true; } /* Do row move from old pgid to new pgid */ LLDPAD_INFO("%s: matrix: (%i,%i) -> (%i,%i)\n", __func__, i, j, pgid, j); result[pgid][j] = entry; } } /* The peer _may_ give some percentage of pgpct to a user * priority that has no pgid mapped to it. This seems like * a poor config by the peer. However add the bandwidth to * the highest priority traffic class (that is not strict). */ if (totalbw != 100) { pgid = fixpg->num_tcs - cstrict - 1; tcbw[pgid] += (100 - totalbw); } /* Traverse result matrix and push values onto entries */ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { /* First Pass: Calculate TC BW and set pgid */ for (j = 0; j < MAX_USER_PRIORITIES; j++) { entry = result[i][j]; if (!entry) continue; LLDPAD_INFO("%s: result: up(%i): map %i->%i\n", __func__, j, entry->pgid, i); fixpg->rx.up[j].pgid = i; entry->pgid = i; } } /* Second Pass: Map BWGs 1:1 with priority groups */ for (i = 0; i < MAX_USER_PRIORITIES; i++) { fixpg->tx.up[i].bwgid = i; if (fixpg->tx.up[i].strict_priority == dcb_link) { fixpg->tx.up[i].percent_of_pg_cap = 0; fixpg->rx.up[i].percent_of_pg_cap = 0; } else { fixpg->tx.up[i].percent_of_pg_cap = 100; fixpg->rx.up[i].percent_of_pg_cap = 100; } } /* Final Pass: Distribute TC bandwidth */ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { fixpg->tx.pg_percent[i] = tcbw[i]; fixpg->rx.pg_percent[i] = tcbw[i]; } return 0; } /****************************************************************************** * This function checks DCB rules for DCBs settings. * The following rules are checked: * 1. The sum of bandwidth percentages of all Bandwidth Groups must total 100%. * 2. The sum of bandwidth percentages of all Traffic Classes within a Bandwidth * Group must total 100. * 3. A Traffic Class should not be set to both Link Strict Priority * and Group Strict Priority. ***** assumed to be already checked * 4. Link strict Bandwidth Groups can only have link strict traffic classes * with zero bandwidth. * dcb_config - Struct containing DCB settings. * return : cmd_status *****************************************************************************/ cmd_status dcb_check_config (full_dcb_attrib_ptrs *attribs) { pg_attribs *pg; u8 i, tx_bw, rx_bw, tx_bw_id, rx_bw_id; u8 tx_bw_sum[MAX_BW_GROUP],rx_bw_sum[MAX_BW_GROUP]; bool tx_link_strict[MAX_BW_GROUP], rx_link_strict[MAX_BW_GROUP]; u8 link_strict_pgid; if (attribs == NULL) return cmd_failed; if (attribs->pg) { int err; pg = attribs->pg; memset(tx_bw_sum,0,sizeof(tx_bw_sum)); memset(rx_bw_sum,0,sizeof(rx_bw_sum)); memset(tx_link_strict,0,sizeof(tx_link_strict)); memset(rx_link_strict,0,sizeof(rx_link_strict)); tx_bw = 0, rx_bw = 0; err = dcb_fixup_pg(pg, attribs->pfc); if (err) { LLDPAD_DBG("dcb_fixup_pg returned error %i\n", err); return cmd_failed; } /* Internally in the pg_attribs structure, a link strict PGID is * maintained as a PGID value (0-7) with a corresponding * strict_priority field value of 'dcb_link'. Only one link strict * PGID is allowed. */ link_strict_pgid = LINK_STRICT_PGID; /* Check rules for Tx and Rx Bandwidth Groups */ for (i = 0; i < MAX_BW_GROUP; i++) { tx_bw = tx_bw + pg->tx.pg_percent[i]; /* check for >1 link strict PGID */ if (pg->tx.up[i].strict_priority == dcb_link) { if (link_strict_pgid == LINK_STRICT_PGID) { link_strict_pgid = pg->tx.up[i].pgid; } else if (pg->tx.up[i].pgid != link_strict_pgid) { LLDPAD_INFO("Too many LSP pgid %d\n", (int)pg->tx.up[i].pgid); return cmd_bad_params; } } } /* don't include link strict group %'s */ if (link_strict_pgid < MAX_BW_GROUP) tx_bw = tx_bw - pg->tx.pg_percent[link_strict_pgid]; if (tx_bw != BW_PERCENT) { /* only valid scenario for BWT!=100 is BWT==0 and all BWGs * link strict */ for (i = 0; i < MAX_BW_GROUP; i++) { if ((tx_bw != 0) || (pg->tx.up[i].strict_priority != dcb_link)) { LLDPAD_INFO("Invalid tx total BWG %d\n", (int)tx_bw); return cmd_bad_params; } } } link_strict_pgid = LINK_STRICT_PGID; for (i = 0; i < MAX_BW_GROUP; i++) { rx_bw = rx_bw + pg->rx.pg_percent[i]; /* check for >1 link strict PGID */ if (pg->rx.up[i].strict_priority == dcb_link) { if (link_strict_pgid == LINK_STRICT_PGID) { link_strict_pgid = pg->rx.up[i].pgid; } else if (pg->rx.up[i].pgid != link_strict_pgid) { LLDPAD_INFO("Too many lsp pgids %d\n", (int)pg->rx.up[i].pgid); return cmd_bad_params; } } } /* don't include link strict group %'s */ if (link_strict_pgid < MAX_BW_GROUP) rx_bw = rx_bw - pg->rx.pg_percent[link_strict_pgid]; if (rx_bw != BW_PERCENT) { /* only valid scenario for BWT!=100 is BWT==0 and all BWGs * link strict */ for (i = 0; i < MAX_BW_GROUP; i++) { if ((rx_bw != 0) || (pg->rx.up[i].strict_priority != dcb_link)) { LLDPAD_INFO("Invalid RX total BWG %d\n", (int)rx_bw); return cmd_bad_params; } } } /* Go through each traffic class and check rules for Tx and Rx */ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) { /* Since we assign strict priority to RX & TX via enumeration, * from the data stores and from the peer. It would be * impossible for both of them to be set. * So that is no longer checked in this function. */ /* Transmit Check */ tx_bw = 0, tx_bw_id = 0; tx_bw = (u8)(pg->tx.up[i].percent_of_pg_cap); tx_bw_id = (u8)(pg->tx.up[i].bwgid); if (tx_bw_id >= MAX_BW_GROUP) { LLDPAD_INFO("Invalid TX BWG idx %d\n", (int)tx_bw_id); return cmd_bad_params; } if (pg->tx.up[i].strict_priority == dcb_link) { tx_link_strict[tx_bw_id] = true; /* Link strict should have zero bandwidth */ if (tx_bw){ LLDPAD_INFO("Non-zero LSP BW %d %d\n", i, (int)tx_bw); return cmd_bad_params; } } else if (!tx_bw) { LLDPAD_INFO("Zero BW on non LSP tc %i", i); /* Non link strict should have non zero bandwidth*/ return cmd_bad_params; } /* Receive Check */ rx_bw = 0, rx_bw_id = 0; rx_bw = (u8)(pg->rx.up[i].percent_of_pg_cap); rx_bw_id = (u8)(pg->rx.up[i].bwgid); if (rx_bw_id >= MAX_BW_GROUP) { LLDPAD_INFO("Invalid RX BW %i", rx_bw_id); return cmd_bad_params; } if (pg->rx.up[i].strict_priority == dcb_link) { rx_link_strict[rx_bw_id] = true; /* Link strict class should have zero bandwidth */ if (rx_bw){ LLDPAD_INFO("Non-zero LSP BW %d %d\n", i, (int)rx_bw); return cmd_bad_params; } } else if (!rx_bw) { LLDPAD_INFO("Zero BW on no LSP tc %i", i); /* Non link strict class should have non-zero bw */ return cmd_bad_params; /* DCB_RX_ERR_TC_BW_ZERO; */ } tx_bw_sum[tx_bw_id] = tx_bw_sum[tx_bw_id] + tx_bw; rx_bw_sum[rx_bw_id] = rx_bw_sum[rx_bw_id] + rx_bw; } /* Transmit Check */ for (i = 0; i < MAX_BW_GROUP; i++) { /* sum of bandwidth percentages of all traffic classes within * a Bandwidth Group must total 100 except for link strict * group (zero bandwidth). */ if (tx_link_strict[i]) { if (tx_bw_sum[i] && pg->tx.pg_percent[i]) { LLDPAD_INFO("Non-zero LSP BW %d %d\n", i, (int)tx_bw_sum[i]); /* Link strict group should have zero bw */ return cmd_bad_params; } } else if (tx_bw_sum[i] != BW_PERCENT && tx_bw_sum[i] != 0) { LLDPAD_INFO("Invalid BW sum on BWG %i %i", i, (int)tx_bw_sum[i]); return cmd_bad_params; } } /* Receive Check */ for (i = 0; i < MAX_BW_GROUP; i++) { /* sum of bandwidth percentages of all traffic classes * within a Bandwidth Group must total 100 except for * link strict group ( zero bandwidth). */ if (rx_link_strict[i]) { if (rx_bw_sum[i] && pg->rx.pg_percent[i]) { LLDPAD_INFO("Non-zero BW on LSP tc " "%u %u\n", i, rx_bw_sum[i]); /* Link strict group should have zero bw */ return cmd_bad_params; } } else if (rx_bw_sum[i] != BW_PERCENT && rx_bw_sum[i] != 0) { LLDPAD_INFO("Invalid BW sum on BWG %i %i", i, (int)rx_bw_sum[i]); return cmd_bad_params; } } } return cmd_success; } /****************************************************************************** * This function checks updates the user priority bandwidth percentages * of the supplied PG attribute. * The user priorities for a given PGID will be assigned an equal share of * the PGID's bandwidth and group strict will be turned off. *****************************************************************************/ /* index is (number of priorities in PGID - 1) * value indicates the number of priorities which need to have the bw * incremented by 1 so the total will add to 100. * ex: 100/3 == 33 33+33+34 = 100 bw_fixup[2] = 1 */ static u8 bw_fixup[MAX_USER_PRIORITIES] = { 0, 0, 1, 0, 0, 4, 2, 4 }; void rebalance_uppcts(pg_attribs *pg) { u8 uplist[MAX_USER_PRIORITIES]; int bwgid; int num_found; bool link_strict; int adjust; int value; int i; for (bwgid = 0; bwgid < MAX_BW_GROUP; bwgid++) { num_found = 0; link_strict = false; memset(uplist, 0xff, sizeof(uplist)); for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (pg->tx.up[i].bwgid == bwgid) { uplist[num_found++] = (u8)i; if (pg->tx.up[i].strict_priority == dcb_link) { link_strict = true; pg->tx.up[i].percent_of_pg_cap = 0; pg->rx.up[i].percent_of_pg_cap = 0; } } } if (num_found && !link_strict) { adjust = bw_fixup[num_found-1]; for (i = 0; i < num_found; i++) { value = BW_PERCENT / num_found; if (adjust) { value++; adjust--; } pg->tx.up[uplist[i]].percent_of_pg_cap = (u8)value; pg->rx.up[uplist[i]].percent_of_pg_cap = (u8)value; pg->tx.up[uplist[i]].strict_priority = dcb_none; pg->rx.up[uplist[i]].strict_priority = dcb_none; } } } } lldpad-0.9.46/dcbtool.c000066400000000000000000000411611215142747300146550ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include "clif.h" #include "lldp_dcbx_cmds.h" #include "clif_msgs.h" #include "lldpad.h" #include "dcbtool.h" #include "version.h" #define UNUSED __attribute__((__unused__)) static int show_raw; static const char *cli_version = "dcbtool v" DCBTOOL_VERSION "\n" "Copyright (c) 2007-2010, Intel Corporation\n" "\nSubstantially modified from: hostapd_cli v 0.5.7\n" "Copyright (c) 2004-2007, Jouni Malinen and contributors"; static const char *cli_license = "This program is free software. You can distribute it and/or modify it\n" "under the terms of the GNU General Public License version 2.\n" "\n"; /* "Alternatively, this software may be distributed under the terms of the\n" "BSD license. See README and COPYING for more details.\n"; */ static const char *cli_full_license = "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License version 2 as\n" "published by the Free Software Foundation.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" "\n" "Alternatively, this software may be distributed under the terms of the\n" "BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" "met:\n" "\n" "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" "2. Redistributions in binary form must reproduce the above copyright\n" " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n" "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" " names of its contributors may be used to endorse or promote products\n" " derived from this software without specific prior written permission.\n" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; static const char *commands_help = "DCBX Commands:\n" " dcbx get configured or operational DCBX versions\n" " the configured version takes effect on next\n" " restart of the lldpad service\n" " sc dcbx v:cee set the DCBX version to be used after next\n" " v:cin lldpad restart. Version can be set to:\n" " v:force-cin cin: fall back to CIN DCBX if non-IEEE peer\n" " v:force-cee cee: fall back to CEE DCBX if non-IEEE peer\n" " force-cin, force-cee: do not start with IEEE DCBX\n" "Per Port Commands:\n" " gc get configuration of on port \n" " go get operational status of \n" " on port \n" " gp get peer configuration of \n" " on port \n" " sc general form of set feature configuration\n\n" " 'feature' can be:\n" " dcb DCB state of port\n" " pg priority groups\n" " pfc priority flow control\n" " app: application specific data\n" " ll: logical link status\n\n" " 'subtype' can be:\n" " [0|fcoe] FCoE\n\n" " [1|iscsi] iSCSI\n\n" " [2|fip] FIP\n\n" " 'args' can include:\n" " [e:<0|1>] controls feature enable\n" " [a:<0|1>] controls feature advertise via DCBX\n" " [w:<0|1>] controls feature DCBX willing mode\n" " [feature specific args] arguments specific to a feature\n\n" " feature specific arguments for 'dcb':\n" " [on|off] the 'dcb' configuration does not use the\n" " enable, advertise or willing mode parameters\n" " 'dcb' also does not use the 'go' or 'gp'\n" " commands.\n\n" " feature specific arguments for 'pg':\n" " [pgid:xxxxxxxx] priority group ID of user priority.\n" " From left to right (priorities 0-7), x is\n" " the corresponding priority group value\n" " 0-7 (for priority groups with bandwidth)\n" " or 'f' (the no bandwidth allocation group)\n" " [pgpct:x,x,x,x,x,x,x,x] priority group percent of link.\n" " From left to right (priority groups 0-7),\n" " x is the percentage of link bandwidth\n" " assigned. The total must equal 100%.\n" " [uppct:x,x,x,x,x,x,x,x] user priority percent of bandwidth group.\n" " From left to right (priorities 0-7),\n" " x is the percentage of priority group\n" " bandwidth assigned to the priority.\n" " The sum of percentages for priorities in the\n" " same priority group must be 100.\n" " [strict:xxxxxxxx] strict priority setting.\n" " From left to right (priorities 0-7),\n" " x is 0 or 1. 1 indicates that the priority\n" " may utilize all of the bandwidth allocated\n" " to its priority group.\n" " [up2tc:xxxxxxxx] user priority to traffic class mapping.\n" " From left to right (priorities 0-7), x is\n" " a the corresponding traffic class (0-7)\n" " (this argument is currently ignored).\n\n" " feature specific arguments for 'pfc':\n" " [pfcup:xxxxxxxx] enable/disable priority flow control.\n" " From left to right (priorities 0-7),\n" " x is 0 or 1. 1 indicates that the priority\n" " is configured to transmit priority pause.\n\n" " feature specific arguments for 'app:':\n" " [appcfg:xx] 'xx' is a hexadecimal value representing an\n" " 8 bit bitmap where 1 bits indicate the\n" " priority which frames for the application\n" " specified by the subtype should use.\n" " The lowest order bit maps to priority 0.\n\n" " feature specific arguments for 'll:':\n" " [status:<0|1>] for testing, the logical link status may\n" " be set to 0 or 1. This setting is not\n" " persisted.\n\n" " help show command information\n" " license show license information\n"; static struct clif *clif_conn; static int cli_quit = 0; static int cli_attached = 0; static void usage(void) { fprintf(stderr, "%s\n", cli_version); fprintf(stderr, "\n" "Usage:\n" " dcbtool -h\n" " dcbtool -v\n" " dcbtool [-rR] [ command ]\n" " dcbtool interactive mode\n" "\n" "Options:\n" " -h help (show this usage text)\n" " -v shown version information\n" " -r show raw messages\n" " -R show only raw messages\n" "%s", commands_help); } void print_raw_message(char *msg, int print) { if (!print || !(print & SHOW_RAW)) return; if (!(print & SHOW_RAW_ONLY)) { switch (msg[MSG_TYPE]) { case EVENT_MSG: printf("event: "); break; case CMD_RESPONSE: printf("rsp: "); break; default: printf("cmd: "); break; } } printf("%s\n", msg); } int parse_print_message(char *msg, int print) { int status = 0; status = parse_response(msg); print_raw_message(msg, print); if (print & SHOW_RAW_ONLY) return status; if (msg[MSG_TYPE] == CMD_RESPONSE) print_response(msg, status); else if (msg[MSG_TYPE] == EVENT_MSG) print_event_msg(msg); return status; } static void cli_close_connection(void) { if (clif_conn == NULL) return; if (cli_attached) { clif_detach(clif_conn); cli_attached = 0; } clif_close(clif_conn); clif_conn = NULL; } static void cli_msg_cb(char *msg, UNUSED size_t len) { parse_print_message(msg, SHOW_OUTPUT | show_raw); } /* structure of the print argument bitmap: * SHOW_NO_OUTPUT (0x0) - don't print anything for the command * SHOW_OUTPUT (0x01) - print output for the command * SHOW_RAW (0x02) - print the raw clif command messages * SHOW_RAW_ONLY (0x04) - print only the raw clif command messages */ static int _clif_command(struct clif *clif, char *cmd, int print) { char buf[MAX_CLIF_MSGBUF]; size_t len; int ret; print_raw_message(cmd, print); if (clif_conn == NULL) { printf("Not connected to lldpad - command dropped.\n"); return -1; } len = sizeof(buf) - 1; ret = clif_request(clif, cmd, strlen(cmd), buf, &len, cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); return -2; } else if (ret < 0) { printf("'%s' command failed.\n", cmd); return -1; } if (print) { buf[len] = '\0'; ret = parse_print_message(buf, print); } return ret; } inline int clif_command(struct clif *clif, char *cmd, int raw) { return _clif_command(clif, cmd, SHOW_OUTPUT | raw); } static int cli_cmd_ping(struct clif *clif, UNUSED int argc, UNUSED char *argv[], int raw) { return clif_command(clif, "P", raw); } static int cli_cmd_help(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED int raw) { printf("%s", commands_help); return 0; } static int cli_cmd_license(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED int raw) { printf("%s\n\n%s\n", cli_version, cli_full_license); return 0; } static int cli_cmd_quit(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED int raw) { cli_quit = 1; return 0; } struct cli_cmd { const char *cmd; int (*handler)(struct clif *clif, int argc, char *argv[], int raw); }; static struct cli_cmd cli_commands[] = { { "ping", cli_cmd_ping }, { "help", cli_cmd_help }, { "license", cli_cmd_license }, { "quit", cli_cmd_quit }, { NULL, NULL } }; static int request(struct clif *clif, int argc, char *argv[], int raw) { struct cli_cmd *cmd, *match = NULL; int count; int ret = 0; count = 0; cmd = cli_commands; while (cmd->cmd) { if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { match = cmd; count++; } cmd++; } if (count > 1) { printf("Ambiguous command '%s'; possible commands:", argv[0]); cmd = cli_commands; while (cmd->cmd) { if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { printf(" %s", cmd->cmd); } cmd++; } printf("\n"); ret = -1; } else if (count == 0) { ret = handle_dcb_cmds(clif, argc, &argv[0], raw); } else { ret = match->handler(clif, argc - 1, &argv[1], raw); } return ret; } static void cli_recv_pending(struct clif *clif, int in_read) { int first = 1; if (clif_conn == NULL) return; while (clif_pending(clif)) { char buf[256]; size_t len = sizeof(buf) - 1; if (clif_recv(clif, buf, &len) == 0) { buf[len] = '\0'; if (in_read && first) printf("\n"); first = 0; cli_msg_cb(buf, len); } else { printf("Could not read pending message.\n"); break; } } } static void cli_interactive(int raw) { const int max_args = 10; char *cmd, *argv[max_args], *pos; int argc; setlinebuf(stdout); printf("\nInteractive mode\n\n"); using_history(); stifle_history(1000); do { cli_recv_pending(clif_conn, 0); alarm(1); cmd = readline("> "); alarm(0); if (!cmd) break; if (*cmd) add_history(cmd); argc = 0; pos = cmd; for (;;) { while (*pos == ' ') pos++; if (*pos == '\0') break; argv[argc] = pos; argc++; if (argc == max_args) break; while (*pos != '\0' && *pos != ' ') pos++; if (*pos == ' ') *pos++ = '\0'; } if (argc) request(clif_conn, argc, argv, raw); free(cmd); } while (!cli_quit); } static void cli_terminate(UNUSED int sig) { cli_close_connection(); exit(0); } static void cli_alarm(UNUSED int sig) { if (clif_conn && _clif_command(clif_conn, "P", SHOW_NO_OUTPUT)) { printf("Connection to lldpad lost - trying to reconnect\n"); cli_close_connection(); } if (!clif_conn) { clif_conn = clif_open(); if (clif_conn) { printf("Connection to lldpad re-established\n"); if (clif_attach(clif_conn, NULL) == 0) cli_attached = 1; else printf( "Warning: Failed to attach to lldpad.\n"); } } if (clif_conn) cli_recv_pending(clif_conn, 1); alarm(1); } int main(int argc, char *argv[]) { int interactive; int raw = 0; int warning_displayed = 0; int c; int ret = 0; for (;;) { c = getopt(argc, argv, "hvrR"); if (c < 0) break; switch (c) { case 'h': usage(); return 0; case 'r': if (raw) { usage(); return -1; } raw = SHOW_RAW; break; case 'R': if (raw) { usage(); return -1; } raw = (SHOW_RAW | SHOW_RAW_ONLY); break; case 'v': printf("%s\n", cli_version); return 0; default: usage(); return -1; } } show_raw = raw; interactive = argc == optind; if (interactive) { printf("%s\n\n%s\n\n", cli_version, cli_license); } for (;;) { clif_conn = clif_open(); if (clif_conn) { if (warning_displayed) printf("Connection established.\n"); break; } if (!interactive) { perror("Failed to connect to lldpad - clif_open"); return -1; } if (!warning_displayed) { printf("Could not connect to lldpad - re-trying\n"); warning_displayed = 1; } sleep(1); continue; } signal(SIGINT, cli_terminate); signal(SIGTERM, cli_terminate); signal(SIGALRM, cli_alarm); if (interactive) { if (clif_attach(clif_conn, NULL) == 0) cli_attached = 1; else printf("Warning: Failed to attach to lldpad.\n"); cli_interactive(raw); } else ret = request(clif_conn, argc - optind, &argv[optind], raw); cli_close_connection(); return ret; } lldpad-0.9.46/dcbtool_cmds.c000066400000000000000000000430211215142747300156600ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include "clif.h" #include "dcbtool.h" #include "lldp_dcbx_cmds.h" #include "lldpad_status.h" #include "dcb_types.h" #include "parse_cli.h" #include "messages.h" #include "lldp_util.h" static char *print_status(cmd_status status); static char *get_pgdesc_args(int cmd); static int hex2int(char *b); static void free_cmd_args(char *args); static char *get_dcb_args(void); static char *get_dcbx_args(void); static char *get_pg_args(void); static char *get_pfc_args(void); static char *get_app_args(void); static char *get_ll_args(void); static char *get_cmd_args(void); static void print_errors(int errors); static const char *hexlist = "0123456789ABCDEF"; static char *print_status(cmd_status status) { char *str; switch(status) { case cmd_success: str = "Successful"; break; case cmd_failed: str = "Failed"; break; case cmd_device_not_found: str = "Device not found, link down or DCB not enabled"; break; case cmd_invalid: str = "Invalid command"; break; case cmd_bad_params: str = "Invalid parameters"; break; case cmd_peer_not_present: str = "Peer feature not present"; break; case cmd_ctrl_vers_not_compatible: str = "Version not compatible"; break; case cmd_not_capable: str = "Device not capable"; break; case cmd_not_applicable: str = "Command not applicable in IEEE-DCBX modes"; break; case cmd_no_access: str = "Access denied"; break; default: str = "Unknown status"; break; } return str; } /* assumes input is pointer to two hex digits */ /* returns -1 on error */ static int hex2int(char *b) { int i; int n=0; int m; for (i=0,m=1; i<2; i++,m--) { if (isxdigit(*(b+i))) { if (*(b+i) <= '9') n |= (*(b+i) & 0x0f) << (4*m); else n |= ((*(b+i) & 0x0f) + 9) << (4*m); } else { return -1; } } return n; } static char *get_dcb_args(void) { char buf[8]; int j; j = 0; if (get_fargs()) buf[j++] = hexlist[get_dcb_param() & 0x0f]; buf[j]=0; return strdup(buf); } static char *get_dcbx_args(void) { char buf[8]; int j; j = 0; if (get_fargs()) buf[j++] = hexlist[get_dcbx_param() & 0x0f]; buf[j]=0; return strdup(buf); } static char *get_pgdesc_args(int cmd) { char buf[MAX_DESCRIPTION_LEN + 8]; char *p; int j; j = 0; sprintf(buf+j, "%01x", get_desc_id()); j++; if (cmd == CMD_SET_CONFIG) { p = get_desc_str(); if (p != NULL && strlen(p) < MAX_DESCRIPTION_LEN - 1) { sprintf(buf+j, "%02x", (unsigned int) strlen(p)); j+=2; memcpy(buf+j, p, strlen(p)); j += strlen(p); } } buf[j] = 0; return strdup(buf); } static char *get_pg_args(void) { char buf[256]; int *aptr; int i; int j; j = 0; buf[j++] = hexlist[get_enable() & 0x0f]; buf[j++] = hexlist[get_advertise() & 0x0f]; buf[j++] = hexlist[get_willing() & 0x0f]; if (get_fargs()) { aptr = get_up2tc(); for (i=0; i= 0) buf[j++] = hexlist[aptr[i] & 0x0f]; else buf[j++] = CLIF_NOT_SUPPLIED; aptr = get_pgpct(); for (i=0; i= 0) { buf[j++] = hexlist[(aptr[i] & 0xf0)>>4]; buf[j++] = hexlist[aptr[i] & 0x0f]; } else { buf[j++] = CLIF_NOT_SUPPLIED; buf[j++] = CLIF_NOT_SUPPLIED; } } aptr = get_pgid(); for (i=0; i= 0) buf[j++] = hexlist[aptr[i] & 0x0f]; else buf[j++] = CLIF_NOT_SUPPLIED; aptr = get_uppct(); for (i=0; i= 0) { buf[j++] = hexlist[(aptr[i] & 0xf0)>>4]; buf[j++] = hexlist[aptr[i] & 0x0f]; } else { buf[j++] = CLIF_NOT_SUPPLIED; buf[j++] = CLIF_NOT_SUPPLIED; } } aptr = get_strict(); for (i=0; i= 0) buf[j++] = hexlist[aptr[i] & 0x0f]; else buf[j++] = CLIF_NOT_SUPPLIED; /* num tc field - not used by set yet */ buf[j++] = CLIF_NOT_SUPPLIED; } buf[j]=0; return strdup(buf); } static char *get_pfc_args(void) { char buf[256]; int *aptr; int i; int j; j = 0; buf[j++] = hexlist[get_enable() & 0x0f]; buf[j++] = hexlist[get_advertise() & 0x0f]; buf[j++] = hexlist[get_willing() & 0x0f]; if (get_fargs()) { aptr = get_pfcup(); for (i=0; i= 0) buf[j++] = hexlist[aptr[i] & 0x0f]; else buf[j++] = CLIF_NOT_SUPPLIED; /* num tc field - not used by set yet */ buf[j++] = CLIF_NOT_SUPPLIED; } buf[j] = 0; return strdup(buf); } static char *get_app_args(void) { char buf[512]; char *p; int j; j = 0; buf[j++] = hexlist[get_enable() & 0x0f]; buf[j++] = hexlist[get_advertise() & 0x0f]; buf[j++] = hexlist[get_willing() & 0x0f]; p = get_appcfg(); if (p != NULL && strlen(p) < sizeof(buf)-5) { sprintf(buf+j, "%02x", (unsigned int)strlen(p)); j+=2; memcpy(buf+j, p, strlen(p)); j += strlen(p); } buf[j] = 0; return strdup(buf); } static char *get_ll_args(void) { char buf[8]; int j; int n; j = 0; buf[j++] = hexlist[get_enable() & 0x0f]; buf[j++] = hexlist[get_advertise() & 0x0f]; buf[j++] = hexlist[get_willing() & 0x0f]; n = get_llstatus(); if (n < 0) buf[j++] = CLIF_NOT_SUPPLIED; else buf[j++] = hexlist[n]; buf[j++] = 0; return strdup(buf); } static char *get_cmd_args(void) { char *args = NULL; switch (get_cmd()) { case CMD_GET_CONFIG: case CMD_GET_OPER: case CMD_GET_PEER: switch (get_feature()) { case FEATURE_PG_DESC: args = get_pgdesc_args(CMD_GET_CONFIG); break; default: /* query commands without arguments */ args = strdup(""); break; } break; case CMD_SET_CONFIG: switch (get_feature()) { case FEATURE_DCB: args = get_dcb_args(); break; case FEATURE_DCBX: args = get_dcbx_args(); break; case FEATURE_PG: args = get_pg_args(); break; case FEATURE_PFC: args = get_pfc_args(); break; case FEATURE_APP: args = get_app_args(); break; case FEATURE_LLINK: args = get_ll_args(); break; case FEATURE_PG_DESC: args = get_pgdesc_args(CMD_SET_CONFIG); break; default: args = strdup(""); printf("warning: unknown feature\n"); break; } break; default: args = strdup(""); printf("warning: unknown cmd\n"); } return args; } static void free_cmd_args(char *args) { if (args) free(args); } int handle_dcb_cmds(struct clif *clif, int argc, char *argv[], int raw) { char cbuf[MAX_CLIF_MSGBUF]; char buf[MAX_CLIF_MSGBUF]; char *cmd_args; int i; memset(buf, 0, sizeof(buf)); for (i = 0; i < argc; i++) snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), "%s ", argv[i]); init_parse_state(); if (parse_dcb_cmd(buf)) { printf("invalid command argument: %s\n", get_parse_error()); free_parse_error(); return -1; } cmd_args = get_cmd_args(); if (get_feature() == FEATURE_DCBX) snprintf(cbuf, sizeof(cbuf), "%c%01x%02x%02x%s", DCB_CMD, CLIF_MSG_VERSION, get_cmd(), get_feature(), cmd_args); else snprintf(cbuf, sizeof(cbuf), "%c%01x%02x%02x%02x%02x%s%s", DCB_CMD, CLIF_MSG_VERSION, get_cmd(), get_feature(), get_subtype(), get_port_len(), get_port(), cmd_args); free_cmd_args(cmd_args); return clif_command(clif, cbuf, raw); } void print_errors(int errors) { int flag = 0; printf("0x%02x - ", errors); if (!errors) { printf("none\n"); return; } if (errors & 0x01) { flag++; printf("mismatch with peer"); } if (errors & 0x02) { if (flag++) printf(", "); printf("local configuration error"); } if (errors & 0x04) { if (flag++) printf(", "); printf("multiple TLV's received"); } if (errors & 0x08) { if (flag++) printf(", "); printf("peer error"); } if (errors & 0x10) { if (flag++) printf(", "); printf("multiple LLDP neighbors"); } if (errors & 0x20) { if (flag++) printf(", "); printf("peer feature not present"); } printf("\n"); } void print_dcb_cmd_response(char *buf, int status) { int version; int dcb_cmd; int feature; int dcbx_st = dcbx_subtype1; int subtype = 0; int plen = 0; int doff; int i; int n; version = buf[DCB_VER_OFF] & 0x0f; dcb_cmd = hex2int(buf+DCB_CMD_OFF); feature = hex2int(buf+DCB_FEATURE_OFF); if (feature == FEATURE_DCBX) { doff = DCBX_CFG_OFF; } else { subtype = hex2int(buf+DCB_SUBTYPE_OFF); plen = hex2int(buf+DCB_PORTLEN_OFF); doff = DCB_PORT_OFF + plen; } if (version != CLIF_MSG_VERSION) { printf("Unsupported client interface message version: %d\n", version); return; } printf("Command: \t"); switch(dcb_cmd) { case CMD_GET_CONFIG: printf("Get Config\n"); break; case CMD_SET_CONFIG: printf("Set Config\n"); break; case CMD_GET_OPER: printf("Get Oper\n"); break; case CMD_GET_PEER: printf("Get Peer\n"); break; default: printf("Unknown DCB command: %d:%s\n", dcb_cmd, buf); return; } printf("Feature: \t"); switch (feature) { case FEATURE_DCB: printf("DCB State\n"); break; case FEATURE_DCBX: printf("DCBX Version\n"); break; case FEATURE_PG: printf("Priority Groups\n"); break; case FEATURE_PFC: printf("Priority Flow Control\n"); break; case FEATURE_APP: printf("Application "); switch (subtype) { case APP_FCOE_STYPE: printf("FCoE\n"); break; case APP_ISCSI_STYPE: printf("iSCSI\n"); break; case APP_FIP_STYPE: printf("FIP\n"); break; default: printf("unknown\n"); break; } break; case FEATURE_LLINK: printf("Logical Link "); switch (subtype) { case LLINK_FCOE_STYPE: printf("FCoE\n"); break; default: printf("unknown\n"); break; } break; case FEATURE_PG_DESC: printf("BWG Desc\n"); break; default: printf("unknown DCB feature: %s\n", buf); return; } if (feature != FEATURE_DCBX) printf("Port: \t%*.*s\n", plen, plen, buf+DCB_PORT_OFF); printf("Status: \t%s\n", print_status(status)); /* print out data */ if (dcb_cmd == CMD_SET_CONFIG) /* set command - we're done */ return; if (status != cmd_success) /* set command - we're done */ return; switch(dcb_cmd) { case CMD_GET_CONFIG: switch(feature) { case FEATURE_PG: case FEATURE_PFC: case FEATURE_APP: case FEATURE_LLINK: printf("Enable: \t%s\n", (*(buf+doff+CFG_ENABLE) == '1')? ("true"):("false")); printf("Advertise: \t%s\n", (*(buf+doff+CFG_ADVERTISE) == '1')? ("true"):("false")); printf("Willing: \t%s\n", (*(buf+doff+CFG_WILLING) == '1')? ("true"):("false")); doff += CFG_LEN; break; default: break; } break; case CMD_GET_OPER: if (feature == FEATURE_DCBX) break; printf("Oper Version:\t%d\n", hex2int(buf+doff+OPER_OPER_VER)); printf("Max Version:\t%d\n", hex2int(buf+doff+OPER_MAX_VER)); printf("Errors: \t"); print_errors(hex2int(buf+doff+OPER_ERROR)); printf("Oper Mode: \t%s\n", (*(buf+doff+OPER_OPER_MODE) == '1')? ("true"):("false")); printf("Syncd: \t%s\n", (*(buf+doff+OPER_SYNCD) == '1')? ("true"):("false")); doff += OPER_LEN; break; case CMD_GET_PEER: printf("Enable: \t%s\n", (*(buf+doff+PEER_ENABLE) == '1')? ("true"):("false")); printf("Willing: \t%s\n", (*(buf+doff+PEER_WILLING) == '1')? ("true"):("false")); printf("Oper Version:\t%d\n", hex2int(buf+doff+PEER_OPER_VER)); printf("Max Version:\t%d\n", hex2int(buf+doff+PEER_MAX_VER)); printf("Error: \t%s\n", (*(buf+doff+PEER_ERROR) == '1')? ("true"):("false")); dcbx_st = (*(buf+doff+PEER_SUBTYPE)) & 0x0f; printf("DCBX Subtype:\t%d\n", dcbx_st); doff += PEER_LEN; break; default: printf("Unknown DCB command: %d:%s\n", dcb_cmd, buf); return; } switch (feature) { case FEATURE_DCB: printf("DCB State:\t%s\n", (*(buf+doff+DCB_STATE) == '1')?("on"):("off")); break; case FEATURE_DCBX: printf("DCBX Version:\t"); switch (*(buf+doff+DCBX_VERSION) ^ '0') { case dcbx_subtype1: printf("CIN\n"); break; case dcbx_subtype2: printf("CEE\n"); break; case dcbx_force_subtype1: printf("FORCED CIN\n"); break; case dcbx_force_subtype2: printf("FORCED CEE\n"); break; default: printf("unknown version\n"); break; } break; case FEATURE_PG_DESC: printf("PGID: \t%d\n", *(buf+doff+PG_DESC_PGID) & 0x0f); printf("Description:\t%*s\n", hex2int(buf+doff+PG_DESC_LEN), buf+doff+PG_DESC_DATA); break; case FEATURE_PG: if (dcb_cmd != CMD_GET_PEER) { printf("up2tc: \t"); for (i=0; i" " dcbx" Get the configured or operational legacy version of the DCBX protocol which will be supported by .B lldpad. The configured version, if different from the operational version, will take effect after .B lldpad is restarted. .TP .BR "sc dcbx v:" "[" "cin" "|" "cee" "|" "force-cin" "|" "force-cee" "]" Set the legacy version of DCBX which will be supported by .B lldpad the next time it is started. .br Information about the CIN version can be found at: .br .br Information about the CEE version can be found at: .br .br The dcbx setting is a global setting and changes only take effect when .B lldpad is restarted. The default DCBX version used is the IEEE standard version. If a pre-IEEE DCBX version is received (per port) which matches the dcbx setting, then .B lldpad will fall back to the configured global dcbx setting. If the dcbx setting is set to either 'force-cin' or 'force-cee' then any port doing DCBX will start out in the corresponding legacy DCBX mode. .PP DCB per-interface commands: .TP \fBgc \fR<\fIifname\fR> <\fIfeature\fR> get configuration of \fIfeature\fR on interface \fIifname\fR. .TP \fBgo \fR<\fIifname\fR> <\fIfeature\fR> get operational status of \fIfeature\fR on interface \fIifname\fR. .TP \fBgp \fR<\fIifname\fR> <\fIfeature\fR> get peer configuration of \fIfeature\fR on interface \fIifname\fR. .TP \fBsc \fR<\fIifname\fR> <\fIfeature\fR> <\fIargs\fR> set the configuration of \fIfeature\fR on interface \fIifname\fR. .PP .I feature may be one of the following: .TP .B dcb DCB state of the port .TP .B pg priority groups .TP .B pfc priority flow control .TP \fBapp:\fR<\fIsubtype\fR> application specific data .TP \fBll:\fR<\fIsubtype\fR> logical link status .PP .I subtype can be: .TP .BR "0" "|" "fcoe" Fiber Channel over Ethernet (FCoE) .TP .BR "1" "|" "iscsi" Internet Small Computer System Interface (iSCSI) .TP .BR "2" "|" "fip" FCoE Initialization Protocol (FIP) .PP .I args can include: .TP .BR "e:" "<" "0" "|" "1" ">" controls feature enable .TP .BR "a:" "<" "0" "|" "1" ">" controls whether the feature is advertised via DCBX to the peer .TP .BR "w:" "<" "0" "|" "1" ">" controls whether the feature is willing to change its operational configuration based on what is received from the peer .TP .RI "[" "feature specific args" "]" arguments specific to a DCB feature .PP Feature specific arguments for .BR "dcb" ":" .TP .BR "on" "|" "off" enable or disable DCB for the interface. The .B go and .B gp commands are not needed for the .B dcb feature. Also, the .RI "enable(" "e" "), advertise(" "a" ") and willing(" "w" ")" arguments are not required. .PP Feature specific arguments for .BR "pg" ":" .TP .BI "pgid:" "xxxxxxxx" Priority group ID for the 8 priorities. From left to right (priorities 0-7), .I x is the corresponding priority group ID value, which can be .I 0-7 for priority groups with bandwidth allocations or .I f (priority group ID 15) for the unrestricted priority group. .TP .BI "pgpct:" "x" "," "x" "," "x" "," "x" "," "x" "," "x" "," "x" "," "x" Priority group percentage of link bandwidth. From left to right (priority groups 0-7), .I x is the percentage of link bandwidth allocated to the corresponding priority group. The total bandwidth must equal 100%. .TP .BI "uppct:" "x" "," "x" "," "x" "," "x" "," "x" "," "x" "," "x" "," "x" Priority percentage of priority group bandwidth. From left to right (priorities 0-7), .I x is the percentage of priority group bandwidth allocated to the corresponding priority. The sum of percentages for priorities which belong to the same priority group must total 100% (except for priority group 15). .TP .BI "strict:" "xxxxxxxx" Strict priority setting. From left to right (priorities 0-7), .I x .RB "is " "0" " or " "1" ". " "1" indicates that the priority may utilize all of the bandwidth allocated to its priority group. .TP .BI "up2tc:" "xxxxxxxx" Priority to traffic class mapping. From left to right (priorities 0-7), .I x is the traffic class (0-7) to which the priority is mapped. .PP Feature specific arguments for .BR "pfc" ":" .TP .BI "pfcup:" "xxxxxxxx" Enable/disable priority flow control. From left to right (priorities 0-7), .I x .RB "is " "0" " or " "1" ". " "1" indicates that the corresponding priority is configured to transmit priority pause. .PP .TP Feature specific arguments for \fBapp:\fR<\fIsubtype\fR>: The app features uses global enable and willing bits for all subtypes. To remove or add subtypes to the TLV set the advertise bit. .TP .BI "appcfg:" "xx" .I xx is a hexadecimal value representing an 8 bit bitmap where 1 bits indicate the priorities which frames for the applications specified by .I subtype should use. The lowest order bit maps to priority 0. .PP Feature specific arguments for \fBll:\fR<\fIsubtype\fR>: .TP \fBstatus:\fR[\fB0\fR|\fB1\fR] For testing purposes, the logical link status may be set to 0 or 1. Changes to the logical link status are not saved in the configuration file. .SH EXAMPLES .PP Enable DCB on interface \fIeth2\fR .PP .B dcbtool sc eth2 dcb on .PP Assign priorities 0-3 to priority group 0, priorities 4-6 to priority group 1 and priority 7 to the unrestricted priority. Also, allocate 25% of link bandwidth to priority group 0 and 75% to group 1. .PP .B dcbtool sc eth2 pg pgid:0000111f pgpct:25,75,0,0,0,0,0,0 .PP Enable transmit of Priority Flow Control for priority 3 and assign FCoE to priority 3. .PP .B dcbtool sc eth2 pfc pfcup:00010000 .br .B dcbtool sc eth2 app:0 appcfg:08 .SH SEE ALSO .BR lldpad (8), .BR lldptool (8), .BR lldptool-dcbx (8), .BR lldptool-ets (8), .BR lldptool-pfc (8), .BR lldptool-app (8) .br .SH COPYRIGHT dcbtool - DCB configuration utility .br Copyright(c) 2007-2012 Intel Corporation. .BR Portions of dcbtool are based on: .IP hostapd-0.5.7 .IP Copyright (c) 2004-2008, Jouni Malinen .SH LICENSE This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. .LP This program is distributed in the hope 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. .LP 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 St - Fifth Floor, Boston, MA 02110-1301 USA. .LP The full GNU General Public License is included in this distribution in the file called "COPYING". .SH SUPPORT Contact Information: open-lldp Mailing List lldpad-0.9.46/docs/lldpad.8000066400000000000000000000144111215142747300153420ustar00rootroot00000000000000.\" LICENSE .\" .\" This software program is released under the terms of a license agreement between you ('Licensee') and Intel. Do not use or load this software or any associated materials (collectively, the 'Software') until you have carefully read the full terms and conditions of the LICENSE located in this software package. By loading or using the Software, you agree to the terms of this Agreement. If you do not agree with the terms of this Agreement, do not install or use the Software. .\" .\" * Other names and brands may be claimed as the property of others. .\" .TH lldpad 8 "March 23, 2012" .SH NAME lldpad \- Link Layer Discovery Protocol (LLDP) agent daemon .SH SYNOPSIS .B lldpad [-h] .B [-v] .B [-V] .B [-d] .B [-k] .B [-p] .B [-s] .BI "[-f" " filename" "]" .SH DESCRIPTION Executes the LLDP protocol for supported network interfaces. The list of TLVs currently supported are: .TP - IEEE 802.1AB Mandatory TLVs .TP - IEEE 802.1AB Basic Management TLVs .TP - IEEE 802.3 Organizationally Specific TLVs .TP - LLDP-MED Organizationally Specific TLVs .TP - Data Center Bridging capabilities exchange protocol (DCBX) TLVs .TP - Edge Virtual Bridging (EVB) TLVs .br .PP Capabilities of .B lldpad include: .TP - Transmission of LLDP PDUs containing enabled TLVs from enabled ports. .TP - Reception of LLDP PDUs from enabled ports. .TP - Operation of the DCBX protocol for interfaces which support the DCB rtnetlink interface. This includes operation of the DCBX state machines above LLDP and corresponding configuration of the DCB parameters of the network interface. Supported DCB features are: Extended Transmission Selection, Priority Flow Control and the FCoE application. .TP - Configuring the DCB settings of the network driver based on the operation of DCBX. .TP - Provides a multi-channel interface for client applications to query and configure features. Events are also generated on the client interface to inform clients of changes. The lldpad package includes two clients: lldptool for general LLDP agent management and dcbtool for DCB management. .PP .B lldpad supports the versions of the DCB capabilities exchange (DCBX) protocol listed as follows: .TP version 1 - also known as CIN DCBX .PP .TP version 2 - also known as CEE DCBX .PP .TP IEEE DCBX See the IEEE 802.1Qaz-2011 specification for details. .PP IEEE DCBX is the default DCBX mode for a DCB capable interface so the default and configured IEEE DCBX TLVs will be transmitted when the interface comes up. .B lldpad can be globally configured to support one of the legacy DCBX versions (CIN or CEE). If the remote LLDP agent does not transmit any IEEE DCBX TLVs and does transmit a legacy DCBX TLV which matches the configured legacy DCBX version, then the DCBX mode will drop back to legacy DCBX mode. It will not transition back to IEEE DCBX mode until the next link reset. If .B lldpad has dropped back to legacy DCBX mode for a given interface and the daemon is stopped and restarted, the legacy DCBX mode for that interface will be used instead of starting out in IEEE DCBX mode. This behavior only applies to the case where .B lldpad is restarted and is not persistent across a system reboot. .PP See .B dcbtool for information on how to globally configure which legacy version of DCBX .B lldpad executes. .PP See .B lldptool for information on how to reset the DCBX mode of an interface back to default (starts out in IEEE DCBX mode). .B lldpad also supports edge virtual bridging as currently under specification in the IEEE 802.1Qb working group. .PP .SH OPTIONS .B lldpad has the following command line options: .TP .B \-h show usage information .TP .BI "-f" " filename" use the specified file as the configuration file instead of the default file: /var/lib/lldpad/lldpad.conf .B lldpad expects the directory of the configuration file to exist, but if the configuration file does not exist, then a default configuration file will be created. lldpad creates and maintains the contents of the configuration file. Configuration should be performed by using lldptool or dcbtool. .TP .B \-d run lldpad as a daemon .TP .B \-v show lldpad version .TP .B \-V set lldpad debugging level. Uses syslog debug levels see syslog.2 for details. .TP .B \-k used to terminate the first instance of lldpad that was started (e.g. from initrd). Once lldpad -k has been invoked and lldpad has been restarted, subsequent invocations of lldpad -k will not terminate lldpad. .TP .B \-s remove lldpad state records from shared memory .TP .B \-p do not create PID file /var/run/lldpad.pid on startup .PP .SH NOTE On termination, lldpad does not undo any of the configurations that it has set. This approach minimizes the risk of restarting the daemon to perform a software update, or of having storage issues during shutdown. Ongoing operation of network interfaces that had been controlled by lldpad may result in unexpected behavior. .SH SEE ALSO .BR dcbtool (8), .BR lldptool (8), .BR lldptool-dcbx (8), .BR lldptool-ets (8), .BR lldptool-pfc (8), .BR lldptool-app (8), .BR lldptool-med (8), .BR lldptool-vdp (8), .BR lldptool-evb (8) .br .SH COPYRIGHT lldpad - LLDP agent daemon with DCBX support .br Copyright(c) 2007-2012 Intel Corporation. .BR Portions of lldpad are based on: .IP hostapd-0.5.7 .IP Copyright (c) 2004-2008, Jouni Malinen .LP .SH LICENSE This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. .LP This program is distributed in the hope 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. .LP 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 St - Fifth Floor, Boston, MA 02110-1301 USA. .LP The full GNU General Public License is included in this distribution in the file called "COPYING". .SH SUPPORT Contact Information: open-lldp Mailing List lldpad-0.9.46/docs/lldptool-app.8000066400000000000000000000035501215142747300165130ustar00rootroot00000000000000.TH lldptool 8 "February 2010" "open-lldp" "Linux" .SH NAME APP \- Show / manipulate APP TLV configuration .SH SYNOPSIS .B lldptool -t -i ethx -V APP [-c app] .sp .B lldptool -T -i ethx -V APP [-d] app=prio,sel,pid .sp .SH DESCRIPTION The APP TLV is an informational TLV and will advertise the current configured application table. If the peer APP TLV and a local APP TLV match the same selector and protocol field the local parameter is used. .SH ARGUMENTS .TP .B app Configures a local application data field using the three parameters: priority (prio), selector (sel), and protocol (pid). The -d option can be provided to remove an entry which matches the three parameters. .SH Theory of Operations The APP TLV is an informational TLV for most end stations. It is not expected that setting APP data should be required. Peer data will be processed and added to the application data table by default assuming there does not already exist a local entry with matching selector and priority fields. By adding APP data to the application table it is available for applications, eg iscsid, to query. Applications query for a specific protocol and selector and may use this data to map traffic to the corresponding traffic class. .SH EXAMPLE & USAGE .TP Configure ISCSI application for priority 4 on \fIeth2\fR .B lldptool -T -i eth2 -V APP app=4,2,3260 .TP Configure FCoE application for priority 3 on \fIeth2\fR .B lldptool -T -i eth2 -V APP app=3,1,35078 .TP Remove FCoE application for priority 3 on \fIeth2\fR .B lldptool -T -i eth2 -V APP -d app=3,1,35078 .TP Display locally configured application table on \fIeth2\fR .B lldptool -t -i eth2 -V APP -c app .TP Display last transmitted APP TLV on \fIeth2\fR .B lldptool -t -i eth2 -V APP .SH SOURCE .TP o IEEE 802.1Qaz (http://www.ieee802.org/1/pages/dcbridges.html) .SH SEE ALSO .BR lldptool (8), .BR lldpad (8) .SH AUTHOR John Fastabend lldpad-0.9.46/docs/lldptool-dcbx.8000066400000000000000000000101301215142747300166430ustar00rootroot00000000000000.TH lldptool-dcbx 8 "August 2012" "open-lldp" "Linux" .SH NAME DCBX \- Show / manipulate DCBX configuration .SH SYNOPSIS .B lldptool -t -i ethx -V IEEE-DCBX -c [mode] .sp .B lldptool -T -i ethx -V IEEE-DCBX mode=reset .sp .B lldptool -t -i ethx -V -c [enableTx] .sp .B lldptool -T -i ethx -V enableTx= .sp .SH DESCRIPTION - DCBX MODE .B lldpad supports the following versions of the DCB capabilities exchange (DCBX) protocol: .TP CIN DCBX .TP CEE DCBX .TP IEEE DCBX See the IEEE 802.1Qaz-2011 specification for details." .PP IEEE DCBX is the default DCBX mode for a DCB capable interface. Therefore the default and configured IEEE DCBX TLVs will be transmitted when the interface comes up. .B lldpad can be globally configured to support one of the legacy DCBX versions (CIN or CEE). If the remote LLDP agent does not transmit any IEEE DCBX TLVs and does transmit a legacy DCBX TLV which matches the configured legacy DCBX version, then the DCBX mode will drop back to legacy DCBX mode. It will not transition back to IEEE DCBX mode until the next link reset. If .B lldpad has dropped back to legacy DCBX mode for a given interface and the daemon is stopped and restarted, the legacy DCBX mode for that interface will be used instead of starting out in IEEE DCBX mode. This behavior only applies to the case where .B lldpad is restarted and is not persistent across a system reboot. .sp The DCBX mode can be queried and configured by using the special tlvid .B IEEE-DCBX. There is not an actual TLV which corresponds to this tlvid. Its use is to query and reset the IEEE DCBX mode. When queried, IEEE DCBX mode can take the following values: .BR auto " - IEEE DCBX will be used (intially) if lldpad is restarted. An exception is if the DCBX mode has been forced to a legacy DCBX mode, then the specified legacy mode will be used. See information about the 'dcbx' parameter in dcbtool(8) for more information about this exception." .sp .BR CEE " - CEE DCBX will be used if lldpad is restarted" .sp .BR CIN " - CIN DCBX will be used if lldpad is restarted" .sp The IEEE DCBX mode can be reset to .B auto by setting the .B mode argument to the value .B reset .SH DESCRIPTION - DCBX CONFIGURATION The detailed configuration of the IEEE DCBX TLVs can be found in related lldptool man pages for each IEEE DCBX TLV (see lldptool-pfc(8), lldptool-ets(8) and lldptool-app(8)). .sp The detailed configuration of the CIN and CEE DCBX TLVs is performed using dcbtool (see dcbtool(8)). However, lldptool can configure the enableTx parameter of the CIN and CEE DCBX TLVs (as it can with most other TLVs). Since lldpad only transmits TLVs for one version of DCBX on any given interface, the enableTx parameter for the CIN and CEE DCBX TLVs (and for the IEEE DCBX feature TLVs) takes effect only when the corresponding DCBX version is active. .SH ARGUMENTS .TP .B mode=reset Reset the DCBX mode that will be used if lldpad is restarted by setting the mode argument to reset using the special tlvid IEEE-DCBX. .TP .B enableTx Enable the specified DCBX TLV (CIN-DCBX or CEE-DCBX) to be transmitted in the LLDPDU if that DCBX mode for the specified interface has been selected. .SH EXAMPLE & USAGE .TP Query the current DCBX mode that will be used if lldpad is restarted. (this is not a persistent setting) .B lldptool -t -i eth3 -V IEEE-DCBX -c mode .TP Reset the DCBX mode to be 'auto' (start in IEEE DCBX mode) after the next lldpad restart .B lldptool -T -i eth3 -V IEEE-DCBX mode=reset .TP Enable transmission of the CEE DCBX TLV .B lldptool -T -i eth3 -V CEE-DCBX enableTx=yes .TP Disable transmission of the CIN DCBX TLV .B lldptool -T -i eth3 -V CIN-DCBX enableTx=no .TP Query the configuration of enableTx for the CEE DCBX TLV .B lldptool -t -i eth3 -V CEE-DCBX -c enableTx .SH NOTES .SH SEE ALSO .BR dcbtool (8), .BR lldptool (8), .BR lldptool-ets (8), .BR lldptool-pfc (8), .BR lldptool-app (8), .BR lldpad (8) .SH AUTHOR Eric Multanen lldpad-0.9.46/docs/lldptool-ets.8000066400000000000000000000104661215142747300165320ustar00rootroot00000000000000.TH lldptool 8 "February 2010" "open-lldp" "Linux" .SH NAME ETS-{CFG|REC} \- Show / manipulate ETS TLV configuration .SH SYNOPSIS .B lldptool -t -i ethx -V ETS-CFG .RI <-c " " CONFIG_ARG " " ...> .sp .ti +4 .IR CONFIG_ARG " := { " .BR enableTx " | " willing " | " tsa " | " up2tc " | " numtcs " | " tcbw " }" .sp .B lldptool -T -i ethx -V ETS-CFG .RI CONFIG_ARG=value " " ... .sp .ti +4 .IR CONFIG_ARG " := .sp .BR enableTx " = " {yes|no} " | " .sp .BR willing " = " {yes|no} " | " .sp .BR tsa " = " tc:{ets|strict|vendor},... " | " .sp .BR up2tc " = " priority:tc,... " | " .sp .BR tcbw " = " bw1,bw2,... .sp .B lldptool -t -i ethx -V ETS-REC .RI <-c " " CONFIG_ARG " " ...> .sp .B lldptool -T -i ethx -V ETS-REC .RI CONFIG_ARG=value " " ... .sp .ti +4 .IR CONFIG_ARG " := .sp .BR enableTx " = " {yes|no} " | " .sp .BR tsa " = " tc:{ets|strict|vendor},... " | " .sp .BR up2tc " = " priority:tc,... " | " .sp .BR tcbw " = " bw1,bw2,... .sp .SH DESCRIPTION The Enhanced Transmission Selection (ETS) feature has a recommendation TLV and a configuration TLV configured with ETS-REC and ETS-CFG respectively. .SH ARGUMENTS .TP .B enableTx Enables the ETS TLV to be transmitted .TP .B willing Sets the ETS-CFG willing bit .TP .B tsa Transmission selection algorithm, sets a comma seperated list \ of traffic classes to the corresponding selection algorithm. Valid algorithms \ include "ets", "strict" and "vendor". .sp .TP .B up2tc Comma seperated list mapping user priorities to traffic classes. .sp .TP .B tcbw Comma separated list of bandwiths for each traffic class the first value being assigned to traffic class 0 and the second to traffic class 1 and so on. Undefined bandwidths are presumed to be 0. .sp .TP .B numtcs Displays the number of ETS supported traffic classes. .SH Theory of Operations IEEE 802.1Qaz is enabled by default on hardware that supports this mode indicated by support for the DCBNL interface. Kernel config option CONFIG_DCB. The ETS-CFG TLV is advertised by default with the attributes indicated by querying the hardware for its current configuration. A valid configuration is to map all priorities to a single traffic class and use the link strict selection algorithm. This is equivalent to being in a non-DCB enabled mode. To support legacy DCBX (CIN or CEE) the ETS-CFG and ETS-REC TLVs are disabled if a legacy DCBX TLV is received and no valid IEEE DCBX TLVs are received. The hardware DCBX mode will also be set to the legacy mode and IEEE mode is disabled. This allows switches to be configured and end nodes will then be configured accordingly without any configuration required on the end node. See \fIlldpad(8)\fR for more information about the operation of the DCBX selection mechanism. Mapping applications and protocols to traffic classes is required for ETS to be useful. User space programs can encode the priority of an application with the SO_PRIORITY option. The net_prio cgroup can be used to assign application traffic to specific priorities. See the kernel documentation and \fIcgdcbxd(8)\fR for net_prio cgroup information. .B .SH EXAMPLE & USAGE .TP Configure willing bit for interface \fIeth2\fR .B lldptool -T -i eth2 -V ETS-CFG willing=yes .TP Configure traffic classes for ETS and strict priority on \fIeth2\fR .B lldptool -T -i eth2 -V ETS-CFG tsa=0:ets,1:ets,2:ets,3:ets,4:strict,5:strict .TP Configure 1:1 mapping from priority to traffic classes on \fIeth2\fR .B lldptool -T -i eth2 -V ETS-CFG up2tc=0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7 .TP Display local configured ETS-CFG parameters for tcbw .B lldptool -t -i eth2 -V ETS-CFG -c tcbw .TP Display last transmitted ETS-CFG TLV .B lldptool -t -i eth2 -V ETS-CFG .TP Configure ETS-CFG and ETS-REC for default DCB on \fIeth2\fR .B lldptool -T -i eth2 -V ETS-CFG tsa=0:ets,1:ets,2:ets,3:ets,4:ets,5:ets,6:ets,7:ets up2tc=0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7 tcbw=12,12,12,12,13,13,13,13 .B lldptool -T -i eth2 -V ETS-REC tsa=0:ets,1:ets,2:ets,3:ets,4:ets,5:ets,6:ets,7:ets up2tc=0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7 tcbw=12,12,12,12,13,13,13,13 .SH SOURCE .TP o IEEE 802.1Qaz (http://www.ieee802.org/1/pages/dcbridges.html) .SH NOTES Support for tc-mqprio was added in 2.6.38 on older kernels other mechanisms may need to be used to map applications to traffic classes. .SH SEE ALSO .BR lldptool (8), .BR lldptool-app (8), .BR lldpad (8), .BR tc-mqprio (8), .SH AUTHOR John Fastabend lldpad-0.9.46/docs/lldptool-evb.8000066400000000000000000000100771215142747300165110ustar00rootroot00000000000000.TH lldptool 8 "February 2010" "open-lldp" "Linux" .SH NAME evb \- Show / manipulate EVB TLV configuration .SH SYNOPSIS .B lldptool -t -g ncb -i ethx -V evbCfg -c enableTx .sp .B lldptool -T -g ncb -i ethx -V evbCfg -c enableTx=[yes|no] .sp .B lldptool -t -g ncb -i ethx -V evbCfg -c fmode .sp .B lldptool -T -g ncb -i ethx -V evbCfg -c fmode=[bridge|reflectiverelay] .sp .B lldptool -t -g ncb -i ethx -V evbCfg -c capabilities .sp .B lldptool -T -g ncb -i ethx -V evbCfg -c capabilities=[rte|ecp|vdp|none] .sp .B lldptool -t -g ncb -i ethx -V evbCfg -c rte .sp .B lldptool -T -g ncb -i ethx -V evbCfg -c rte=[] .sp .B lldptool -t -g ncb -i ethx -V evbCfg -c vsis .sp .B lldptool -T -g ncb -i ethx -V evbCfg -c vsis=[] .sp .SH DESCRIPTION The EVB TLV is a TLV to announce the station and bridge's edge virtual bridging (evb) capabilities and request the bridge forwarding mode. If both sides have agree on edge control protocol (ECP), VSI discovery protocol (VDP) capabilities, both sides can exchange VDP TLV using ECP frames. The vsis parameter will set the maximum number of VSIs and show the number of currently configured VSIs. .SH ARGUMENTS .TP .B enableTx Enables the EVB TLV to be transmitted .TP .B fmode shows or sets the forwarding mode between bridge (default) or reflectiverelay (RR). .TP .B capabilities shows or sets the local capabilities that are announced to the adjacent switch in the TLV. This parameter will accept any combination of rte, vdp or ecp, separated by ",". Use the keyword "none" if you do not want to set any capabilities. .TP .B rte shows or set the local run time exponent (RTE). The RTE will be used as the base for the timing of the ECP and VDP protocols. .TP .B vsis shows or sets the number of virtual station interfaces (VSIs) that are announced to the adjacent switch in the TLV. This parameter expects a number between 0 and 65535. .SH Theory of Operation The EVB TLV is used to announce and exchange supported parameters between the station and an adjacent switch. The TLV uses the nearest customer bridge agent. If "reflectiverelay" is set as forwarding mode, the switch will allow "reflection" of frames coming from different sources at the same port back to the port. This will allow communication between virtual machines on the same host via the switch. The capabilities parameter is used to set RTE, ECP and VDP support. VDP TLVs in ECP frames can only be exchanged if both sides agree on ECP and VDP as capabilities. RTE will be used as the base timing parameter for ECP and VDP. .SH EXAMPLE & USAGE .TP Display locally configured values for \fIeth8\fR .B lldptool -t -g ncb -i eth8 -V evbCfg .TP Display remotely configured values for \fIeth8\fR .B lldptool -n -g ncb -t -i eth8 -V evbCfg .TP Display wether evb tlv is configured for tx on \fIeth8\fR .B lldptool -t -g ncb -i eth8 -V evbCfg -c enableTx .TP Display the currently requested forwarding mode for \fIeth8\fR .B lldptool -t -g ncb -i eth8 -V evbCfg -c fmode .TP Set the forwarding mode to reflective relay .B lldptool -T -g ncb -i eth8 -V evbCfg -c fmode=reflectiverelay .TP Display the currently configured capabilities .B lldptool -t -g ncb -i ethx -V evbCfg -c capabilities .TP Set the locally possible capabilities to RTE, ECP and VDP .B lldptool -T -g ncb -i ethx -V evbCfg -c capabilities=rte,ecp,vdp .TP Resets the locally possible capabilities to "none" .B lldptool -T -g ncb -i ethx -V evbCfg -c capabilities=none .TP Display the locally configured value for RTE .B lldptool -t -g ncb -i ethx -V evbCfg -c rte .TP Set the value for RTE .B lldptool -T -g ncb -i ethx -V evbCfg -c rte=[] .TP Display the configured maximum number of VSIs .B lldptool -t -g ncb -i ethx -V evbCfg -c vsis .TP Set the maximum number of VSIs .B lldptool -T -g ncb -i ethx -V evbCfg -c vsis=[] .sp .SH SOURCE .TP o IEEE 802.1Qbg (http://www.ieee802.org/1/pages/802.1bg.html) .SH NOTES Currently the code in lldpad reflects draft 0 of the upcoming standard. EVB TLVs on the wire can be decoded with wireshark > v1.6. .SH SEE ALSO .BR lldptool-vdp (8), .BR lldptool (8), .BR lldpad (8) .SH AUTHOR Jens Osterkamp lldpad-0.9.46/docs/lldptool-evb22.8000066400000000000000000000166741215142747300166660ustar00rootroot00000000000000.TH lldptool 8 "February 2013" "open-lldp" "Linux" .SH NAME evb22 \- Show / manipulate EVB IEEE 802.1 Ratified Standard TLV configuration .SH SYNOPSIS .B lldptool -t -i ethx -g ncb -V evb .sp .B lldptool -t -i ethx -g ncb -V evb -c evbmode .sp .B lldptool -T -i ethx -g ncb -V evb -c enabletx=[yes|no] .sp .B lldptool -T -i ethx -g ncb -V evb -c evbmode=[bridge|station] .sp .B lldptool -t -i ethx -g ncb -V evb -c evbrrreq .sp .B lldptool -T -i ethx -g ncb -V evb -c evbrrreq=[yes|no] .sp .B lldptool -t -i ethx -g ncb -V evb -c evbrrcap .sp .B lldptool -T -i ethx -g ncb -V evb -c evbrrcap=[yes|no] .sp .B lldptool -t -i ethx -g ncb -V evb -c evbgpid .sp .B lldptool -T -i ethx -g ncb -V evb -c evbgpid=[yes|no] .sp .B lldptool -t -i ethx -g ncb -V evb -c ecpretries .sp .B lldptool -T -i ethx -g ncb -V evb -c ecpretries=[0..7] .sp .B lldptool -t -i ethx -g ncb -V evb -c ecprte .sp .B lldptool -T -i ethx -g ncb -V evb -c ecprte=[0..31] .sp .B lldptool -t -i ethx -g ncb -V evb -c vdprwd .sp .B lldptool -T -i ethx -g ncb -V evb -c vdprwd=[0..31] .sp .B lldptool -t -i ethx -g ncb -V evb -c vdprka .sp .B lldptool -T -i ethx -g ncb -V evb -c vdprka=[0..31] .SH DESCRIPTION The Edge Virtual Bridge (EVB) TLV is a TLV to announce the station and bridge's edge virtual bridging (EVB) capabilities and may request the bridge port to be set into reflective relay (hairpin) mode. If both sides agree on the modes and time out values, the edge control protocol (ECP) will be used to exchange VSI discovery protocol (VDP) data using ECP frames between the host interface and the adjacent switch port facing the host interface. This man pages describes the IEEE 802.1 Qbg ratified standard dated from July 5th, 2012. The arguments and parameters differ from the IEEE 802.1 Qbg draft 0.2, which is also implemented. The EVB protocol version to be used depends on the organizational unique identifier (OUI) of the EVB TLV in the LLDP data stream. A OUI value of 0x001b3f stands for the IEEE 802.1 Qbg draft 0.2, a OUI value of 0x0080c2 stands for the IEEE 802.1 Qbg ratified standard. The version of the ECP and VDP protocols are determined by the ethernet type field in the ethernet header. The ethernet type value for IEEE 802.1 Qbg draft 0.2 is 0x88b7, the value for IEEE 802.1 Qbg ratified standard is 0x8890. Note that the EVB protocol is exchanged between nearest customer bridges only, employing the reserved multicast MAC address 01:80:c2:00:00:00 as destination MAC address. .BR lldpad (8) supports both versions, the switch port configuration determines which version will be used. The switch port configuration should select only one protocol version, never both. .sp 1 The command line options and arugments are explained in the .BR lldptool (8) man pages. Only the EVB, ECP and VDP protocol specific parameters are detailed in this manual page. .SH ARGUMENTS The invocation without command line option '-c' and argument displays the complete EVB, ECP and VDP protocol settings. See below for a detailed description on how to interpret the output. .TP \fB\-c\fP \fItext\fP Use command line option '-c' and one of the following arguments to display and set individual parameters. .I Text can be one of the following values: .TP .B enabletx Enables or disables the EVB TLV to be transmitted. When set to disabled no EVB TLV will be included in the LLDP data stream. Furthermore the output of the complete EVB settings without option '-c' will be empty. .TP .B evbmode Display the current role or sets the role the to given value. Supported values are either "station" or "bridge". .TP .B evbrrreq Shows the current reflective relay (hairpin) request mode or sets the reflective relay (hairpin) request mode. If the value is "yes", the station requests the interface facing switch port to be set in reflective relay (hairpin) mode. This field is only valid for stations, the output of evbmode equals "station". .TP .B evbrrcap Shows the current reflective relay (hairpin) capabilities or sets the reflective relay (hairpin) capabilities. If the value is "yes", the switch port will be set in reflective relay (hairpin) mode. This field is only valid for switches, the output of evbmode equals "bridge". .TP .B gpid Shows the current station or switch support for grouping or turns on/off the station or switch support for grouping. If set to true, the station or switch wants to use group identifiers in VDP protocols. .TP .B ecpretries Shows or sets the maximum number of retries for ECP frames to be retransmitted. A retransmit occurs when no ECP acknowledgement message has been received during a given time period. .TP .B ecprte Shows or sets the local run time exponent (RTE). The RTE will be used as the base for the timing of the ECP protocol time outs and retransmits. The wait time is calcuated as 10*2\u\fIecprte\fP\d micro seconds. .TP .B vdprwd Shows or sets the number of resource wait delay value. This value is calcuated as 10*2\u\fIvdprwd\fP\d micro seconds and determines the maximum wait time for VDP protocol acknowledgements. .TP .B vdprka Shows or sets the number of re-init keep alive value. This value is calcuated as 10*2\u\fIvdprka\fP\d micro seconds and determines the wait time for VDP protocol to send a keep alive message. .SH Theory of Operation The EVB TLV is used to announce and exchange supported parameters between the station and an adjacent switch. If .I reflectiverelay is active, the switch sends back ethernet frames on the very same port it received the frame on. This is an extension to the current bridging standard and allows communication between virtual machines on the same host through the switch port. .SH EXAMPLE & USAGE .TP Display locally configured values for \fIeth0\fR .B lldptool -t -g ncb -i eth0 -V evb .DS .nf EVB Configuration TLV bridge:(00) station:rrreq,rrstat(0x5) retries:7 rte:31 mode:station r/l:0 rwd:31 r/l:0 rka:8 .fi .DE This output is displayed when enabletx has been enabled. The first line shows the currently known status of the bridge. The second line shows the currently known status of the station. The status is displayed verbose appended by the hexadecimal value in parenthesis. The verbose output uses the bit naming convention used in the standard document. The third line displays the values for the ECP protocol number of retransmits (retries) and the retransmit timeout exponent. The forth line shows the current mode of operation, either bridge or station, the resource wait delay value (rwd) and an indication if the local (0) or remote (1) rwd value is used. The fifth line displays the value of the re-init keep alive counter (rka) and an indication if the local (0) or remote (1) rka value is used. .TP Display the currently requested forwarding mode for \fIeth0\fR .B lldptool -t -g ncb -i eth0 -V evb -c evbrrreq .TP Display the locally configured value for RTE .B lldptool -t -g ncb -i eth0 -V evb -c evbrte .TP Set the value for RTE to its maximum value .B lldptool -T -g ncb -i eth0 -V evb -c rte=7 .TP Set the value for enabletx to yes to transmit EVB TLV to the switch. .B lldptool -T -g ncb -i eth0 -V evb -c enabletx=yes .SH NOTES Currently the code in lldpad reflects IEEE 802.1 Qbg draft 0.2 of the upcoming standard. Wireshark support for IEEE 802.1 Qbg ratified standard TLVs is currently missing. Support for the IEEE 802.1 Qbg ratified standard protocols ECP and VDP is currently under development and not fully functional. .SH SEE ALSO .BR lldptool-vdp (8), .BR lldptool (8), .BR lldpad (8) .br IEEE 802.1Qbg (http://www.ieee802.org/1/pages/802.1bg.html) .SH AUTHOR Thomas Richter lldpad-0.9.46/docs/lldptool-med.8000066400000000000000000000045521215142747300165030ustar00rootroot00000000000000.TH lldptool 8 "June 2011" "open-lldp" "Linux" .SH NAME LLDP-MED \- Show / manipulate MED TLV configurations .SH SYNOPSIS .B lldptool -t -i ethx -V .RI "[ " TLV_TYPE " ]" .B enableTx .sp .B lldptool -T -i ethx -V .RI "[ " TLV_TYPE " ]" .B enableTx = { yes | no } .sp .B lldptool -T -i ethx -V LLDP-MED .B devtype = { class1 | class2 | class3 } .sp .B lldptool -t -i ethx -V LLDP-MED .B devtype .sp .ti -8 .IR TLV_TYPE " : = {LLDP-MED | medCap | medPolicy | medLoc | medPower | medHwRev | medFwRev | medSwRev | medSerNum | medManuf | medModel | medAssetID }" .SH DESCRIPTION The .B LLDP-MED extensions support the Link Layer Discovery Protocol for .B Media Endpoint Devices defined in the .B ANSI/TIA-1057-2006 document. Each TLV can be configured as a .B class1 , .B class2 or .B class3 device. Class I devices are the most basic class of Endpoint Device, Class II devices support media stream capabilities and Class III devices directly support end users of the IP communication system. See .B ANS-TIA-1057 for clarification of class types. .SH ARGUMENTS .TP .B enableTx Enables the TLV to be transmitted .TP .B devtype Set or query the class type of the device. .SH TLV_TYPE List of supported TLV specifiers applicable to Media Endpoint Devices. .TP .BR LLDP-MED apply arguments to all supported MED TLVs. .TP .BR medCAP LLDP-MED Capabilities TLV .TP .BR medPolicy LLDP-MED Network Policy TLV .TP .BR medLoc LLDP-MED Location TLV .TP .BR medPower LLDP-MED Extended Power-via-MDI TLV .TP .BR medHwRev LLDP-MED Hardware Revision TLV .TP .BR medFwRev LLDP-MED Firmware Revision TLV .TP .BR medSwRev LLDP-MED Software Revision TLV .TP .BR medSerNum LLDP-MED Serial Number TLV .TP .BR medManuf LLDP-MED Manufacturer Name TL .TP .BR medModel LLDP-MED Model Name TLV .TP .BR medAssetID LLDP-MED Asset ID TLV .SH EXAMPLE & USAGE .TP Enable class1 MED device on \fIeth2\fR .B lldptool -T -i eth2 -V LLDP-MED enableTx=yes devtype=class1 .TP Query class type of MED on \fIeth2\fR .B lldptool -t -i eth2 -V LLDP-MED -c devtype .TP Query transmit state of medPolicy on device \fIeth2\fR .B lldptool -t -i eth2 -V medPolicy -c enableTx .TP .SH SOURCE .TP o Link Layer Discovery Protocol for Media Endpoint Devices (http://www.tiaonline.org/standards/technology/voip/documents/ANSI-TIA-1057_final_for_publication.pdf) .SH NOTES .SH SEE ALSO .BR lldptool (8), .BR lldpad (8) .SH AUTHOR John Fastabend lldpad-0.9.46/docs/lldptool-pfc.8000066400000000000000000000044451215142747300165070ustar00rootroot00000000000000.TH lldptool 8 "August 2012" "open-lldp" "Linux" .SH NAME PFC \- Show / manipulate PFC TLV configuration .SH SYNOPSIS .B lldptool -t -i ethx -V PFC [ -c [ enableTx | willing | enabled | delay ] ] .sp .B lldptool -T -i ethx -V PFC .RI < CONFIG_ARG=value " " ... > .sp .ti +4 .IR CONFIG_ARG := .sp .BR enableTx= "<" yes "|" no ">" .sp .BR willing= "<" yes "|" no "|" 0 "|" 1 ">" .sp .BR enabled= "<" none "|[" 0 ".." 7 "]" , "[" 0 ".." 7 "]" , "...> .sp .BR delay= "" .sp .SH DESCRIPTION The PFC TLV is used to display and set current PFC TLV attributes. .SH ARGUMENTS .TP .B enableTx Enable the PFC TLV to be transmitted in the LLDP PDU for the specified interface. .TP .B willing Display or set the willing attribute. If set to .B yes and a peer TLV is received then the peer PFC attributes will be used. If set to .B no then locally configured attributes are used. .TP .B enabled Display or set the priorities with PFC enabled. The set attribute takes a comma separated list of priorities to enable, or the string .B none to disable all priorities. .TP .B delay Display or set the delay attribute used to configure PFC thresholds in hardware buffers. If PFC is enabled and frames continue to be dropped due to full hardware buffers then increasing this value may help. .SH Theory of Operations The PFC TLV uses the Symmetric attribute passing state machine defined in IEEE 802.1Qaz. This means the attributes used will depend on the willing bit. If the willing bit is set to 1 and a peer TLV is received then the peers attributes will be used. If the willing bit is set to 0 the local attributes will be used. When both the peer and local configuration are willing a tie breaking scheme is used. For more detailed coverage see the specification. .SH EXAMPLE & USAGE .TP Enable PFC for priorities 1, 2, and 4 on \fIeth2\fR .B lldptool -T -i eth2 -V PFC enabled=1,2,4 .TP Disable PFC for all priorities on \fIeth2\fR .B lldptool -T -i eth2 -V PFC enabled=none .TP Display configuration of PFC enabled priorities for \fIeth2\fR .B lldptool -t -i eth2 -V PFC -c enabled .TP Display last transmitted PFC TLV on \fIeth2\fR .B lldptool -t -i eth2 -V PFC .SH SOURCE .TP o IEEE 802.1Qaz (http://www.ieee802.org/1/pages/dcbridges.html) .SH NOTES .SH SEE ALSO .BR lldptool (8), .BR lldpad (8) .SH AUTHOR John Fastabend lldpad-0.9.46/docs/lldptool-vdp.8000066400000000000000000000055031215142747300165240ustar00rootroot00000000000000.TH lldptool 8 "February 2010" "open-lldp" "Linux" .SH NAME vdp \- Show / manipulate VDP TLV configuration .SH SYNOPSIS .B lldptool -t -i ethx -V vdp -c enableTx .sp .B lldptool -T -i ethx -V vdp -c enableTx=[yes|no] .sp .B lldptool -t -i ethx -V vdp -c mode .sp .B lldptool -T -i ethx -V vdp -c mode=,,,,,, .sp .B lldptool -t -i ethx -V vdp -c role .sp .B lldptool -T -i ethx -V vdp -c role=[station|bridge] .sp .SH DESCRIPTION The VSI discovery protocol (VDP) is \fINOT\fR a TLV in the LLDP sense but rather a protocol to manage the association and deassociation of virtual station interfaces (VSIs) between the station and an adjacent switch. VDP uses ECP as transport for VDP TLVs. An ECP frame may contain multiple VDP TLVs. Each VDP TLVs contains a mode, typeid, version, instanceid, mac and vlan for a VSI. .SH ARGUMENTS .TP .B enableTx Enables or disables VDP .TP .B mode shows or sets modes for VSIs with the following parameters: .RS .IP mode (0=preassociate, 1=preassociate with RR, 2=associate, 3=deassociate) .IP manager (database) id .IP VSI type id .IP VSI type id version .IP VSI instance id .IP VDP filter info format .IP VSI mac address .IP VSI vlan id .RE .TP .B role shows or sets the role of the local machine to act as either station (default) or bridge. .SH Theory of Operation The VDP protocol is used to pre-associate, associate or deassociate VSIs to and adjacent switch. Information about the VSIs is formatted into VDP TLVs which are then handed to ECP for lower-level transport. Each ECP frame may contain multiple VDP TLVs. Two ways to receive VSI information exist in llpdad: via netlink or with lldptool. netlink is used by libvirt to communicate VSIs to lldpad. lldptool can be used to associate/deassociate VSIs from the command line. This is especially helpful for testing purposes. .SH EXAMPLE & USAGE .TP Display if vdp is enabled on \fIeth8\fR .B lldptool -t -i eth8 -V vdp -c enableTx .TP Enable vdp on \fIeth8\fR .B lldptool -T -i eth8 -V vdp -c enableTx=yes .TP Display the currently configured VSIs for \fIeth8\fR .B lldptool -t -i eth8 -V vdp -c mode .TP Associate a VSI on \fIeth8\fR .B lldptool -T -i eth8 -V vdp -c mode=2,12,1193046,1,fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f,2,52:54:00:C7:3E:CE,3 .TP Display the locally configured role for VDP on \fIeth8\fR .B lldptool -t -i eth8 -V vdp -c role .TP Set the local role for VDP on \fIeth8\fR .B lldptool -T -i eth8 -V vdp -c role=bridge .SH SOURCE .TP o IEEE 802.1Qbg (http://www.ieee802.org/1/pages/802.1bg.html) .SH NOTES Currently the code in lldpad reflects draft 0 of the upcoming standard. ECP/VDP TLVs on the wire can be decoded with wireshark > v1.8. .SH SEE ALSO .BR lldptool-evb (8), .BR lldptool (8), .BR lldpad (8) .SH AUTHOR Jens Osterkamp lldpad-0.9.46/docs/lldptool.8000066400000000000000000000177421215142747300157450ustar00rootroot00000000000000.\" LICENSE .\" .\" This software program is released under the terms of a license agreement between you ('Licensee') and Intel. Do not use or load this software or any associated materials (collectively, the 'Software') until you have carefully read the full terms and conditions of the LICENSE located in this software package. By loading or using the Software, you agree to the terms of this Agreement. If you do not agree with the terms of this Agreement, do not install or use the Software. .\" .\" * Other names and brands may be claimed as the property of others. .\" .TH lldptool 8 "August 2012" "open-lldp" "Linux" .SH NAME lldptool \- manage the LLDP settings and status of lldpad .SH SYNOPSIS .B lldptool [options] [argument] .br .SH DESCRIPTION .B lldptool is used to query and configure .B lldpad. It connects to the client interface of .B lldpad to perform these operations. .B lldptool will operate in interactive mode if it is executed without a \fIcommand\fR. In interactive mode, .B lldptool will also function as an event listener to print out events as they are received asynchronously from .B lldpad. It will use libreadline for interactive input when available. .SH OPTIONS .TP .B \-i [ifname] specifies the network interface to which the command applies. Most lldptool commands require specifying a network interface. .TP .B -V [tlvid] specifies the TLV identifier .br The tlvid is an integer value used to identify specific LLDP TLVs. The tlvid value is the Type value for types not equal to 127 (the organizationally specific type). For organizationally specific TLVs, the tlvid is the value represented by the 3 byte OUI and 1 byte subtype - where the subtype is the lowest order byte of the tlvid. .br The tlvid can be entered as a numerical value (e.g. 10 or 0xa), or for supported TLVs, as a keyword. Review the .B lldptool help output to see the list of supported TLV keywords. .TP .B \-n "neighbor" option for commands which can use it (e.g. get-tlv) .TP .B -g [bridge scope] specify the bridge scope this command operates on. Allows to set and query all LLDP TLV modules for "nearest_bridge" (short: "nb"), "nearest_customer_bridge" ("ncb") and "nearest_nontpmr_bridge" ("nntpmrb") group mac addresses. Configurations are saved into independent sections in lldpad.conf. If no bridge scope is supplied this defaults to "nearest bridge" to preserve the previous behaviour. .TP .B \-c "config" option for TLV queries. Indicates that the query is for the configuration elements for the specified TLV. The argument list specifies the specific elements to query. If no arguments are listed, then all configuration elements for the TLV are returned. .TP .B \-r show raw client interface messages .TP .B \-R show only raw Client interface messages .PP .SH COMMANDS .TP .B license show license information .TP .B \-h, help show usage information .TP .B \-v, version show version information .TP .B \-S, stats get LLDP statistics for the specified interface .TP .B \-t, get-tlv get TLV information for the specified interface .TP .B \-T, set-tlv set TLV information for the specified interface .TP .B \-l, get-lldp get LLDP parameters for the specified interface .TP .B \-L, set-lldp set LLDP parameters for the specified interface .TP .B \-p, ping display the process identifier of the running lldpad process .TP .B \-q, quit exit from interactive mode .PP .SH ARGUMENTS This section lists arguments which are available for administration of LLDP parameters. Arguments for basic TLV's (non-organizationally specific TLVs) are also described. See the SEE ALSO section for references to other lldptool man pages which contain usage details and arguments for various organizationally specific TLVs. .TP .B adminStatus Argument for the .B get-lldp/set-lldp commands. Configures the LLDP adminStatus parameter for the specified interface. Valid values are: \fIdisabled\fR, \fIrx\fR, \fItx\fR, \fIrxtx\fR .TP .B enableTx Argument for the .B get-tlv/set-tlv commands. May be applied per interface for a specified TLV. Valid values are: \fIyes\fR, \fIno\fR. If the DCBX TLV enableTx is set to \fIno\fR, then all of the DCB feature TLVs DCBX advertise settings will be turned off as well. Setting enableTx to \fIyes\fR for a DCBX TLV will not affect the DCBX advertise settings. .TP .B ipv4 Argument for the .B get-tlv/set-tlv commands with respect to the Management Address TLV. The get command will retrieve the configured value. Set values take the form of an IPv4 address: \fIA.B.C.D\fR .TP .B ipv6 Argument for the .B get-tlv/set-tlv commands with respect to the Management Address TLV. The get command will retrieve the configured value. Set values take the form of an IPv6 address: \fI1111:2222:3333:4444:5555:6666:7777:8888\fR and various shorthand variations. .PP .SH EXAMPLES .TP Configure LLDP adminStatus to Receive and Transmit for interface \fIeth2\fR .B lldptool -L -i eth2 adminStatus=rxtx .br .B lldptool set-lldp -i eth2 adminStatus=rxtx .TP Query the LLDP adminStatus for interface \fIeth3\fR .B lldptool -l -i eth3 adminStatus .br .B lldptool get-lldp -i eth3 adminStatus .TP Query the LLDP statistics for interface \fIeth3\fR .B lldptool -S -i eth3 adminStatus .br .B lldptool stats -i eth3 adminStatus .TP Query the local TLVs which are being transmitted for a given interface: .B lldptool -t -i eth3 .br .B lldptool get-tlv -i eth3 .TP Query the received neighbor TLVs received on a given interface: .B lldptool -t -n -i eth3 .br .B lldptool get-tlv -n -i eth3 .TP Query the value of the System Description TLV as received from the neighbor on a given interface: .B lldptool -t -n -i eth3 -V sysDesc .br .B lldptool get-tlv -n -i eth3 -V 6 .TP Disable transmit of the IEEE 802.3 MAC/PHY Configuration Status TLV for a given interface: .B lldptool -T -i eth3 -V macPhyCfg enableTx=no .br .B lldptool set-tlv -i eth3 -V 0x120f01 enableTx=no .TP Query value of the transmit setting for the Port Description TLV for a given interface: .B lldptool -t -i eth3 -V portDesc -c enableTx .br .B lldptool get-tlv -i eth3 -V 4 -c enableTx .TP Set a Management Address TLV on eth3 to carry IPv4 address 192.168.10.10 .B lldptool -T -i eth3 -V mngAddr ipv4=192.168.10.10 .TP Set a Management Address TLV on eth3 to carry IPv6 address ::192.168.10.10 .B lldptool -T -i eth3 -V mngAddr ipv6=::192.168.10.10 .TP Get the configured IPv4 address for the Management Address TLV on eth3 .B lldptool -t -i eth3 -V mngAddr -c ipv4 .TP Get all configured attributes for the Management Address TLV on eth3 .B lldptool -t -i eth3 -V mngAddr -c .TP Enable transmit of the Edge Virtual Bridging TLV for interface eth4 .B lldptool -i eth4 -T -V evbCfg enableTx=yes .TP Enable transmit of VDP for interface eth4 .B lldptool -i eth4 -T -V vdp enableTx=yes .TP Display process identifier of lldpad .B lldptool -p .SH SEE ALSO .BR lldptool-dcbx (8), .BR lldptool-ets (8), .BR lldptool-pfc (8), .BR lldptool-app (8), .BR lldptool-med (8), .BR lldptool-vdp (8), .BR lldptool-evb (8), .BR lldptool-evb22 (8), .BR dcbtool (8), .BR lldpad (8) .br .SH COPYRIGHT lldptool - LLDP agent configuration utility .br Copyright(c) 2007-2012 Intel Corporation. .BR Portions of lldptool are based on: .IP hostapd-0.5.7 .IP Copyright (c) 2004-2008, Jouni Malinen .SH LICENSE This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. .LP This program is distributed in the hope 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. .LP 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 St - Fifth Floor, Boston, MA 02110-1301 USA. .LP The full GNU General Public License is included in this distribution in the file called "COPYING". .SH SUPPORT Contact Information: open-lldp Mailing List lldpad-0.9.46/eloop.c000066400000000000000000000272161215142747300143520ustar00rootroot00000000000000/* * Event loop based on select() loop * Copyright (c) 2002-2005, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include #include #include #include #include #include #include #include #include "eloop.h" #include "include/messages.h" #define UNUSED __attribute__((__unused__)) typedef long os_time_t; /** * os_sleep - Sleep (sec, usec) * @sec: Number of seconds to sleep * @usec: Number of microseconds to sleep */ struct os_time { os_time_t sec; os_time_t usec; }; /** * os_get_time - Get current time (sec, usec) * @t: Pointer to buffer for the time * Returns: 0 on success, -1 on failure */ int os_get_time(struct os_time *t) { int res; struct timeval tv; res = gettimeofday(&tv, NULL); t->sec = tv.tv_sec; t->usec = tv.tv_usec; return res; } /* Helper macros for handling struct os_time */ #define os_time_before(a, b) \ ((a)->sec < (b)->sec || \ ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) #define os_time_sub(a, b, res) do { \ (res)->sec = (a)->sec - (b)->sec; \ (res)->usec = (a)->usec - (b)->usec; \ if ((res)->usec < 0) { \ (res)->sec--; \ (res)->usec += 1000000; \ } \ } while (0) struct eloop_sock { int sock; void *eloop_data; void *user_data; eloop_sock_handler handler; }; struct eloop_timeout { struct os_time time; void *eloop_data; void *user_data; eloop_timeout_handler handler; struct eloop_timeout *next; }; struct eloop_signal { int sig; void *user_data; eloop_signal_handler handler; int signaled; }; struct eloop_sock_table { int count; struct eloop_sock *table; int changed; }; struct eloop_data { void *user_data; int max_sock; struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; struct eloop_timeout *timeout; int signal_count; struct eloop_signal *signals; int signaled; int pending_terminate; int terminate; int reader_table_changed; }; static struct eloop_data eloop; int eloop_init(void *user_data) { memset(&eloop, 0, sizeof(eloop)); eloop.user_data = user_data; return 0; } static int eloop_sock_table_add_sock(struct eloop_sock_table *table, int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { struct eloop_sock *tmp; if (table == NULL) return -EINVAL; tmp = (struct eloop_sock *) realloc(table->table, (table->count + 1) * sizeof(struct eloop_sock)); if (tmp == NULL) return -ENOMEM; tmp[table->count].sock = sock; tmp[table->count].eloop_data = eloop_data; tmp[table->count].user_data = user_data; tmp[table->count].handler = handler; table->count++; table->table = tmp; if (sock > eloop.max_sock) eloop.max_sock = sock; table->changed = 1; return 0; } static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, int sock) { int i; if (table == NULL || table->table == NULL || table->count == 0) return; for (i = 0; i < table->count; i++) { if (table->table[i].sock == sock) break; } if (i == table->count) return; if (i != table->count - 1) { memmove(&table->table[i], &table->table[i + 1], (table->count - i - 1) * sizeof(struct eloop_sock)); } table->count--; table->changed = 1; } static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) { int i; FD_ZERO(fds); if (table->table == NULL) return; for (i = 0; i < table->count; i++) FD_SET(table->table[i].sock, fds); } static void eloop_sock_table_dispatch(struct eloop_sock_table *table, fd_set *fds) { int i; if (table == NULL || table->table == NULL) return; table->changed = 0; for (i = 0; i < table->count; i++) { if (FD_ISSET(table->table[i].sock, fds)) { table->table[i].handler(table->table[i].sock, table->table[i].eloop_data, table->table[i].user_data); if (table->changed) break; } } } static void eloop_sock_table_destroy(struct eloop_sock_table *table) { int rc, tc, sock; if (table) { tc = table->count; while (tc > 0) { sock = table->table[tc].sock; rc = fcntl(sock, F_GETFD); if (rc != -1) { rc = close(sock); if (rc) LLDPAD_ERR("Failed to close fd - %s\n", strerror(errno)); } tc--; } free(table->table); } } int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { return eloop_register_sock(sock, EVENT_TYPE_READ, handler, eloop_data, user_data); } void eloop_unregister_read_sock(int sock) { eloop_unregister_sock(sock, EVENT_TYPE_READ); } static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type) { switch (type) { case EVENT_TYPE_READ: return &eloop.readers; case EVENT_TYPE_WRITE: return &eloop.writers; case EVENT_TYPE_EXCEPTION: return &eloop.exceptions; } return NULL; } int eloop_register_sock(int sock, eloop_event_type type, eloop_sock_handler handler, void *eloop_data, void *user_data) { struct eloop_sock_table *table; table = eloop_get_sock_table(type); return eloop_sock_table_add_sock(table, sock, handler, eloop_data, user_data); } void eloop_unregister_sock(int sock, eloop_event_type type) { struct eloop_sock_table *table; table = eloop_get_sock_table(type); eloop_sock_table_remove_sock(table, sock); } int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp, *prev; timeout = malloc(sizeof(*timeout)); if (timeout == NULL) return -1; os_get_time(&timeout->time); timeout->time.sec += secs; timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; timeout->time.usec -= 1000000; } timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; timeout->next = NULL; if (eloop.timeout == NULL) { eloop.timeout = timeout; return 0; } prev = NULL; tmp = eloop.timeout; while (tmp != NULL) { if (os_time_before(&timeout->time, &tmp->time)) break; prev = tmp; tmp = tmp->next; } if (prev == NULL) { timeout->next = eloop.timeout; eloop.timeout = timeout; } else { timeout->next = prev->next; prev->next = timeout; } return 0; } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *prev, *next; int removed = 0; prev = NULL; timeout = eloop.timeout; while (timeout != NULL) { next = timeout->next; if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { if (prev == NULL) eloop.timeout = next; else prev->next = next; free(timeout); removed++; } else prev = timeout; timeout = next; } return removed; } static void eloop_handle_alarm(int sig) { fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " "seconds. Looks like there\n" "is a bug that ends up in a busy loop that " "prevents clean shutdown.\n" "Killing program forcefully.\n" "sig is %d.\n", sig); exit(1); } static void eloop_handle_signal(int sig) { int i; if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { /* Use SIGALRM to break out from potential busy loops that * would not allow the program to be killed. */ eloop.pending_terminate = 1; signal(SIGALRM, eloop_handle_alarm); alarm(2); } eloop.signaled++; for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].sig == sig) { eloop.signals[i].signaled++; break; } } } static void eloop_process_pending_signals(void) { int i; if (eloop.signaled == 0) return; eloop.signaled = 0; if (eloop.pending_terminate) { alarm(0); eloop.pending_terminate = 0; } for (i = 0; i < eloop.signal_count; i++) { if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, eloop.user_data, eloop.signals[i].user_data); } } } int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { struct eloop_signal *tmp; tmp = (struct eloop_signal *) realloc(eloop.signals, (eloop.signal_count + 1) * sizeof(struct eloop_signal)); if (tmp == NULL) return -1; tmp[eloop.signal_count].sig = sig; tmp[eloop.signal_count].user_data = user_data; tmp[eloop.signal_count].handler = handler; tmp[eloop.signal_count].signaled = 0; eloop.signal_count++; eloop.signals = tmp; signal(sig, eloop_handle_signal); return 0; } int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data) { int ret = eloop_register_signal(SIGINT, handler, user_data); if (ret == 0) ret = eloop_register_signal(SIGTERM, handler, user_data); return ret; } int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data) { return eloop_register_signal(SIGHUP, handler, user_data); } void eloop_run(void) { fd_set *rfds, *wfds, *efds; int res; struct timeval _tv; struct os_time tv, now; rfds = malloc(sizeof(*rfds)); wfds = malloc(sizeof(*wfds)); efds = malloc(sizeof(*efds)); if (rfds == NULL || wfds == NULL || efds == NULL) { printf("eloop_run - malloc failed\n"); goto out; } while (!eloop.terminate && (eloop.timeout || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { if (eloop.timeout) { os_get_time(&now); if (os_time_before(&now, &eloop.timeout->time)) os_time_sub(&eloop.timeout->time, &now, &tv); else tv.sec = tv.usec = 0; _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; } eloop_sock_table_set_fds(&eloop.readers, rfds); eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.exceptions, efds); res = select(eloop.max_sock + 1, rfds, wfds, efds, eloop.timeout ? &_tv : NULL); if (res < 0 && errno != EINTR && errno != 0) { perror("select"); goto out; } eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ if (eloop.timeout) { struct eloop_timeout *tmp; os_get_time(&now); if (!os_time_before(&now, &eloop.timeout->time)) { tmp = eloop.timeout; eloop.timeout = eloop.timeout->next; tmp->handler(tmp->eloop_data, tmp->user_data); free(tmp); } } if (res <= 0) continue; eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.exceptions, efds); } out: free(rfds); free(wfds); free(efds); } void eloop_terminate(int sig, UNUSED void *eloop_ctx, UNUSED void *signal_ctx) { printf("Signal %d received - terminating\n", sig); eloop.terminate = 1; } void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; timeout = eloop.timeout; while (timeout != NULL) { prev = timeout; timeout = timeout->next; free(prev); } eloop_sock_table_destroy(&eloop.readers); eloop_sock_table_destroy(&eloop.writers); eloop_sock_table_destroy(&eloop.exceptions); free(eloop.signals); } int eloop_terminated(void) { return eloop.terminate; } void eloop_wait_for_read_sock(int sock) { fd_set rfds; if (sock < 0) return; FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); } void * eloop_get_user_data(void) { return eloop.user_data; } lldpad-0.9.46/event_iface.c000066400000000000000000000301311215142747300154720ustar00rootroot00000000000000/****************************************************************************** LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Implementation of peer netlink interface (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Stefan Berger Gerhard Stenzel Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/if.h" #include "linux/if_vlan.h" #include "linux/rtnetlink.h" #include "linux/if_link.h" #include "lldpad.h" #include "lldp_mod.h" #include "eloop.h" #include "event_iface.h" #include "lldp_util.h" #include "config.h" #include "lldp/l2_packet.h" #include "config.h" #include "lldp/states.h" #include "messages.h" #include "lldp_rtnl.h" #include "lldp_vdpnl.h" #include "lldp_tlv.h" extern unsigned int if_nametoindex(const char *); extern char *if_indextoname(unsigned int, char *); static int peer_sock; static void event_if_decode_rta(int type, struct rtattr *rta, int *ls, char *d) { switch (type) { case IFLA_ADDRESS: LLDPAD_DBG(" IFLA_ADDRESS\n"); break; case IFLA_BROADCAST: LLDPAD_DBG(" IFLA_BROADCAST\n"); break; case IFLA_IFNAME: strncpy(d, (char *)RTA_DATA(rta), IFNAMSIZ); LLDPAD_DBG(" IFLA_IFNAME\n"); LLDPAD_DBG(" device name is %s\n", d); break; case IFLA_MTU: LLDPAD_DBG(" IFLA_MTU\n"); break; case IFLA_LINK: LLDPAD_DBG(" IFLA_LINK\n"); break; case IFLA_QDISC: LLDPAD_DBG(" IFLA_QDISC\n"); break; case IFLA_STATS: LLDPAD_DBG(" IFLA_STATS\n"); break; case IFLA_COST: LLDPAD_DBG(" IFLA_COST\n"); break; case IFLA_PRIORITY: LLDPAD_DBG(" IFLA_PRIORITY\n"); break; case IFLA_MASTER: LLDPAD_DBG(" IFLA_MASTER\n"); break; case IFLA_WIRELESS: LLDPAD_DBG(" IFLA_WIRELESS\n"); break; case IFLA_PROTINFO: LLDPAD_DBG(" IFLA_PROTINFO\n"); break; case IFLA_TXQLEN: LLDPAD_DBG(" IFLA_TXQLEN\n"); break; case IFLA_MAP: LLDPAD_DBG(" IFLA_MAP\n"); break; case IFLA_WEIGHT: LLDPAD_DBG(" IFLA_WEIGHT\n"); break; case IFLA_OPERSTATE: LLDPAD_DBG(" IFLA_OPERSTATE\n"); *ls = (*((int *)RTA_DATA(rta))); LLDPAD_DBG(" OPERSTATE = 0x%02x\n", *ls); break; case IFLA_LINKMODE: LLDPAD_DBG(" IFLA_LINKMODE\n"); LLDPAD_DBG(" LINKMODE = %s\n", (*((int *)RTA_DATA(rta)))? "IF_LINK_MODE_DORMANT": "IF_LINK_MODE_DEFAULT"); break; case IFLA_LINKINFO: LLDPAD_DBG(" IFLA_LINKINFO\n"); break; case IFLA_NET_NS_PID: LLDPAD_DBG(" IFLA_NET_NS_PID\n"); break; case IFLA_IFALIAS: LLDPAD_DBG(" IFLA_IFALIAS\n"); break; case IFLA_NUM_VF: LLDPAD_DBG(" IFLA_NUMVF\n"); break; case IFLA_VFINFO_LIST: LLDPAD_DBG(" IFLA_VFINFO_LIST\n"); break; case IFLA_STATS64: LLDPAD_DBG(" IFLA_STATS64\n"); break; case IFLA_VF_PORTS: LLDPAD_DBG(" IFLA_VF_PORTS\n"); break; case IFLA_PORT_SELF: LLDPAD_DBG(" IFLA_PORT_SELF\n"); break; case IFLA_AF_SPEC: LLDPAD_DBG(" IFLA_AF_SPEC\n"); break; case IFLA_GROUP: LLDPAD_DBG(" IFLA_GROUP\n"); break; case IFLA_NET_NS_FD: LLDPAD_DBG(" IFLA_NET_NS_FD\n"); break; case IFLA_EXT_MASK: LLDPAD_DBG(" IFLA_EXT_MASK\n"); break; case IFLA_PROMISCUITY: LLDPAD_DBG(" IFLA_PROMISCUITY\n"); break; case IFLA_NUM_TX_QUEUES: LLDPAD_DBG(" IFLA_NUM_TX_QUEUES\n"); break; case IFLA_NUM_RX_QUEUES: LLDPAD_DBG(" IFLA_NUM_RX_QUEUES\n"); break; case IFLA_CARRIER: LLDPAD_DBG(" IFLA_CARRIER\n"); break; default: LLDPAD_DBG(" unknown type : 0x%02x\n", type); break; } } int oper_add_device(char *device_name) { struct lldp_module *np; struct port *port, *newport; struct lldp_agent *agent; port = porthead; while (port != NULL) { if (!strncmp(device_name, port->ifname, MAX_DEVICE_NAME_LEN)) break; port = port->next; } if (!port) { newport = add_port(device_name); if (newport == NULL) { LLDPAD_INFO("%s: Error adding device %s\n", __func__, device_name); return -EINVAL; } LLDPAD_INFO("%s: Adding device %s\n", __func__, device_name); port = newport; } else if (is_bond(device_name) || !port->portEnabled) reinit_port(device_name); lldp_add_agent(device_name, NEAREST_BRIDGE); lldp_add_agent(device_name, NEAREST_NONTPMR_BRIDGE); lldp_add_agent(device_name, NEAREST_CUSTOMER_BRIDGE); LIST_FOREACH(agent, &port->agent_head, entry) { LLDPAD_DBG("%s: calling ifup for agent %p.\n", __func__, agent); LIST_FOREACH(np, &lldp_head, lldp) { if (np->ops->lldp_mod_ifup) np->ops->lldp_mod_ifup(device_name, agent); } } set_lldp_port_enable(device_name, 1); return 0; } static void event_if_decode_nlmsg(int route_type, void *data, int len) { struct lldp_module *np; const struct lldp_mod_ops *ops; struct rtattr *rta; char device_name[IFNAMSIZ]; struct lldp_agent *agent; int attrlen; int valid; int link_status = IF_OPER_UNKNOWN; switch (route_type) { case RTM_NEWLINK: case RTM_DELLINK: case RTM_SETLINK: case RTM_GETLINK: LLDPAD_DBG(" IFINFOMSG\n"); LLDPAD_DBG(" ifi_family = 0x%02x\n", ((struct ifinfomsg *)data)->ifi_family); LLDPAD_DBG(" ifi_type = 0x%x\n", ((struct ifinfomsg *)data)->ifi_type); LLDPAD_DBG(" ifi_index = %i\n", ((struct ifinfomsg *)data)->ifi_index); LLDPAD_DBG(" ifi_flags = 0x%04x\n", ((struct ifinfomsg *)data)->ifi_flags); LLDPAD_DBG(" ifi_change = 0x%04x\n", ((struct ifinfomsg *)data)->ifi_change); /* print attributes */ rta = IFLA_RTA(data); attrlen = len - sizeof(struct ifinfomsg); while (RTA_OK(rta, attrlen)) { event_if_decode_rta(rta->rta_type, rta, &link_status, device_name); rta = RTA_NEXT(rta, attrlen); } LLDPAD_DBG(" link status: %i\n", link_status); LLDPAD_DBG(" device name: %s\n", device_name); switch (link_status) { case IF_OPER_DOWN: LLDPAD_DBG("******* LINK DOWN: %s\n", device_name); valid = is_valid_lldp_device(device_name); if (!valid) break; struct port *port = port_find_by_name(device_name); if (!port) break; LIST_FOREACH(agent, &port->agent_head, entry) { LLDPAD_DBG("%s: calling ifdown for agent %p.\n", __func__, agent); LIST_FOREACH(np, &lldp_head, lldp) { ops = np->ops; if (ops->lldp_mod_ifdown) ops->lldp_mod_ifdown(device_name, agent); } } /* Disable Port */ set_lldp_port_enable(device_name, 0); if (route_type == RTM_DELLINK) { LLDPAD_INFO("%s: %s: device removed!\n", __func__, device_name); remove_port(device_name); } break; case IF_OPER_DORMANT: LLDPAD_DBG("******* LINK DORMANT: %s\n", device_name); valid = is_valid_lldp_device(device_name); if (!valid) break; set_port_oper_delay(device_name); oper_add_device(device_name); break; case IF_OPER_UP: LLDPAD_DBG("******* LINK UP: %s\n", device_name); valid = is_valid_lldp_device(device_name); if (!valid) break; oper_add_device(device_name); break; default: LLDPAD_DBG("******* LINK STATUS %d: %s\n", link_status, device_name); break; } break; case RTM_NEWADDR: case RTM_DELADDR: case RTM_GETADDR: LLDPAD_DBG("Address change.\n"); break; default: LLDPAD_DBG("No decode for this type\n"); } } static void event_if_process_recvmsg(struct nlmsghdr *nlmsg) { LLDPAD_DBG("%s:%s: nlmsg_type: %d\n", __FILE__, __FUNCTION__, nlmsg->nlmsg_type); event_if_decode_nlmsg(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg), NLMSG_PAYLOAD(nlmsg, 0)); } int event_trigger(struct nlmsghdr *nlh, pid_t pid) { struct sockaddr_nl dest_addr; int dest_addrlen = sizeof dest_addr, rc; memset(&dest_addr, 0, dest_addrlen); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = pid; rc = sendto(peer_sock, (void *)nlh, nlh->nlmsg_len, 0, (struct sockaddr *) &dest_addr, dest_addrlen); LLDPAD_DBG("%s rc:%d pid:%d sender-pid:%d msgsize:%d\n", __func__, rc, pid, nlh->nlmsg_pid, nlh->nlmsg_len); return rc; } static void event_iface_receive_user_space(int sock, UNUSED void *eloop_ctx, UNUSED void *sock_ctx) { struct sockaddr_nl dest_addr; unsigned char buf[MAX_PAYLOAD]; socklen_t fromlen = sizeof(dest_addr); int result; LLDPAD_DBG("Waiting for message\n"); result = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &dest_addr, &fromlen); if(result < 0) { LLDPAD_ERR("%s:receive error on netlink socket:%d\n", __func__, errno); return; } LLDPAD_DBG("%s:recvfrom received %d bytes from pid %d\n", __func__, result, dest_addr.nl_pid); result = vdpnl_recv(buf, sizeof buf); if (result > 0) { /* Data to send back */ result = sendto(sock, buf, result, 0, (struct sockaddr *) &dest_addr, fromlen); if (result < 0) LLDPAD_ERR("%s:send error on netlink socket:%d\n", __func__, errno); else LLDPAD_DBG("%s:sentto pid %d bytes:%d\n", __func__, dest_addr.nl_pid, result); } } static void event_iface_receive(int sock, UNUSED void *eloop_ctx, UNUSED void *sock_ctx) { struct nlmsghdr *nlh; struct sockaddr_nl dest_addr; char buf[MAX_PAYLOAD]; socklen_t fromlen = sizeof(dest_addr); int result; result = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *) &dest_addr, &fromlen); if (result < 0) { perror("recvfrom(Event interface)"); eloop_register_timeout(INI_TIMER, 0, scan_port, NULL, NULL); return; } LLDPAD_DBG("%s:%s result from receive: %d.\n", __FILE__, __FUNCTION__, result); /* userspace messages handled in event_iface_receive_user_space() */ if (dest_addr.nl_pid != 0) return; nlh = (struct nlmsghdr *)buf; event_if_process_recvmsg(nlh); } int event_iface_init() { int fd; int rcv_size = MAX_PAYLOAD; struct sockaddr_nl snl; fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return fd; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { close(fd); return -EIO; } memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_groups = RTMGRP_LINK; if (bind(fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(fd); return -EIO; } return eloop_register_read_sock(fd, event_iface_receive, NULL, NULL); } int event_iface_init_user_space() { int fd; int rcv_size = MAX_PAYLOAD; struct sockaddr_nl snl; fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return fd; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { close(fd); return -EIO; } memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); /* self pid */ snl.nl_groups = 0; if (bind(fd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(fd); LLDPAD_ERR("Error binding to netlink socket (%s) !\n", strerror(errno)); return -EIO; } peer_sock = fd; LLDPAD_DBG("%s(%i): socket %i.\n", __func__, __LINE__, peer_sock); return eloop_register_read_sock(fd, event_iface_receive_user_space, NULL, NULL); } int event_iface_deinit() { int rc; rc = fcntl(peer_sock, F_GETFD); if (rc != -1) { rc = close(peer_sock); if (rc) LLDPAD_ERR("Failed to close fd - %s\n", strerror(errno)); } return 0; } lldpad-0.9.46/include/000077500000000000000000000000001215142747300145035ustar00rootroot00000000000000lldpad-0.9.46/include/clif.h000066400000000000000000000153351215142747300156000ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef CLIF_H #define CLIF_H #include #include "clif_sock.h" /** * struct clif - Internal structure for client interface library * * This structure is used by the lldpad client interface * library to store internal data. Programs using the library should not touch * this data directly. They can only use the pointer to the data structure as * an identifier for the client interface connection and use this as one of * the arguments for most of the client interface library functions. */ struct clif { int s; struct sockaddr_un local; struct sockaddr_un dest; }; /* lldpad client interface access */ /** * clif_open - Open a client interface to the lldpad * Returns: Pointer to abstract client interface data or %NULL on failure * * This function is used to open a client interface to the lldpad. */ struct clif *clif_open(void); /** * clif_close - Close a client interface to lldpad * @clif: Control interface data from clif_open() * * This function is used to close a client interface. */ void clif_close(struct clif *clif); /** * clif_request - Send a command to lldpad * @clif: Control interface data from clif_open() * @cmd: Command; usually, ASCII text, e.g., "PING" * @cmd_len: Length of the cmd in bytes * @reply: Buffer for the response * @reply_len: Reply buffer length * @msg_cb: Callback function for unsolicited messages or %NULL if not used * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout * * This function is used to send commands to lldpad. Received * response will be written to reply and reply_len is set to the actual length * of the reply. This function will block for up to two seconds while waiting * for the reply. If unsolicited messages are received, the blocking time may * be longer. * * msg_cb can be used to register a callback function that will be called for * unsolicited messages received while waiting for the command response. These * messages may be received if clif_request() is called at the same time as * lldpad is sending such a message. This can happen only if * the program has used clif_attach() to register itself as a monitor for * event messages. Alternatively to msg_cb, programs can register two client * interface connections and use one of them for commands and the other one for * receiving event messages, in other words, call clif_attach() only for * the client interface connection that will be used for event messages. */ #define CMD_RESPONSE_TIMEOUT 2 int clif_request(struct clif *clif, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)); /** * clif_attach - Register as an event monitor for the client interface * @clif: Control interface data from clif_open() * Returns: 0 on success, -1 on failure, -2 on timeout * * This function registers the client interface connection as a monitor for * lldpad events. After a success clif_attach() call, the * client interface connection starts receiving event messages that can be * read with clif_recv(). */ int clif_attach(struct clif *clif, char *hex_tlvs); /** * clif_detach - Unregister event monitor from the client interface * @clif: Control interface data from clif_open() * Returns: 0 on success, -1 on failure, -2 on timeout * * This function unregisters the client interface connection as a monitor for * lldpad events, i.e., cancels the registration done with * clif_attach(). */ int clif_detach(struct clif *clif); /** * clif_recv - Receive a pending client interface message * @clif: Control interface data from clif_open() * @reply: Buffer for the message data * @reply_len: Length of the reply buffer * Returns: 0 on success, -1 on failure * * This function will receive a pending client interface message. This * function will block if no messages are available. The received response will * be written to reply and reply_len is set to the actual length of the reply. * clif_recv() is only used for event messages, i.e., clif_attach() * must have been used to register the client interface as an event monitor. */ int clif_recv(struct clif *clif, char *reply, size_t *reply_len); /** * clif_pending - Check whether there are pending event messages * @clif: Control interface data from clif_open() * Returns: 1 if there are pending messages, 0 if no, or -1 on error * * This function will check whether there are any pending client interface * message available to be received with clif_recv(). clif_pending() is * only used for event messages, i.e., clif_attach() must have been used to * register the client interface as an event monitor. */ int clif_pending(struct clif *clif); /** * clif_get_fd - Get file descriptor used by the client interface * @clif: Control interface data from clif_open() * Returns: File descriptor used for the connection * * This function can be used to get the file descriptor that is used for the * client interface connection. The returned value can be used, e.g., with * select() while waiting for multiple events. * * The returned file descriptor must not be used directly for sending or * receiving packets; instead, the library functions clif_request() and * clif_recv() must be used for this. */ int clif_get_fd(struct clif *clif); /** * clif_getpid - Get PID of running lldpad process * Returns: The PID of lldpad or 0 on failure * * This function is returns the PID of the running lldpad. It connects to the * lldpad and sends a PING command. Lldpad returns with a PONG followed by * its PID. Extract the PID and return it to the caller. */ pid_t clif_getpid(void); #endif /* CLIF_H */ lldpad-0.9.46/include/clif_msgs.h000066400000000000000000000100531215142747300166210ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _CLIF_MSGS_H #define _CLIF_MSGS_H #include #include "clif_sock.h" #include "lldpad_status.h" #ifndef IFNAMSIZ #define IFNAMSIZ 16 #endif /* Client interface versions */ /* Version 2 * DCBX subtype field added to peer commands * Priority Group feature adds 'number of TC's supported' * Priority Flow Control feature adds 'number of TC's supported' */ #define CLIF_EV_VERSION 2 #define CLIF_MSG_VERSION 3 #define CLIF_RSP_VERSION CLIF_MSG_VERSION /* Minimum DCB CLIF MSG version we can resolve */ #define CLIF_DCBMSG_VERSION 2 /* Client interface global command codes */ #define UNKNOWN_CMD '.' #define PING_CMD 'P' #define LEVEL_CMD 'L' #define ATTACH_CMD 'A' #define DETACH_CMD 'D' #define DCB_CMD 'C' #define MOD_CMD 'M' #define EVENT_MSG 'E' #define CMD_RESPONSE 'R' #define CMD_REQUEST DCB_CMD /* Remote Change Event ByteCode */ #define LLDP_RCHANGE 1 /* Offsets in client interface module request message */ #define MOD_ID 1 /* Generic client interface field offsets */ #define MSG_TYPE 0 #define MOD_MSG_TYPE 9 /* location of MSG_TYPE in a module message */ /* Client interface event message field offsets */ #define EV_MSG_OFF 0 #define EV_LEVEL_OFF 1 #define EV_GENMSG_OFF 2 /* for unformatted non-DCB event messages */ #define EV_VERSION_OFF 2 /* for DCB event messages */ #define EV_PORT_LEN_OFF 3 #define EV_PORT_LEN_LEN 2 #define EV_PORT_ID_OFF (EV_PORT_LEN_OFF + EV_PORT_LEN_LEN) /* Offsets in client interface request messages * Module message type and module id */ #define MSG_TYPE 0 /* message type i.e. 'C' */ #define MSG_VER 1 /* message version */ #define CMD_CODE 2 /* command code */ #define CMD_OPS 4 /* command options */ #define CMD_IF_LEN 12 /* length of ifname field, '00' is ok */ #define CMD_IF 14 /* ifname field */ /* Client interface response message field offsets */ #define CLIF_STAT_OFF 1 #define CLIF_STAT_LEN 2 #define CLIF_RSP_OFF (CLIF_STAT_OFF + CLIF_STAT_LEN) /* max buffer length needed for a field with an unsigned char length */ #define MAX_U8_BUF 256 /* max buffer length for a clif message */ #define MAX_CLIF_MSGBUF 4096 struct cmd { __u8 cmd; __u32 module_id; __u32 ops; __u32 tlvid; __u8 type; char ifname[IFNAMSIZ+1]; char obuf[MAX_CLIF_MSGBUF]; }; enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR, MSG_EVENT }; #define MSG_DCB MSG_EVENT #define SHOW_NO_OUTPUT 0x00 #define SHOW_OUTPUT 0x01 #define SHOW_RAW 0x02 #define SHOW_RAW_ONLY 0x04 #define INVALID_TLVID 127 struct type_name_info { __u32 type; char *name; /* printable name */ char *key; /* key word */ void (* print_info)(__u16, char *); void (* get_info)(__u16, char *); }; #define LLDP_ARG 0x00 #define TLV_ARG 0x01 struct arg_handlers { char *arg; int arg_class; int (*handle_get)(struct cmd *, char *, char *, char *, int); int (*handle_set)(struct cmd *, char *, char *, char *, int); int (*handle_test)(struct cmd *, char *, char *, char *, int); }; #endif lldpad-0.9.46/include/clif_sock.h000066400000000000000000000023661215142747300166170ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: e1000-eedc Mailing List Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *******************************************************************************/ #ifndef CLIF_SOCK_H #define CLIF_SOCK_H #define LLDP_CLIF_SOCK "/com/intel/lldpad" #endif /* CLIF_SOCK_H */ lldpad-0.9.46/include/config.h000066400000000000000000000114421215142747300161230ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _CONFIG_H_ #define _CONFIG_H_ #include "lldp.h" #include #define DEFAULT_CFG_FILE "/var/lib/lldpad/lldpad.conf" #define LLDP_SETTING "lldp" #define LLDP_COMMON "common" #define INI_TIMER 5 #define TYPE_BOOL 0 #define TYPE_PERCENT 1 #define TYPE_INT 2 #define TYPE_PRIORITY 3 #define TYPE_CHAR 4 #define TYPE_DOUBLE 4 #ifdef LIBCONFIG_VER_MAJOR #if LIBCONFIG_VER_MAJOR >= 1 && LIBCONFIG_VER_MINOR >= 4 #define CONFIG_INT_TYPE #endif /* LIBCONFIG_VER_MAJOR >= 1 && LIBCONFIG_VER_MINOR >= 4 */ #endif /* LIBCONFIG_VER_MAJOR */ #ifdef CONFIG_INT_TYPE typedef int config_int_t; #else typedef long config_int_t; #endif /* CONFIG_INT_TYPE */ extern char *cfg_file_name; union cfg_get { int *pint; unsigned int *puint; long long *p64; double *pfloat; const char **ppchar; } __attribute__((__transparent_union__)); union cfg_set { const int *pint; const unsigned int *puint; const long long *p64; const double *pfloat; const char **ppchar; } __attribute__((__transparent_union__)); void scan_port(void *eloop_data, void *user_ctx); int get_cfg(const char *ifname, int agenttype, char *path, union cfg_get value, int type); int set_cfg(const char *ifname, int agenttype, char *path, union cfg_set value, int type); int get_config_setting(const char *ifname, int agenttype, char *path, union cfg_get value, int type); int set_config_setting(const char *ifname, int agenttype, char *path, union cfg_set value, int type); int remove_config_setting(const char *ifname, int agenttype, char *parent, char *name); int get_config_tlvfield(const char *ifname, int agenttype, u32 tlvid, const char *field, union cfg_get value, int type); int get_config_tlvfield_int(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value); int get_config_tlvfield_bool(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value); int get_config_tlvfield_bin(const char *ifname, int agenttype, u32 tlvid, const char *field, void *value, size_t size); int get_config_tlvfield_str(const char *ifname, int agenttype, u32 tlvid, const char *field, char *value, size_t size); int get_config_tlvinfo_bin(const char *ifname, int agenttype, u32 tlvid, void *value, size_t size); int get_config_tlvinfo_str(const char *ifname, int agenttype, u32 tlvid, char *value, size_t size); int set_config_tlvfield(const char *ifname, int agenttype, u32 tlvid, const char *field, union cfg_set value, int type); int set_config_tlvfield_int(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value); int set_config_tlvfield_bool(const char *ifname, int agenttype, u32 tlvid, const char *field, int *value); int set_config_tlvfield_bin(const char *ifname, int agenttype, u32 tlvid, const char *field, void *value, size_t size); int set_config_tlvfield_str(const char *ifname, int agenttype, u32 tlvid, const char *field, const char *value); int set_config_tlvinfo_bin(const char *ifname, int agenttype, u32 tlvid, void *value, size_t size); int set_config_tlvinfo_str(const char *ifname, int agenttype, u32 tlvid, char *value); int is_tlv_txdisabled(const char *ifname, int agenttype, u32 tlvid); int is_tlv_txenabled(const char *ifname, int agenttype, u32 tlvid); int tlv_enabletx(const char *ifname, int agenttype, u32 tlvid); int tlv_disabletx(const char *ifname, int agenttype, u32 tlvid); int get_med_devtype(const char *ifname, int agenttype); void set_med_devtype(const char *ifname, int agenttype, int devtype); void create_default_cfg_file(void); int get_int_config(config_setting_t *s, char *attr, int int_type, int *result); int get_array_config(config_setting_t *s, char *attr, int int_type, int *result); int init_cfg(void); void destroy_cfg(void); int check_cfg_file(void); int check_for_old_file_format(void); void init_ports(void); #endif /* _CONFIG_H_ */ lldpad-0.9.46/include/ctrl_iface.h000066400000000000000000000052331215142747300167520ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef CTRL_IFACE_H #define CTRL_IFACE_H #include #include #include "lldp.h" #include "lldp_rtnl.h" #include "dcb_protocol.h" struct ctrl_dst; struct clif_data { int ctrl_sock; struct ctrl_dst *ctrl_dst; }; int ctrl_iface_init(struct clif_data *clifd); int ctrl_iface_register(struct clif_data *clifd); void ctrl_iface_deinit(struct clif_data *clifd); void ctrl_iface_send(struct clif_data *clifd, int level, u32 moduleid, char *buf, size_t len); int clif_iface_attach(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int clif_iface_detach(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int clif_iface_level(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int clif_iface_ping(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int clif_iface_cmd_unknown(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int clif_iface_module(struct clif_data *clifd, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); #endif /* CTRL_IFACE_H */ lldpad-0.9.46/include/dcb_driver_interface.h000066400000000000000000000035041215142747300210010ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_DRIVER_INTERFACE_H_ #define _DCB_DRIVER_INTERFACE_H_ typedef struct pgroup_attribs { dcb_traffic_attribs_type tx; dcb_traffic_attribs_type rx; } pgroup_attribs; typedef struct appgroup_attribs { u8 dcb_app_idtype; u16 dcb_app_id; u8 dcb_app_priority; } appgroup_attribs; int set_hw_pg(char *device_name, pgroup_attribs *pg_data, bool Opermode); int set_hw_pfc(char *device_name, dcb_pfc_list_type pfc_data, bool Opermode); int set_hw_app(char *device_name, appgroup_attribs *app_data); int set_hw_all(char *device_name); int get_dcb_capabilities(char *device_name, struct feature_support *dcb_capabilites); u32 double_to_fixpt_int(double double_val); int get_dcb_numtcs(const char *device_name, u8 *pgtcs, u8 *pfctcs); #endif /* _DCB_DRIVER_INTERFACE_H_ */ lldpad-0.9.46/include/dcb_events.h000066400000000000000000000026761215142747300170030ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_EVENTS_H_ #define _DCB_EVENTS_H_ #define EVENT_OPERMODE 0x0001 /* Operational mode changed */ #define EVENT_OPERATTR 0x0002 /* Operational configuration changed */ void pg_event(char *dn, u32 events); void pfc_event(char *dn, u32 events); void app_event(char *dn, u32 subtype, u32 events); void llink_event(char *dn, u32 subtype, u32 events); #endif /* _DCB_EVENTS_H_ */ lldpad-0.9.46/include/dcb_persist_store.h000066400000000000000000000024751215142747300204010ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_PERSIST_STORE_H_ #define _DCB_PERSIST_STORE_H_ #include "lldp.h" cmd_status get_persistent(char *device_name, full_dcb_attribs *attribs); cmd_status set_persistent(char *device_name, full_dcb_attrib_ptrs *attribs); #endif /* _DCB_PERSIST_STORE_H_ */ lldpad-0.9.46/include/dcb_protocol.h000066400000000000000000000121571215142747300173330ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_PROTOCOL_H_ #define _DCB_PROTOCOL_H_ #include "lldp.h" #include "dcb_types.h" #include "lldpad_status.h" /* Feature State Machine Error definitions */ #define FEAT_ERR_NONE 0x00 /* no error */ #define FEAT_ERR_MISMATCH 0x01 /* feature mismatch with peer */ #define FEAT_ERR_CFG 0x02 /* error setting feature configuration */ #define FEAT_ERR_MULTI_TLV 0x04 /* multiple feature TLVs rcvd from peer */ #define FEAT_ERR_PEER 0x08 /* peer error */ #define FEAT_ERR_MULTI_PEER 0x10 /* multiple LLDP neighbors present */ #define FEAT_ERR_NO_TLV 0x20 /* feature not present in peer tlv */ /* Peer Error_Flag bits */ #define DUP_DCBX_TLV_CTRL 0x0001 #define DUP_DCBX_TLV_PG 0x0002 #define DUP_DCBX_TLV_PFC 0x0004 #define DUP_DCBX_TLV_APP 0x0010 #define DUP_DCBX_TLV_LLINK 0x0020 #define TOO_MANY_NGHBRS 0x0040 #define INIT_DCB_OUI {0x00,0x1b,0x21} int dcbx_add_adapter(char *device_name); int dcbx_remove_adapter(char *device_name); int dcbx_remove_all(void); bool init_dcb_support(char *device_name, full_dcb_attribs *attribs); cmd_status get_dcb_support(char *device_name, struct feature_support *dcb_capabilites); void remove_dcb_support(void); /* unique strings for default data storage */ #define DEF_CFG_STORE "default_cfg_attribs" /* Localization OK */ #define DEF_CFG_NUM 1 #define SUBTYPE_DEFAULT 0 bool add_pg_defaults(void); bool add_pfc_defaults(void); bool add_app_defaults(u32 subtype); void mark_pg_sent(char *device_name); void mark_pfc_sent(char *device_name); void mark_app_sent(char *device_name); bool add_llink_defaults(u32 subtype); void mark_llink_sent(char *device_name, u32 subtype); cmd_status get_control(char *device_name, control_protocol_attribs *control_data); cmd_status get_peer_control(char *device_name, control_protocol_attribs *control_data); cmd_status put_peer_control(char *, control_protocol_attribs *); cmd_status get_pg(char *device_name, pg_attribs *pg_data); cmd_status put_pg(char *device_name, pg_attribs *pg_data, pfc_attribs *pfc_data); cmd_status put_peer_pg(char *, pg_attribs *); cmd_status get_oper_pg(char *device_name, pg_attribs *pg_data); cmd_status get_peer_pg(char *device_name, pg_attribs *pg_data); cmd_status get_pfc(char *device_name, pfc_attribs *pfc_data); cmd_status put_pfc(char *device_name, pfc_attribs *pfc_data); cmd_status put_peer_pfc(char *, pfc_attribs *); cmd_status get_oper_pfc(char *device_name, pfc_attribs *pfc_data); cmd_status get_peer_pfc(char *device_name, pfc_attribs *pfc_data); cmd_status get_all_bwg_descrpts(char *device_name, pg_info *names); cmd_status get_bwg_descrpt(char *device_name, u8 bwgid, char **name); cmd_status put_bwg_descrpt(char *device_name, u8 bwgid, char *name); cmd_status get_app(char *device_name, u32 subtype, app_attribs *app_data); cmd_status put_app(char *device_name, u32 subtype, app_attribs *app_data); cmd_status put_peer_app(char *device_name, u32 subtype, app_attribs *); cmd_status get_oper_app(char *device_name, u32 subtype, app_attribs *app_data); cmd_status get_peer_app(char *device_name, u32 subtype, app_attribs *app_data); cmd_status get_llink(char *device_name, u32 subtype, llink_attribs *llink_data); cmd_status put_llink(char *device_name, u32 subtype, llink_attribs *llink_data); cmd_status put_peer_llink(char *, u32 subtype, llink_attribs *); cmd_status get_oper_llink(char *device_name, u32 subtype, llink_attribs *llink_data); cmd_status get_peer_llink(char *device_name, u32 subtype, llink_attribs *llink_data); cmd_status dcb_check_config(full_dcb_attrib_ptrs *attribs); void rebalance_uppcts(pg_attribs *pg); cmd_status run_feature_protocol(char *device_name, u32 EventFlag, u32 Subtype); cmd_status run_control_protocol(char *device_name, u32 EventFlag); cmd_status run_dcb_protocol(char *device_name, u32 EventFlag, u32 Subtype); cmd_status save_dcbx_state(const char *device_name); int set_dcbx_state(const char *device_name, dcbx_state *state); int get_dcbx_state(const char *device_name, dcbx_state *state); int clear_dcbx_state(); #endif /*_DCB_PROTOCOL_H_ */ lldpad-0.9.46/include/dcb_rule_chk.h000066400000000000000000000045201215142747300172610ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_RULE_CHK_H #define _DCB_RULE_CHK_H #include "lldp.h" #define MAX_USER_PRIORITY MAX_USER_PRIORITIES #define MAX_TRAFFIC_CLASS MAX_TRAFFIC_CLASSES #define MAX_BW_GROUP MAX_BANDWIDTH_GROUPS #define BW_PERCENT 100 /* DCB error Codes */ #define DCB_SUCCESS 0 #define DCB_ERR_CONFIG -1 #define DCB_ERR_PARAM -2 /* Trasmit and receive Errors */ /* Error in bandwidth group allocation */ #define DCB_TX_ERR_BW_GROUP -4 #define DCB_RX_ERR_BW_GROUP -5 /* Error in traffic class bandwidth allocation */ #define DCB_TX_ERR_TC_BW -6 #define DCB_RX_ERR_TC_BW -7 /* Traffic class has both link strict and group strict enabled */ #define DCB_TX_ERR_LS_GS -8 #define DCB_RX_ERR_LS_GS -9 /* Link strict traffic class has non zero bandwidth */ #define DCB_TX_ERR_LS_BW_NONZERO -0xA #define DCB_RX_ERR_LS_BW_NONZERO -0xB /* Link strict bandwidth group has non zero bandwidth */ #define DCB_TX_ERR_LS_BWG_NONZERO -0xC #define DCB_RX_ERR_LS_BWG_NONZERO -0xD /* Traffic calss has zero bandwidth */ #define DCB_TX_ERR_TC_BW_ZERO -0xE #define DCB_RX_ERR_TC_BW_ZERO -0xF #define DCB_NOT_IMPLEMENTED 0x7FFFFFFF #endif /* _DCB_RULE_CHK_H */ lldpad-0.9.46/include/dcb_types.h000066400000000000000000000177161215142747300166440ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCB_CLIENT_IF_TYPES_H_ #define _DCB_CLIENT_IF_TYPES_H_ #include #include #define MAX_USER_PRIORITIES 8 #define MAX_BANDWIDTH_GROUPS 8 #define MAX_TRAFFIC_CLASSES 8 #define MIN_TRAFFIC_CLASSES 3 #define SHORT_STRING 20 #define MAX_DESCRIPTION_LEN 100 #define LINK_STRICT_PGID 15 /* DCBX subtypes */ typedef enum { dcbx_subtype0 = 0, /* auto IEEE */ dcbx_subtype1 = 1, /* CIN */ dcbx_subtype2 = 2, /* CEE */ dcbx_force_subtype1 = 5,/* FORCE CIN */ dcbx_force_subtype2 = 6,/* FORCE CEE */ } dcbx_subtype; #define MASK_DCBX_FORCE 0x03 #define DCBX_FORCE_BIT 0x04 /* PFC configuration */ typedef enum { pfc_disabled = 0x000, pfc_enabled, pfc_invalid, } pfc_type; /* Peer DCB TLV States */ typedef enum { DCB_PEER_NONE = 0x000, DCB_PEER_PRESENT, DCB_PEER_EXPIRED, DCB_PEER_RESET, } peer_dcb_tlv_state; typedef pfc_type dcb_pfc_type; typedef enum { dcb_none = 0x0000, dcb_group, dcb_link, dcb_invalid, } dcb_strict_priority_type; typedef pfc_type dcb_pfc_list_type[MAX_USER_PRIORITIES]; typedef struct dcb_user_priority_attribs { __u8 pgid; __u8 bwgid; __u8 percent_of_pg_cap; dcb_strict_priority_type strict_priority; } dcb_user_priority_attribs_type; typedef struct dcb_traffic_attribs { __u8 pg_percent[MAX_BANDWIDTH_GROUPS]; /* percent of link */ dcb_user_priority_attribs_type up[MAX_USER_PRIORITIES]; } dcb_traffic_attribs_type; /* Define protocol and feature version that we support */ #define DCB_MAX_VERSION 0 #define DCB_PG_MAX_VERSION 0 #define DCB_PFC_MAX_VERSION 0 #define DCB_APPTLV_MAX_VERSION 0 #define DCB_LLINK_MAX_VERSION 0 #define DCB_START_SEQ_NUMBER 0 /* Definitions for dcb protocol event Flags. */ #define DCB_LOCAL_CHANGE_PG 0x00000001U #define DCB_REMOTE_CHANGE_PG 0x00000002U #define DCB_LOCAL_CHANGE_PFC 0x00000004U #define DCB_REMOTE_CHANGE_PFC 0x00000008U #define DCB_LOCAL_CHANGE_LLINK 0x00000010U #define DCB_REMOTE_CHANGE_LLINK 0x00000020U #define DCB_LOCAL_CHANGE_APP 0x00000040U #define DCB_REMOTE_CHANGE_APP 0x00000080U #define DCB_EVENT_FLAGS 0x00000FFFU /* There are multiple APP event flags, one for each subtype */ #define DCB_EVT_SUB(e, sub) ((e) << ((sub)*2)) #define DCB_LOCAL_CHANGE_APPTLV(sub) ((DCB_LOCAL_CHANGE_APP) << ((sub)*2)) #define DCB_REMOTE_CHANGE_APPTLV(sub) ((DCB_REMOTE_CHANGE_APP) << ((sub)*2)) #define DCB_SET_ALL_FLAGS(t) ({ \ int i, mask; \ \ mask = DCB_##t##_CHANGE_PG; \ mask |= DCB_##t##_CHANGE_PFC; \ mask |= DCB_##t##_CHANGE_LLINK; \ for (i = 0; i < DCB_MAX_APPTLV; i++) \ mask |= DCB_##t##_CHANGE_APPTLV(i);\ mask; \ }) /* DCB SubTypes */ #define DEFAULT_SUBTYPE 0 #define APP_STYPE_LEN 1 #define APP_FCOE_STYPE 0 #define APP_FCOE_STYPE_LEN 1 #define APP_FCOE_DEFAULT_DATA 0x08 /* user priority 3 */ #define APP_ISCSI_STYPE 1 #define APP_ISCSI_STYPE_LEN 1 #define APP_ISCSI_DEFAULT_DATA 0x10 #define APP_FIP_STYPE 2 #define APP_FIP_STYPE_LEN 1 #define APP_FIP_DEFAULT_DATA 0x08 /* no default FIP */ #define DCB_MAX_APPTLV 3 /* max APP TLV supported */ /* Link SubTypes */ #define LLINK_FCOE_STYPE 0 #define DCB_MAX_LLKTLV 1 /* max Link TLV supported */ /* Max TLV length */ #define DCB_MAX_TLV_LENGTH 507 /* Definitions for different data store. */ #define LOCAL_STORE 0x00000001 #define PEER_STORE 0x00000002 #define OPER_STORE 0x00000004 #define DATA_STORE_FLAGS 0x0000000F #define MAX_DEVICE_NAME_LEN 256 /* NDIS supports 256 */ #define MAC_ADDR_LEN 6 #define LLDP_RXPKT_LEN 2300 /* DCB Feature and control states */ #define DCB_NSTATES 3 #define DCB_CLOSED 0 /* closed */ #define DCB_INIT 1 /* Initialization */ #define DCB_LISTEN 2 /* listening for peer */ /* APP ETH TYPE */ #ifndef APP_FCOE_ETHTYPE #define APP_FCOE_ETHTYPE 0x8906 #endif #ifndef APP_FIP_ETHTYPE #define APP_FIP_ETHTYPE 0x8914 #endif /* APP PROTOCOL TYPES */ #define APP_ISCSI_PORT 3260 /* Flags */ #define DCB_SET_FLAGS(_FlagsVar, _BitsToSet) \ (_FlagsVar) = (_FlagsVar) | (_BitsToSet) #define DCB_TEST_FLAGS(_FlagsVar, _Mask, _BitsToCheck) \ (((_FlagsVar) & (_Mask)) == (_BitsToCheck)) /* Field Error_Flag for feature Oper_state_config */ typedef struct feature_protocol_attribs { bool Enable; bool Willing; bool Advertise; bool Advertise_prev; bool OperMode; bool PeerWilling; /* for local data */ bool Error; bool Syncd; __u32 Oper_version; __u32 Max_version; __u32 FeatureSeqNo; bool TLVPresent; /* for Peer data */ bool tlv_sent; /* for local config */ bool force_send; /* for local config */ __u8 dcbx_st; __u16 State; __u8 Error_Flag; /* bitmap of Oper and Peer errors */ } feature_protocol_attribs; typedef struct control_protocol_attribs { __u32 Oper_version; __u32 Max_version; __u32 SeqNo; __u32 AckNo; __u32 MyAckNo; peer_dcb_tlv_state RxDCBTLVState; /* for Peer data */ __u16 State; __u8 Error_Flag; /* bitmap */ } control_protocol_attribs; typedef struct pg_attribs { feature_protocol_attribs protocol; dcb_traffic_attribs_type tx; dcb_traffic_attribs_type rx; __u8 num_tcs; } pg_attribs; typedef struct pfc_attribs { feature_protocol_attribs protocol; dcb_pfc_list_type admin; __u8 num_tcs; } pfc_attribs; typedef char dcb_descript[MAX_DESCRIPTION_LEN]; typedef struct pg_info { __u8 max_pgid_desc; dcb_descript pgid_desc[MAX_BANDWIDTH_GROUPS]; } pg_info; typedef struct app_attribs { feature_protocol_attribs protocol; __u32 Length; __u8 AppData[DCB_MAX_TLV_LENGTH]; } app_attribs; typedef struct llink_cfg { __u8 llink_status; } llink_cfg; typedef struct llink_attribs { feature_protocol_attribs protocol; llink_cfg llink; } llink_attribs; typedef struct full_dcb_attribs { pg_attribs pg; pfc_attribs pfc; pg_info descript; app_attribs app[DCB_MAX_APPTLV]; llink_attribs llink[DCB_MAX_LLKTLV]; } full_dcb_attribs; typedef struct full_dcb_attrib_ptrs { pg_attribs *pg; pfc_attribs *pfc; pg_info *pgid; app_attribs *app; __u8 app_subtype; llink_attribs *llink; __u8 llink_subtype; } full_dcb_attrib_ptrs; typedef struct feature_support { /* non-zero indicates support */ __u8 pg; /* priority groups */ __u8 pfc; /* priority flow control */ /* non-zero indicates support */ __u8 up2tc_mappable; /* abilty to map priorities to traffic classes */ __u8 gsp; /* group strict priority */ /* Each bit represents a number of traffic classes supported by the hw. * If 4 or 8 traffic classes can be configured, then the value is 0x88. */ __u8 traffic_classes; __u8 pfc_traffic_classes; __u8 dcbx; } feature_support; typedef struct dcbx_state { __u32 SeqNo; __u32 AckNo; bool FCoEenable; bool iSCSIenable; bool FIPenable; } dcbx_state; #endif /* DCB_CLIENT_IF_TYPES_H_ */ lldpad-0.9.46/include/dcbtool.h000066400000000000000000000027621215142747300163110ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DCBTOOL_H #define _DCBTOOL_H int clif_command(struct clif *clif, char *cmd, int raw); void print_raw_message(char *msg, int print); int parse_print_message(char *msg, int print); void print_event_msg(char *buf); void print_response(char *buf, int status); int parse_response(char *buf); void print_dcb_cmd_response(char *buf, int status); int handle_dcb_cmds(struct clif *clif, int argc, char *argv[], int raw); #endif /* _DCBTOOL_H */ lldpad-0.9.46/include/eloop.h000066400000000000000000000310611215142747300157730ustar00rootroot00000000000000/* * Event loop * Copyright (c) 2002-2006, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * * This file defines an event loop interface that supports processing events * from registered timeouts (i.e., do something after N seconds), sockets * (e.g., a new packet available for reading), and signals. eloop.c is an * implementation of this interface using select() and sockets. This is * suitable for most UNIX/POSIX systems. When porting to other operating * systems, it may be necessary to replace that implementation with OS specific * mechanisms. */ #ifndef ELOOP_H #define ELOOP_H /** * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts */ #define ELOOP_ALL_CTX (void *) -1 /** * eloop_event_type - eloop socket event type for eloop_register_sock() * @EVENT_TYPE_READ: Socket has data available for reading * @EVENT_TYPE_WRITE: Socket has room for new data to be written * @EVENT_TYPE_EXCEPTION: An exception has been reported */ typedef enum { EVENT_TYPE_READ = 0, EVENT_TYPE_WRITE, EVENT_TYPE_EXCEPTION } eloop_event_type; /** * eloop_sock_handler - eloop socket event callback type * @sock: File descriptor number for the socket * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); /** * eloop_event_handler - eloop generic event callback type * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); /** * eloop_timeout_handler - eloop timeout event callback type * @eloop_ctx: Registered callback context data (eloop_data) * @sock_ctx: Registered callback context data (user_data) */ typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type * @sig: Signal number * @eloop_ctx: Registered callback context data (global user_data from * eloop_init() call) * @signal_ctx: Registered callback context data (user_data from * eloop_register_signal(), eloop_register_signal_terminate(), or * eloop_register_signal_reconfig() call) */ typedef void (*eloop_signal_handler)(int sig, void *eloop_ctx, void *signal_ctx); /** * eloop_init() - Initialize global event loop data * @user_data: Pointer to global data passed as eloop_ctx to signal handlers * Returns: 0 on success, -1 on failure * * This function must be called before any other eloop_* function. user_data * can be used to configure a global (to the process) pointer that will be * passed as eloop_ctx parameter to signal handlers. */ int eloop_init(void *user_data); /** * eloop_register_read_sock - Register handler for read events * @sock: File descriptor number for the socket * @handler: Callback function to be called when data is available for reading * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register a read socket notifier for the given file descriptor. The handler * function will be called whenever data is available for reading from the * socket. The handler function is responsible for clearing the event after * having processed it in order to avoid eloop from calling the handler again * for the same event. */ int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_read_sock - Unregister handler for read events * @sock: File descriptor number for the socket * * Unregister a read socket notifier that was previously registered with * eloop_register_read_sock(). */ void eloop_unregister_read_sock(int sock); /** * eloop_register_sock - Register handler for socket events * @sock: File descriptor number for the socket * @type: Type of event to wait for * @handler: Callback function to be called when the event is triggered * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register an event notifier for the given socket's file descriptor. The * handler function will be called whenever the that event is triggered for the * socket. The handler function is responsible for clearing the event after * having processed it in order to avoid eloop from calling the handler again * for the same event. */ int eloop_register_sock(int sock, eloop_event_type type, eloop_sock_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_sock - Unregister handler for socket events * @sock: File descriptor number for the socket * @type: Type of event for which sock was registered * * Unregister a socket event notifier that was previously registered with * eloop_register_sock(). */ void eloop_unregister_sock(int sock, eloop_event_type type); /** * eloop_register_event - Register handler for generic events * @event: Event to wait (eloop implementation specific) * @event_size: Size of event data * @handler: Callback function to be called when event is triggered * @eloop_data: Callback context data (eloop_data) * @user_data: Callback context data (user_data) * Returns: 0 on success, -1 on failure * * Register an event handler for the given event. This function is used to * register eloop implementation specific events which are mainly targetted for * operating system specific code (driver interface and l2_packet) since the * portable code will not be able to use such an OS-specific call. The handler * function will be called whenever the event is triggered. The handler * function is responsible for clearing the event after having processed it in * order to avoid eloop from calling the handler again for the same event. * * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, * and they would call this function with eloop_register_event(h, sizeof(h), * ...). */ int eloop_register_event(void *event, size_t event_size, eloop_event_handler handler, void *eloop_data, void *user_data); /** * eloop_unregister_event - Unregister handler for a generic event * @event: Event to cancel (eloop implementation specific) * @event_size: Size of event data * * Unregister a generic event notifier that was previously registered with * eloop_register_event(). */ void eloop_unregister_event(void *event, size_t event_size); /** * eloop_register_timeout - Register timeout * @secs: Number of seconds to the timeout * @usecs: Number of microseconds to the timeout * @handler: Callback function to be called when timeout occurs * @eloop_data: Callback context data (eloop_ctx) * @user_data: Callback context data (sock_ctx) * Returns: 0 on success, -1 on failure * * Register a timeout that will cause the handler function to be called after * given time. */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_cancel_timeout - Cancel timeouts * @handler: Matching callback function * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all * Returns: Number of cancelled timeouts * * Cancel matching timeouts registered with * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for * cancelling all timeouts regardless of eloop_data/user_data. */ int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data); /** * eloop_register_signal - Register handler for signals * @sig: Signal number (e.g., SIGHUP) * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a signal is received. * The callback function is actually called only after the system signal * handler has returned. This means that the normal limits for sighandlers * (i.e., only "safe functions" allowed) do not apply for the registered * callback. * * Signals are 'global' events and there is no local eloop_data pointer like * with other handlers. The global user_data pointer registered with * eloop_init() will be used as eloop_ctx for signal handlers. */ int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data); /** * eloop_register_signal_terminate - Register handler for terminate signals * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a process termination * signal is received. The callback function is actually called only after the * system signal handler has returned. This means that the normal limits for * sighandlers (i.e., only "safe functions" allowed) do not apply for the * registered callback. * * Signals are 'global' events and there is no local eloop_data pointer like * with other handlers. The global user_data pointer registered with * eloop_init() will be used as eloop_ctx for signal handlers. * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function * registers handlers for SIGINT and SIGTERM. */ int eloop_register_signal_terminate(eloop_signal_handler handler, void *user_data); /** * eloop_register_signal_reconfig - Register handler for reconfig signals * @handler: Callback function to be called when the signal is received * @user_data: Callback context data (signal_ctx) * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a reconfiguration / * hangup signal is received. The callback function is actually called only * after the system signal handler has returned. This means that the normal * limits for sighandlers (i.e., only "safe functions" allowed) do not apply * for the registered callback. * * Signals are 'global' events and there is no local eloop_data pointer like * with other handlers. The global user_data pointer registered with * eloop_init() will be used as eloop_ctx for signal handlers. * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function * registers a handler for SIGHUP. */ int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data); /** * eloop_run - Start the event loop * * Start the event loop and continue running as long as there are any * registered event handlers. This function is run after event loop has been * initialized with event_init() and one or more events have been registered. */ void eloop_run(void); /** * eloop_terminate - Terminate event loop * * Terminate event loop even if there are registered events. This can be used * to request the program to be terminated cleanly. */ void eloop_terminate(int sig, void *eloop_ctx, void *signal_ctx); /** * eloop_destroy - Free any resources allocated for the event loop * * After calling eloop_destroy(), other eloop_* functions must not be called * before re-running eloop_init(). */ void eloop_destroy(void); /** * eloop_terminated - Check whether event loop has been terminated * Returns: 1 = event loop terminate, 0 = event loop still running * * This function can be used to check whether eloop_terminate() has been called * to request termination of the event loop. This is normally used to abort * operations that may still be queued to be run when eloop_terminate() was * called. */ int eloop_terminated(void); /** * eloop_wait_for_read_sock - Wait for a single reader * @sock: File descriptor number for the socket * * Do a blocking wait for a single read socket. */ void eloop_wait_for_read_sock(int sock); /** * eloop_get_user_data - Get global user data * Returns: user_data pointer that was registered with eloop_init() */ void * eloop_get_user_data(void); #endif /* ELOOP_H */ lldpad-0.9.46/include/event_iface.h000066400000000000000000000026411215142747300171270ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _EVENT_IFACE_H_ #define _EVENT_IFACE_H_ int event_iface_init(void); int event_iface_init_user_space(void); int event_iface_deinit(void); int oper_add_device(char *device_name); void sendto_event_iface(char *buf, int len); /* index for event interface socket pair usage */ #define EVENT_IF_READ 0 #define EVENT_IF_WRITE 1 #endif /* _EVENT_IFACE_H_ */ lldpad-0.9.46/include/linux/000077500000000000000000000000001215142747300156425ustar00rootroot00000000000000lldpad-0.9.46/include/linux/dcbnl.h000066400000000000000000000511271215142747300171030ustar00rootroot00000000000000/* * Copyright (c) 2008-2011, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. * * Author: Lucy Liu */ #ifndef __LINUX_DCBNL_H__ #define __LINUX_DCBNL_H__ #include /* IEEE 802.1Qaz std supported values */ #define IEEE_8021QAZ_MAX_TCS 8 #define IEEE_8021QAZ_TSA_STRICT 0 #define IEEE_8021QAZ_TSA_CB_SHAPER 1 #define IEEE_8021QAZ_TSA_ETS 2 #define IEEE_8021QAZ_TSA_VENDOR 255 /* This structure contains the IEEE 802.1Qaz ETS managed object * * @willing: willing bit in ETS configuration TLV * @ets_cap: indicates supported capacity of ets feature * @cbs: credit based shaper ets algorithm supported * @tc_tx_bw: tc tx bandwidth indexed by traffic class * @tc_rx_bw: tc rx bandwidth indexed by traffic class * @tc_tsa: TSA Assignment table, indexed by traffic class * @prio_tc: priority assignment table mapping 8021Qp to traffic class * @tc_reco_bw: recommended tc bandwidth indexed by traffic class for TLV * @tc_reco_tsa: recommended tc bandwidth indexed by traffic class for TLV * @reco_prio_tc: recommended tc tx bandwidth indexed by traffic class for TLV * * Recommended values are used to set fields in the ETS recommendation TLV * with hardware offloaded LLDP. * * ---- * TSA Assignment 8 bit identifiers * 0 strict priority * 1 credit-based shaper * 2 enhanced transmission selection * 3-254 reserved * 255 vendor specific */ struct ieee_ets { __u8 willing; __u8 ets_cap; __u8 cbs; __u8 tc_tx_bw[IEEE_8021QAZ_MAX_TCS]; __u8 tc_rx_bw[IEEE_8021QAZ_MAX_TCS]; __u8 tc_tsa[IEEE_8021QAZ_MAX_TCS]; __u8 prio_tc[IEEE_8021QAZ_MAX_TCS]; __u8 tc_reco_bw[IEEE_8021QAZ_MAX_TCS]; __u8 tc_reco_tsa[IEEE_8021QAZ_MAX_TCS]; __u8 reco_prio_tc[IEEE_8021QAZ_MAX_TCS]; }; /* This structure contains the IEEE 802.1Qaz PFC managed object * * @pfc_cap: Indicates the number of traffic classes on the local device * that may simultaneously have PFC enabled. * @pfc_en: bitmap indicating pfc enabled traffic classes * @mbc: enable macsec bypass capability * @delay: the allowance made for a round-trip propagation delay of the * link in bits. * @requests: count of the sent pfc frames * @indications: count of the received pfc frames */ struct ieee_pfc { __u8 pfc_cap; __u8 pfc_en; __u8 mbc; __u16 delay; __u64 requests[IEEE_8021QAZ_MAX_TCS]; __u64 indications[IEEE_8021QAZ_MAX_TCS]; }; /* CEE DCBX std supported values */ #define CEE_DCBX_MAX_PGS 8 #define CEE_DCBX_MAX_PRIO 8 /** * struct cee_pg - CEE Priority-Group managed object * * @willing: willing bit in the PG tlv * @error: error bit in the PG tlv * @pg_en: enable bit of the PG feature * @tcs_supported: number of traffic classes supported * @pg_bw: bandwidth percentage for each priority group * @prio_pg: priority to PG mapping indexed by priority */ struct cee_pg { __u8 willing; __u8 error; __u8 pg_en; __u8 tcs_supported; __u8 pg_bw[CEE_DCBX_MAX_PGS]; __u8 prio_pg[CEE_DCBX_MAX_PGS]; }; /** * struct cee_pfc - CEE PFC managed object * * @willing: willing bit in the PFC tlv * @error: error bit in the PFC tlv * @pfc_en: bitmap indicating pfc enabled traffic classes * @tcs_supported: number of traffic classes supported */ struct cee_pfc { __u8 willing; __u8 error; __u8 pfc_en; __u8 tcs_supported; }; /* IEEE 802.1Qaz std supported values */ #define IEEE_8021QAZ_APP_SEL_ETHERTYPE 1 #define IEEE_8021QAZ_APP_SEL_STREAM 2 #define IEEE_8021QAZ_APP_SEL_DGRAM 3 #define IEEE_8021QAZ_APP_SEL_ANY 4 /* This structure contains the IEEE 802.1Qaz APP managed object. This * object is also used for the CEE std as well. There is no difference * between the objects. * * @selector: protocol identifier type * @protocol: protocol of type indicated * @priority: 3-bit unsigned integer indicating priority * * ---- * Selector field values * 0 Reserved * 1 Ethertype * 2 Well known port number over TCP or SCTP * 3 Well known port number over UDP or DCCP * 4 Well known port number over TCP, SCTP, UDP, or DCCP * 5-7 Reserved */ struct dcb_app { __u8 selector; __u8 priority; __u16 protocol; }; /** * struct dcb_peer_app_info - APP feature information sent by the peer * * @willing: willing bit in the peer APP tlv * @error: error bit in the peer APP tlv * * In addition to this information the full peer APP tlv also contains * a table of 'app_count' APP objects defined above. */ struct dcb_peer_app_info { __u8 willing; __u8 error; }; struct dcbmsg { __u8 dcb_family; __u8 cmd; __u16 dcb_pad; }; /** * enum dcbnl_commands - supported DCB commands * * @DCB_CMD_UNDEFINED: unspecified command to catch errors * @DCB_CMD_GSTATE: request the state of DCB in the device * @DCB_CMD_SSTATE: set the state of DCB in the device * @DCB_CMD_PGTX_GCFG: request the priority group configuration for Tx * @DCB_CMD_PGTX_SCFG: set the priority group configuration for Tx * @DCB_CMD_PGRX_GCFG: request the priority group configuration for Rx * @DCB_CMD_PGRX_SCFG: set the priority group configuration for Rx * @DCB_CMD_PFC_GCFG: request the priority flow control configuration * @DCB_CMD_PFC_SCFG: set the priority flow control configuration * @DCB_CMD_SET_ALL: apply all changes to the underlying device * @DCB_CMD_GPERM_HWADDR: get the permanent MAC address of the underlying * device. Only useful when using bonding. * @DCB_CMD_GCAP: request the DCB capabilities of the device * @DCB_CMD_GNUMTCS: get the number of traffic classes currently supported * @DCB_CMD_SNUMTCS: set the number of traffic classes * @DCB_CMD_GBCN: set backward congestion notification configuration * @DCB_CMD_SBCN: get backward congestion notification configration. * @DCB_CMD_GAPP: get application protocol configuration * @DCB_CMD_SAPP: set application protocol configuration * @DCB_CMD_IEEE_SET: set IEEE 802.1Qaz configuration * @DCB_CMD_IEEE_GET: get IEEE 802.1Qaz configuration * @DCB_CMD_GDCBX: get DCBX engine configuration * @DCB_CMD_SDCBX: set DCBX engine configuration * @DCB_CMD_GFEATCFG: get DCBX features flags * @DCB_CMD_SFEATCFG: set DCBX features negotiation flags * @DCB_CMD_CEE_GET: get CEE aggregated configuration * @DCB_CMD_IEEE_DEL: delete IEEE 802.1Qaz configuration */ enum dcbnl_commands { DCB_CMD_UNDEFINED, DCB_CMD_GSTATE, DCB_CMD_SSTATE, DCB_CMD_PGTX_GCFG, DCB_CMD_PGTX_SCFG, DCB_CMD_PGRX_GCFG, DCB_CMD_PGRX_SCFG, DCB_CMD_PFC_GCFG, DCB_CMD_PFC_SCFG, DCB_CMD_SET_ALL, DCB_CMD_GPERM_HWADDR, DCB_CMD_GCAP, DCB_CMD_GNUMTCS, DCB_CMD_SNUMTCS, DCB_CMD_PFC_GSTATE, DCB_CMD_PFC_SSTATE, DCB_CMD_BCN_GCFG, DCB_CMD_BCN_SCFG, DCB_CMD_GAPP, DCB_CMD_SAPP, DCB_CMD_IEEE_SET, DCB_CMD_IEEE_GET, DCB_CMD_GDCBX, DCB_CMD_SDCBX, DCB_CMD_GFEATCFG, DCB_CMD_SFEATCFG, DCB_CMD_CEE_GET, DCB_CMD_IEEE_DEL, __DCB_CMD_ENUM_MAX, DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1, }; /** * enum dcbnl_attrs - DCB top-level netlink attributes * * @DCB_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_ATTR_IFNAME: interface name of the underlying device (NLA_STRING) * @DCB_ATTR_STATE: enable state of DCB in the device (NLA_U8) * @DCB_ATTR_PFC_STATE: enable state of PFC in the device (NLA_U8) * @DCB_ATTR_PFC_CFG: priority flow control configuration (NLA_NESTED) * @DCB_ATTR_NUM_TC: number of traffic classes supported in the device (NLA_U8) * @DCB_ATTR_PG_CFG: priority group configuration (NLA_NESTED) * @DCB_ATTR_SET_ALL: bool to commit changes to hardware or not (NLA_U8) * @DCB_ATTR_PERM_HWADDR: MAC address of the physical device (NLA_NESTED) * @DCB_ATTR_CAP: DCB capabilities of the device (NLA_NESTED) * @DCB_ATTR_NUMTCS: number of traffic classes supported (NLA_NESTED) * @DCB_ATTR_BCN: backward congestion notification configuration (NLA_NESTED) * @DCB_ATTR_IEEE: IEEE 802.1Qaz supported attributes (NLA_NESTED) * @DCB_ATTR_DCBX: DCBX engine configuration in the device (NLA_U8) * @DCB_ATTR_FEATCFG: DCBX features flags (NLA_NESTED) * @DCB_ATTR_CEE: CEE std supported attributes (NLA_NESTED) */ enum dcbnl_attrs { DCB_ATTR_UNDEFINED, DCB_ATTR_IFNAME, DCB_ATTR_STATE, DCB_ATTR_PFC_STATE, DCB_ATTR_PFC_CFG, DCB_ATTR_NUM_TC, DCB_ATTR_PG_CFG, DCB_ATTR_SET_ALL, DCB_ATTR_PERM_HWADDR, DCB_ATTR_CAP, DCB_ATTR_NUMTCS, DCB_ATTR_BCN, DCB_ATTR_APP, /* IEEE std attributes */ DCB_ATTR_IEEE, DCB_ATTR_DCBX, DCB_ATTR_FEATCFG, /* CEE nested attributes */ DCB_ATTR_CEE, __DCB_ATTR_ENUM_MAX, DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1, }; /** * enum ieee_attrs - IEEE 802.1Qaz get/set attributes * * @DCB_ATTR_IEEE_UNSPEC: unspecified * @DCB_ATTR_IEEE_ETS: negotiated ETS configuration * @DCB_ATTR_IEEE_PFC: negotiated PFC configuration * @DCB_ATTR_IEEE_APP_TABLE: negotiated APP configuration * @DCB_ATTR_IEEE_PEER_ETS: peer ETS configuration - get only * @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only * @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only */ enum ieee_attrs { DCB_ATTR_IEEE_UNSPEC, DCB_ATTR_IEEE_ETS, DCB_ATTR_IEEE_PFC, DCB_ATTR_IEEE_APP_TABLE, DCB_ATTR_IEEE_PEER_ETS, DCB_ATTR_IEEE_PEER_PFC, DCB_ATTR_IEEE_PEER_APP, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) enum ieee_attrs_app { DCB_ATTR_IEEE_APP_UNSPEC, DCB_ATTR_IEEE_APP, __DCB_ATTR_IEEE_APP_MAX }; #define DCB_ATTR_IEEE_APP_MAX (__DCB_ATTR_IEEE_APP_MAX - 1) /** * enum cee_attrs - CEE DCBX get attributes * * @DCB_ATTR_CEE_UNSPEC: unspecified * @DCB_ATTR_CEE_PEER_PG: peer PG configuration - get only * @DCB_ATTR_CEE_PEER_PFC: peer PFC configuration - get only * @DCB_ATTR_CEE_PEER_APP: peer APP tlv - get only */ enum cee_attrs { DCB_ATTR_CEE_UNSPEC, DCB_ATTR_CEE_PEER_PG, DCB_ATTR_CEE_PEER_PFC, DCB_ATTR_CEE_PEER_APP_TABLE, __DCB_ATTR_CEE_MAX }; #define DCB_ATTR_CEE_MAX (__DCB_ATTR_CEE_MAX - 1) enum peer_app_attr { DCB_ATTR_CEE_PEER_APP_UNSPEC, DCB_ATTR_CEE_PEER_APP_INFO, DCB_ATTR_CEE_PEER_APP, __DCB_ATTR_CEE_PEER_APP_MAX }; #define DCB_ATTR_CEE_PEER_APP_MAX (__DCB_ATTR_CEE_PEER_APP_MAX - 1) /** * enum dcbnl_pfc_attrs - DCB Priority Flow Control user priority nested attrs * * @DCB_PFC_UP_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_PFC_UP_ATTR_0: Priority Flow Control value for User Priority 0 (NLA_U8) * @DCB_PFC_UP_ATTR_1: Priority Flow Control value for User Priority 1 (NLA_U8) * @DCB_PFC_UP_ATTR_2: Priority Flow Control value for User Priority 2 (NLA_U8) * @DCB_PFC_UP_ATTR_3: Priority Flow Control value for User Priority 3 (NLA_U8) * @DCB_PFC_UP_ATTR_4: Priority Flow Control value for User Priority 4 (NLA_U8) * @DCB_PFC_UP_ATTR_5: Priority Flow Control value for User Priority 5 (NLA_U8) * @DCB_PFC_UP_ATTR_6: Priority Flow Control value for User Priority 6 (NLA_U8) * @DCB_PFC_UP_ATTR_7: Priority Flow Control value for User Priority 7 (NLA_U8) * @DCB_PFC_UP_ATTR_MAX: highest attribute number currently defined * @DCB_PFC_UP_ATTR_ALL: apply to all priority flow control attrs (NLA_FLAG) * */ enum dcbnl_pfc_up_attrs { DCB_PFC_UP_ATTR_UNDEFINED, DCB_PFC_UP_ATTR_0, DCB_PFC_UP_ATTR_1, DCB_PFC_UP_ATTR_2, DCB_PFC_UP_ATTR_3, DCB_PFC_UP_ATTR_4, DCB_PFC_UP_ATTR_5, DCB_PFC_UP_ATTR_6, DCB_PFC_UP_ATTR_7, DCB_PFC_UP_ATTR_ALL, __DCB_PFC_UP_ATTR_ENUM_MAX, DCB_PFC_UP_ATTR_MAX = __DCB_PFC_UP_ATTR_ENUM_MAX - 1, }; /** * enum dcbnl_pg_attrs - DCB Priority Group attributes * * @DCB_PG_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_PG_ATTR_TC_0: Priority Group Traffic Class 0 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_1: Priority Group Traffic Class 1 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_2: Priority Group Traffic Class 2 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_3: Priority Group Traffic Class 3 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_4: Priority Group Traffic Class 4 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_5: Priority Group Traffic Class 5 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_6: Priority Group Traffic Class 6 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_7: Priority Group Traffic Class 7 configuration (NLA_NESTED) * @DCB_PG_ATTR_TC_MAX: highest attribute number currently defined * @DCB_PG_ATTR_TC_ALL: apply to all traffic classes (NLA_NESTED) * @DCB_PG_ATTR_BW_ID_0: Percent of link bandwidth for Priority Group 0 (NLA_U8) * @DCB_PG_ATTR_BW_ID_1: Percent of link bandwidth for Priority Group 1 (NLA_U8) * @DCB_PG_ATTR_BW_ID_2: Percent of link bandwidth for Priority Group 2 (NLA_U8) * @DCB_PG_ATTR_BW_ID_3: Percent of link bandwidth for Priority Group 3 (NLA_U8) * @DCB_PG_ATTR_BW_ID_4: Percent of link bandwidth for Priority Group 4 (NLA_U8) * @DCB_PG_ATTR_BW_ID_5: Percent of link bandwidth for Priority Group 5 (NLA_U8) * @DCB_PG_ATTR_BW_ID_6: Percent of link bandwidth for Priority Group 6 (NLA_U8) * @DCB_PG_ATTR_BW_ID_7: Percent of link bandwidth for Priority Group 7 (NLA_U8) * @DCB_PG_ATTR_BW_ID_MAX: highest attribute number currently defined * @DCB_PG_ATTR_BW_ID_ALL: apply to all priority groups (NLA_FLAG) * */ enum dcbnl_pg_attrs { DCB_PG_ATTR_UNDEFINED, DCB_PG_ATTR_TC_0, DCB_PG_ATTR_TC_1, DCB_PG_ATTR_TC_2, DCB_PG_ATTR_TC_3, DCB_PG_ATTR_TC_4, DCB_PG_ATTR_TC_5, DCB_PG_ATTR_TC_6, DCB_PG_ATTR_TC_7, DCB_PG_ATTR_TC_MAX, DCB_PG_ATTR_TC_ALL, DCB_PG_ATTR_BW_ID_0, DCB_PG_ATTR_BW_ID_1, DCB_PG_ATTR_BW_ID_2, DCB_PG_ATTR_BW_ID_3, DCB_PG_ATTR_BW_ID_4, DCB_PG_ATTR_BW_ID_5, DCB_PG_ATTR_BW_ID_6, DCB_PG_ATTR_BW_ID_7, DCB_PG_ATTR_BW_ID_MAX, DCB_PG_ATTR_BW_ID_ALL, __DCB_PG_ATTR_ENUM_MAX, DCB_PG_ATTR_MAX = __DCB_PG_ATTR_ENUM_MAX - 1, }; /** * enum dcbnl_tc_attrs - DCB Traffic Class attributes * * @DCB_TC_ATTR_PARAM_UNDEFINED: unspecified attribute to catch errors * @DCB_TC_ATTR_PARAM_PGID: (NLA_U8) Priority group the traffic class belongs to * Valid values are: 0-7 * @DCB_TC_ATTR_PARAM_UP_MAPPING: (NLA_U8) Traffic class to user priority map * Some devices may not support changing the * user priority map of a TC. * @DCB_TC_ATTR_PARAM_STRICT_PRIO: (NLA_U8) Strict priority setting * 0 - none * 1 - group strict * 2 - link strict * @DCB_TC_ATTR_PARAM_BW_PCT: optional - (NLA_U8) If supported by the device and * not configured to use link strict priority, * this is the percentage of bandwidth of the * priority group this traffic class belongs to * @DCB_TC_ATTR_PARAM_ALL: (NLA_FLAG) all traffic class parameters * */ enum dcbnl_tc_attrs { DCB_TC_ATTR_PARAM_UNDEFINED, DCB_TC_ATTR_PARAM_PGID, DCB_TC_ATTR_PARAM_UP_MAPPING, DCB_TC_ATTR_PARAM_STRICT_PRIO, DCB_TC_ATTR_PARAM_BW_PCT, DCB_TC_ATTR_PARAM_ALL, __DCB_TC_ATTR_PARAM_ENUM_MAX, DCB_TC_ATTR_PARAM_MAX = __DCB_TC_ATTR_PARAM_ENUM_MAX - 1, }; /** * enum dcbnl_cap_attrs - DCB Capability attributes * * @DCB_CAP_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_CAP_ATTR_ALL: (NLA_FLAG) all capability parameters * @DCB_CAP_ATTR_PG: (NLA_U8) device supports Priority Groups * @DCB_CAP_ATTR_PFC: (NLA_U8) device supports Priority Flow Control * @DCB_CAP_ATTR_UP2TC: (NLA_U8) device supports user priority to * traffic class mapping * @DCB_CAP_ATTR_PG_TCS: (NLA_U8) bitmap where each bit represents a * number of traffic classes the device * can be configured to use for Priority Groups * @DCB_CAP_ATTR_PFC_TCS: (NLA_U8) bitmap where each bit represents a * number of traffic classes the device can be * configured to use for Priority Flow Control * @DCB_CAP_ATTR_GSP: (NLA_U8) device supports group strict priority * @DCB_CAP_ATTR_BCN: (NLA_U8) device supports Backwards Congestion * Notification * @DCB_CAP_ATTR_DCBX: (NLA_U8) device supports DCBX engine * */ enum dcbnl_cap_attrs { DCB_CAP_ATTR_UNDEFINED, DCB_CAP_ATTR_ALL, DCB_CAP_ATTR_PG, DCB_CAP_ATTR_PFC, DCB_CAP_ATTR_UP2TC, DCB_CAP_ATTR_PG_TCS, DCB_CAP_ATTR_PFC_TCS, DCB_CAP_ATTR_GSP, DCB_CAP_ATTR_BCN, DCB_CAP_ATTR_DCBX, __DCB_CAP_ATTR_ENUM_MAX, DCB_CAP_ATTR_MAX = __DCB_CAP_ATTR_ENUM_MAX - 1, }; /** * DCBX capability flags * * @DCB_CAP_DCBX_HOST: DCBX negotiation is performed by the host LLDP agent. * 'set' routines are used to configure the device with * the negotiated parameters * * @DCB_CAP_DCBX_LLD_MANAGED: DCBX negotiation is not performed in the host but * by another entity * 'get' routines are used to retrieve the * negotiated parameters * 'set' routines can be used to set the initial * negotiation configuration * * @DCB_CAP_DCBX_VER_CEE: for a non-host DCBX engine, indicates the engine * supports the CEE protocol flavor * * @DCB_CAP_DCBX_VER_IEEE: for a non-host DCBX engine, indicates the engine * supports the IEEE protocol flavor * * @DCB_CAP_DCBX_STATIC: for a non-host DCBX engine, indicates the engine * supports static configuration (i.e no actual * negotiation is performed negotiated parameters equal * the initial configuration) * */ #define DCB_CAP_DCBX_HOST 0x01 #define DCB_CAP_DCBX_LLD_MANAGED 0x02 #define DCB_CAP_DCBX_VER_CEE 0x04 #define DCB_CAP_DCBX_VER_IEEE 0x08 #define DCB_CAP_DCBX_STATIC 0x10 /** * enum dcbnl_numtcs_attrs - number of traffic classes * * @DCB_NUMTCS_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_NUMTCS_ATTR_ALL: (NLA_FLAG) all traffic class attributes * @DCB_NUMTCS_ATTR_PG: (NLA_U8) number of traffic classes used for * priority groups * @DCB_NUMTCS_ATTR_PFC: (NLA_U8) number of traffic classes which can * support priority flow control */ enum dcbnl_numtcs_attrs { DCB_NUMTCS_ATTR_UNDEFINED, DCB_NUMTCS_ATTR_ALL, DCB_NUMTCS_ATTR_PG, DCB_NUMTCS_ATTR_PFC, __DCB_NUMTCS_ATTR_ENUM_MAX, DCB_NUMTCS_ATTR_MAX = __DCB_NUMTCS_ATTR_ENUM_MAX - 1, }; enum dcbnl_bcn_attrs{ DCB_BCN_ATTR_UNDEFINED = 0, DCB_BCN_ATTR_RP_0, DCB_BCN_ATTR_RP_1, DCB_BCN_ATTR_RP_2, DCB_BCN_ATTR_RP_3, DCB_BCN_ATTR_RP_4, DCB_BCN_ATTR_RP_5, DCB_BCN_ATTR_RP_6, DCB_BCN_ATTR_RP_7, DCB_BCN_ATTR_RP_ALL, DCB_BCN_ATTR_BCNA_0, DCB_BCN_ATTR_BCNA_1, DCB_BCN_ATTR_ALPHA, DCB_BCN_ATTR_BETA, DCB_BCN_ATTR_GD, DCB_BCN_ATTR_GI, DCB_BCN_ATTR_TMAX, DCB_BCN_ATTR_TD, DCB_BCN_ATTR_RMIN, DCB_BCN_ATTR_W, DCB_BCN_ATTR_RD, DCB_BCN_ATTR_RU, DCB_BCN_ATTR_WRTT, DCB_BCN_ATTR_RI, DCB_BCN_ATTR_C, DCB_BCN_ATTR_ALL, __DCB_BCN_ATTR_ENUM_MAX, DCB_BCN_ATTR_MAX = __DCB_BCN_ATTR_ENUM_MAX - 1, }; /** * enum dcb_general_attr_values - general DCB attribute values * * @DCB_ATTR_UNDEFINED: value used to indicate an attribute is not supported * */ enum dcb_general_attr_values { DCB_ATTR_VALUE_UNDEFINED = 0xff }; #define DCB_APP_IDTYPE_ETHTYPE 0x00 #define DCB_APP_IDTYPE_PORTNUM 0x01 enum dcbnl_app_attrs { DCB_APP_ATTR_UNDEFINED, DCB_APP_ATTR_IDTYPE, DCB_APP_ATTR_ID, DCB_APP_ATTR_PRIORITY, __DCB_APP_ATTR_ENUM_MAX, DCB_APP_ATTR_MAX = __DCB_APP_ATTR_ENUM_MAX - 1, }; /** * enum dcbnl_featcfg_attrs - features conifiguration flags * * @DCB_FEATCFG_ATTR_UNDEFINED: unspecified attribute to catch errors * @DCB_FEATCFG_ATTR_ALL: (NLA_FLAG) all features configuration attributes * @DCB_FEATCFG_ATTR_PG: (NLA_U8) configuration flags for priority groups * @DCB_FEATCFG_ATTR_PFC: (NLA_U8) configuration flags for priority * flow control * @DCB_FEATCFG_ATTR_APP: (NLA_U8) configuration flags for application TLV * */ #define DCB_FEATCFG_ERROR 0x01 /* error in feature resolution */ #define DCB_FEATCFG_ENABLE 0x02 /* enable feature */ #define DCB_FEATCFG_WILLING 0x04 /* feature is willing */ #define DCB_FEATCFG_ADVERTISE 0x08 /* advertise feature */ enum dcbnl_featcfg_attrs { DCB_FEATCFG_ATTR_UNDEFINED, DCB_FEATCFG_ATTR_ALL, DCB_FEATCFG_ATTR_PG, DCB_FEATCFG_ATTR_PFC, DCB_FEATCFG_ATTR_APP, __DCB_FEATCFG_ATTR_ENUM_MAX, DCB_FEATCFG_ATTR_MAX = __DCB_FEATCFG_ATTR_ENUM_MAX - 1, }; #endif /* __LINUX_DCBNL_H__ */ lldpad-0.9.46/include/linux/ethtool.h000066400000000000000000000524501215142747300174770ustar00rootroot00000000000000/* * ethtool.h: Defines for Linux ethtool. * * Copyright (C) 1998 David S. Miller (davem@redhat.com) * Copyright 2001 Jeff Garzik * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) * Portions Copyright 2002 Intel (eli.kupermann@intel.com, * christopher.leech@intel.com, * scott.feldman@intel.com) * Portions Copyright (C) Sun Microsystems 2008 */ #ifndef _LINUX_ETHTOOL_H #define _LINUX_ETHTOOL_H #include /* This should work for both 32 and 64 bit userland. */ struct ethtool_cmd { __u32 cmd; __u32 supported; /* Features this interface supports */ __u32 advertising; /* Features this interface advertises */ __u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ __u8 duplex; /* Duplex, half or full */ __u8 port; /* Which connector port */ __u8 phy_address; __u8 transceiver; /* Which transceiver to use */ __u8 autoneg; /* Enable or disable autonegotiation */ __u8 mdio_support; __u32 maxtxpkt; /* Tx pkts before generating tx int */ __u32 maxrxpkt; /* Rx pkts before generating rx int */ __u16 speed_hi; __u8 eth_tp_mdix; __u8 reserved2; __u32 lp_advertising; /* Features the link partner advertises */ __u32 reserved[2]; }; static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, __u32 speed) { ep->speed = (__u16)speed; ep->speed_hi = (__u16)(speed >> 16); } static inline __u32 ethtool_cmd_speed(struct ethtool_cmd *ep) { return (ep->speed_hi << 16) | ep->speed; } #define ETHTOOL_FWVERS_LEN 32 #define ETHTOOL_BUSINFO_LEN 32 /* these strings are set to whatever the driver author decides... */ struct ethtool_drvinfo { __u32 cmd; char driver[32]; /* driver short name, "tulip", "eepro100" */ char version[32]; /* driver version string */ char fw_version[ETHTOOL_FWVERS_LEN]; /* firmware version string */ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ /* For PCI devices, use pci_name(pci_dev). */ char reserved1[32]; char reserved2[12]; /* * Some struct members below are filled in * using ops->get_sset_count(). Obtaining * this info from ethtool_drvinfo is now * deprecated; Use ETHTOOL_GSSET_INFO * instead. */ __u32 n_priv_flags; /* number of flags valid in ETHTOOL_GPFLAGS */ __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ __u32 testinfo_len; __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ }; #define SOPASS_MAX 6 /* wake-on-lan settings */ struct ethtool_wolinfo { __u32 cmd; __u32 supported; __u32 wolopts; __u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ }; /* for passing single values */ struct ethtool_value { __u32 cmd; __u32 data; }; /* for passing big chunks of data */ struct ethtool_regs { __u32 cmd; __u32 version; /* driver-specific, indicates different chips/revs */ __u32 len; /* bytes */ __u8 data[0]; }; /* for passing EEPROM chunks */ struct ethtool_eeprom { __u32 cmd; __u32 magic; __u32 offset; /* in bytes */ __u32 len; /* in bytes */ __u8 data[0]; }; /* for configuring coalescing parameters of chip */ struct ethtool_coalesce { __u32 cmd; /* ETHTOOL_{G,S}COALESCE */ /* How many usecs to delay an RX interrupt after * a packet arrives. If 0, only rx_max_coalesced_frames * is used. */ __u32 rx_coalesce_usecs; /* How many packets to delay an RX interrupt after * a packet arrives. If 0, only rx_coalesce_usecs is * used. It is illegal to set both usecs and max frames * to zero as this would cause RX interrupts to never be * generated. */ __u32 rx_max_coalesced_frames; /* Same as above two parameters, except that these values * apply while an IRQ is being serviced by the host. Not * all cards support this feature and the values are ignored * in that case. */ __u32 rx_coalesce_usecs_irq; __u32 rx_max_coalesced_frames_irq; /* How many usecs to delay a TX interrupt after * a packet is sent. If 0, only tx_max_coalesced_frames * is used. */ __u32 tx_coalesce_usecs; /* How many packets to delay a TX interrupt after * a packet is sent. If 0, only tx_coalesce_usecs is * used. It is illegal to set both usecs and max frames * to zero as this would cause TX interrupts to never be * generated. */ __u32 tx_max_coalesced_frames; /* Same as above two parameters, except that these values * apply while an IRQ is being serviced by the host. Not * all cards support this feature and the values are ignored * in that case. */ __u32 tx_coalesce_usecs_irq; __u32 tx_max_coalesced_frames_irq; /* How many usecs to delay in-memory statistics * block updates. Some drivers do not have an in-memory * statistic block, and in such cases this value is ignored. * This value must not be zero. */ __u32 stats_block_coalesce_usecs; /* Adaptive RX/TX coalescing is an algorithm implemented by * some drivers to improve latency under low packet rates and * improve throughput under high packet rates. Some drivers * only implement one of RX or TX adaptive coalescing. Anything * not implemented by the driver causes these values to be * silently ignored. */ __u32 use_adaptive_rx_coalesce; __u32 use_adaptive_tx_coalesce; /* When the packet rate (measured in packets per second) * is below pkt_rate_low, the {rx,tx}_*_low parameters are * used. */ __u32 pkt_rate_low; __u32 rx_coalesce_usecs_low; __u32 rx_max_coalesced_frames_low; __u32 tx_coalesce_usecs_low; __u32 tx_max_coalesced_frames_low; /* When the packet rate is below pkt_rate_high but above * pkt_rate_low (both measured in packets per second) the * normal {rx,tx}_* coalescing parameters are used. */ /* When the packet rate is (measured in packets per second) * is above pkt_rate_high, the {rx,tx}_*_high parameters are * used. */ __u32 pkt_rate_high; __u32 rx_coalesce_usecs_high; __u32 rx_max_coalesced_frames_high; __u32 tx_coalesce_usecs_high; __u32 tx_max_coalesced_frames_high; /* How often to do adaptive coalescing packet rate sampling, * measured in seconds. Must not be zero. */ __u32 rate_sample_interval; }; /* for configuring RX/TX ring parameters */ struct ethtool_ringparam { __u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ /* Read only attributes. These indicate the maximum number * of pending RX/TX ring entries the driver will allow the * user to set. */ __u32 rx_max_pending; __u32 rx_mini_max_pending; __u32 rx_jumbo_max_pending; __u32 tx_max_pending; /* Values changeable by the user. The valid values are * in the range 1 to the "*_max_pending" counterpart above. */ __u32 rx_pending; __u32 rx_mini_pending; __u32 rx_jumbo_pending; __u32 tx_pending; }; /* for configuring link flow control parameters */ struct ethtool_pauseparam { __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ /* If the link is being auto-negotiated (via ethtool_cmd.autoneg * being true) the user may set 'autonet' here non-zero to have the * pause parameters be auto-negotiated too. In such a case, the * {rx,tx}_pause values below determine what capabilities are * advertised. * * If 'autoneg' is zero or the link is not being auto-negotiated, * then {rx,tx}_pause force the driver to use/not-use pause * flow control. */ __u32 autoneg; __u32 rx_pause; __u32 tx_pause; }; #define ETH_GSTRING_LEN 32 enum ethtool_stringset { ETH_SS_TEST = 0, ETH_SS_STATS, ETH_SS_PRIV_FLAGS, ETH_SS_NTUPLE_FILTERS, }; /* for passing string sets for data tagging */ struct ethtool_gstrings { __u32 cmd; /* ETHTOOL_GSTRINGS */ __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ __u32 len; /* number of strings in the string set */ __u8 data[0]; }; struct ethtool_sset_info { __u32 cmd; /* ETHTOOL_GSSET_INFO */ __u32 reserved; __u64 sset_mask; /* input: each bit selects an sset to query */ /* output: each bit a returned sset */ __u32 data[0]; /* ETH_SS_xxx count, in order, based on bits in sset_mask. One bit implies one __u32, two bits implies two __u32's, etc. */ }; enum ethtool_test_flags { ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */ ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */ }; /* for requesting NIC test and getting results*/ struct ethtool_test { __u32 cmd; /* ETHTOOL_TEST */ __u32 flags; /* ETH_TEST_FL_xxx */ __u32 reserved; __u32 len; /* result length, in number of u64 elements */ __u64 data[0]; }; /* for dumping NIC-specific statistics */ struct ethtool_stats { __u32 cmd; /* ETHTOOL_GSTATS */ __u32 n_stats; /* number of u64's being returned */ __u64 data[0]; }; struct ethtool_perm_addr { __u32 cmd; /* ETHTOOL_GPERMADDR */ __u32 size; __u8 data[0]; }; /* boolean flags controlling per-interface behavior characteristics. * When reading, the flag indicates whether or not a certain behavior * is enabled/present. When writing, the flag indicates whether * or not the driver should turn on (set) or off (clear) a behavior. * * Some behaviors may read-only (unconditionally absent or present). * If such is the case, return EINVAL in the set-flags operation if the * flag differs from the read-only value. */ enum ethtool_flags { ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ ETH_FLAG_NTUPLE = (1 << 27), /* N-tuple filters enabled */ ETH_FLAG_RXHASH = (1 << 28), }; /* The following structures are for supporting RX network flow * classification configuration. Note, all multibyte fields, e.g., * ip4src, ip4dst, psrc, pdst, spi, etc. are expected to be in network * byte order. */ struct ethtool_tcpip4_spec { __be32 ip4src; __be32 ip4dst; __be16 psrc; __be16 pdst; __u8 tos; }; struct ethtool_ah_espip4_spec { __be32 ip4src; __be32 ip4dst; __be32 spi; __u8 tos; }; struct ethtool_rawip4_spec { __be32 ip4src; __be32 ip4dst; __u8 hdata[64]; }; struct ethtool_ether_spec { __be16 ether_type; __u8 frame_size; __u8 eframe[16]; }; #define ETH_RX_NFC_IP4 1 #define ETH_RX_NFC_IP6 2 struct ethtool_usrip4_spec { __be32 ip4src; __be32 ip4dst; __be32 l4_4_bytes; __u8 tos; __u8 ip_ver; __u8 proto; }; struct ethtool_rx_flow_spec { __u32 flow_type; union { struct ethtool_tcpip4_spec tcp_ip4_spec; struct ethtool_tcpip4_spec udp_ip4_spec; struct ethtool_tcpip4_spec sctp_ip4_spec; struct ethtool_ah_espip4_spec ah_ip4_spec; struct ethtool_ah_espip4_spec esp_ip4_spec; struct ethtool_rawip4_spec raw_ip4_spec; struct ethtool_ether_spec ether_spec; struct ethtool_usrip4_spec usr_ip4_spec; __u8 hdata[64]; } h_u, m_u; /* entry, mask */ __u64 ring_cookie; __u32 location; }; struct ethtool_rxnfc { __u32 cmd; __u32 flow_type; /* The rx flow hash value or the rule DB size */ __u64 data; /* The following fields are not valid and must not be used for * the ETHTOOL_{G,X}RXFH commands. */ struct ethtool_rx_flow_spec fs; __u32 rule_cnt; __u32 rule_locs[0]; }; struct ethtool_rxfh_indir { __u32 cmd; /* On entry, this is the array size of the user buffer. On * return from ETHTOOL_GRXFHINDIR, this is the array size of * the hardware indirection table. */ __u32 size; __u32 ring_index[0]; /* ring/queue index for each hash value */ }; struct ethtool_rx_ntuple_flow_spec { __u32 flow_type; union { struct ethtool_tcpip4_spec tcp_ip4_spec; struct ethtool_tcpip4_spec udp_ip4_spec; struct ethtool_tcpip4_spec sctp_ip4_spec; struct ethtool_ah_espip4_spec ah_ip4_spec; struct ethtool_ah_espip4_spec esp_ip4_spec; struct ethtool_rawip4_spec raw_ip4_spec; struct ethtool_ether_spec ether_spec; struct ethtool_usrip4_spec usr_ip4_spec; __u8 hdata[64]; } h_u, m_u; /* entry, mask */ __u16 vlan_tag; __u16 vlan_tag_mask; __u64 data; /* user-defined flow spec data */ __u64 data_mask; /* user-defined flow spec mask */ /* signed to distinguish between queue and actions (DROP) */ __s32 action; #define ETHTOOL_RXNTUPLE_ACTION_DROP -1 }; struct ethtool_rx_ntuple { __u32 cmd; struct ethtool_rx_ntuple_flow_spec fs; }; #define ETHTOOL_FLASH_MAX_FILENAME 128 enum ethtool_flash_op_type { ETHTOOL_FLASH_ALL_REGIONS = 0, }; /* for passing firmware flashing related parameters */ struct ethtool_flash { __u32 cmd; __u32 region; char data[ETHTOOL_FLASH_MAX_FILENAME]; }; /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* Get settings. */ #define ETHTOOL_SSET 0x00000002 /* Set settings. */ #define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ #define ETHTOOL_GREGS 0x00000004 /* Get NIC registers. */ #define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ #define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options. */ #define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ #define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */ #define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation. */ #define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */ #define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ #define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */ #define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ #define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ #define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ #define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters. */ #define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ #define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ #define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ #define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ #define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ #define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ #define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable * (ethtool_value) */ #define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable * (ethtool_value). */ #define ETHTOOL_TEST 0x0000001a /* execute NIC self-test. */ #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ #define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ #define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ #define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ #define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ #define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ #define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ #define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ #define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ #define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ #define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ #define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ #define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ #define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */ #define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */ #define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ #define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ #define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ #define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ #define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ #define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ #define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ #define ETHTOOL_RESET 0x00000034 /* Reset hardware */ #define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ #define ETHTOOL_GRXNTUPLE 0x00000036 /* Get n-tuple filters from device */ #define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ #define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ #define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET /* Indicates what features are supported by the interface. */ #define SUPPORTED_10baseT_Half (1 << 0) #define SUPPORTED_10baseT_Full (1 << 1) #define SUPPORTED_100baseT_Half (1 << 2) #define SUPPORTED_100baseT_Full (1 << 3) #define SUPPORTED_1000baseT_Half (1 << 4) #define SUPPORTED_1000baseT_Full (1 << 5) #define SUPPORTED_Autoneg (1 << 6) #define SUPPORTED_TP (1 << 7) #define SUPPORTED_AUI (1 << 8) #define SUPPORTED_MII (1 << 9) #define SUPPORTED_FIBRE (1 << 10) #define SUPPORTED_BNC (1 << 11) #define SUPPORTED_10000baseT_Full (1 << 12) #define SUPPORTED_Pause (1 << 13) #define SUPPORTED_Asym_Pause (1 << 14) #define SUPPORTED_2500baseX_Full (1 << 15) #define SUPPORTED_Backplane (1 << 16) #define SUPPORTED_1000baseKX_Full (1 << 17) #define SUPPORTED_10000baseKX4_Full (1 << 18) #define SUPPORTED_10000baseKR_Full (1 << 19) #define SUPPORTED_10000baseR_FEC (1 << 20) /* Indicates what features are advertised by the interface. */ #define ADVERTISED_10baseT_Half (1 << 0) #define ADVERTISED_10baseT_Full (1 << 1) #define ADVERTISED_100baseT_Half (1 << 2) #define ADVERTISED_100baseT_Full (1 << 3) #define ADVERTISED_1000baseT_Half (1 << 4) #define ADVERTISED_1000baseT_Full (1 << 5) #define ADVERTISED_Autoneg (1 << 6) #define ADVERTISED_TP (1 << 7) #define ADVERTISED_AUI (1 << 8) #define ADVERTISED_MII (1 << 9) #define ADVERTISED_FIBRE (1 << 10) #define ADVERTISED_BNC (1 << 11) #define ADVERTISED_10000baseT_Full (1 << 12) #define ADVERTISED_Pause (1 << 13) #define ADVERTISED_Asym_Pause (1 << 14) #define ADVERTISED_2500baseX_Full (1 << 15) #define ADVERTISED_Backplane (1 << 16) #define ADVERTISED_1000baseKX_Full (1 << 17) #define ADVERTISED_10000baseKX4_Full (1 << 18) #define ADVERTISED_10000baseKR_Full (1 << 19) #define ADVERTISED_10000baseR_FEC (1 << 20) /* The following are all involved in forcing a particular link * mode for the device for setting things. When getting the * devices settings, these indicate the current mode and whether * it was foced up into this mode or autonegotiated. */ /* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 #define SPEED_2500 2500 #define SPEED_10000 10000 /* Duplex, half or full. */ #define DUPLEX_HALF 0x00 #define DUPLEX_FULL 0x01 /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 #define PORT_MII 0x02 #define PORT_FIBRE 0x03 #define PORT_BNC 0x04 #define PORT_DA 0x05 #define PORT_NONE 0xef #define PORT_OTHER 0xff /* Which transceiver to use. */ #define XCVR_INTERNAL 0x00 #define XCVR_EXTERNAL 0x01 #define XCVR_DUMMY1 0x02 #define XCVR_DUMMY2 0x03 #define XCVR_DUMMY3 0x04 /* Enable or disable autonegotiation. If this is set to enable, * the forced link modes above are completely ignored. */ #define AUTONEG_DISABLE 0x00 #define AUTONEG_ENABLE 0x01 /* Mode MDI or MDI-X */ #define ETH_TP_MDI_INVALID 0x00 #define ETH_TP_MDI 0x01 #define ETH_TP_MDI_X 0x02 /* Wake-On-Lan options. */ #define WAKE_PHY (1 << 0) #define WAKE_UCAST (1 << 1) #define WAKE_MCAST (1 << 2) #define WAKE_BCAST (1 << 3) #define WAKE_ARP (1 << 4) #define WAKE_MAGIC (1 << 5) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ /* L3-L4 network traffic flow types */ #define TCP_V4_FLOW 0x01 #define UDP_V4_FLOW 0x02 #define SCTP_V4_FLOW 0x03 #define AH_ESP_V4_FLOW 0x04 #define TCP_V6_FLOW 0x05 #define UDP_V6_FLOW 0x06 #define SCTP_V6_FLOW 0x07 #define AH_ESP_V6_FLOW 0x08 #define AH_V4_FLOW 0x09 #define ESP_V4_FLOW 0x0a #define AH_V6_FLOW 0x0b #define ESP_V6_FLOW 0x0c #define IP_USER_FLOW 0x0d #define IPV4_FLOW 0x10 #define IPV6_FLOW 0x11 /* L3-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) #define RXH_VLAN (1 << 2) #define RXH_L3_PROTO (1 << 3) #define RXH_IP_SRC (1 << 4) #define RXH_IP_DST (1 << 5) #define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ #define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ #define RXH_DISCARD (1 << 31) #define RX_CLS_FLOW_DISC 0xffffffffffffffffULL /* Reset flags */ /* The reset() operation must clear the flags for the components which * were actually reset. On successful return, the flags indicate the * components which were not reset, either because they do not exist * in the hardware or because they cannot be reset independently. The * driver must never reset any components that were not requested. */ enum ethtool_reset_flags { /* These flags represent components dedicated to the interface * the command is addressed to. Shift any flag left by * ETH_RESET_SHARED_SHIFT to reset a shared component of the * same type. */ ETH_RESET_MGMT = 1 << 0, /* Management processor */ ETH_RESET_IRQ = 1 << 1, /* Interrupt requester */ ETH_RESET_DMA = 1 << 2, /* DMA engine */ ETH_RESET_FILTER = 1 << 3, /* Filtering/flow direction */ ETH_RESET_OFFLOAD = 1 << 4, /* Protocol offload */ ETH_RESET_MAC = 1 << 5, /* Media access controller */ ETH_RESET_PHY = 1 << 6, /* Transceiver/PHY */ ETH_RESET_RAM = 1 << 7, /* RAM shared between * multiple components */ ETH_RESET_DEDICATED = 0x0000ffff, /* All components dedicated to * this interface */ ETH_RESET_ALL = 0xffffffff, /* All components used by this * interface, even if shared */ }; #define ETH_RESET_SHARED_SHIFT 16 #endif /* _LINUX_ETHTOOL_H */ lldpad-0.9.46/include/linux/if.h000066400000000000000000000177561215142747300164310ustar00rootroot00000000000000/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * Global definitions for the INET interface module. * * Version: @(#)if.h 1.0.2 04/18/93 * * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 * Ross Biro * Fred N. van Kempen, * * 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. */ #ifndef _LINUX_IF_H #define _LINUX_IF_H #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ #define IFNAMSIZ 16 #define IFALIASZ 256 #include /* Standard interface flags (netdevice->flags). */ #define IFF_UP 0x1 /* interface is up */ #define IFF_BROADCAST 0x2 /* broadcast address valid */ #define IFF_DEBUG 0x4 /* turn on debugging */ #define IFF_LOOPBACK 0x8 /* is a loopback net */ #define IFF_POINTOPOINT 0x10 /* interface is has p-p link */ #define IFF_NOTRAILERS 0x20 /* avoid use of trailers */ #define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */ #define IFF_NOARP 0x80 /* no ARP protocol */ #define IFF_PROMISC 0x100 /* receive all packets */ #define IFF_ALLMULTI 0x200 /* receive all multicast packets*/ #define IFF_MASTER 0x400 /* master of a load balancer */ #define IFF_SLAVE 0x800 /* slave of a load balancer */ #define IFF_MULTICAST 0x1000 /* Supports multicast */ #define IFF_PORTSEL 0x2000 /* can set media type */ #define IFF_AUTOMEDIA 0x4000 /* auto media select active */ #define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/ #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ #define IFF_DORMANT 0x20000 /* driver signals dormant */ #define IFF_ECHO 0x40000 /* echo sent packets */ #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) /* Private (from user) interface flags (netdevice->priv_flags). */ #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ #define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */ #define IFF_SLAVE_INACTIVE 0x4 /* bonding slave not the curr. active */ #define IFF_MASTER_8023AD 0x8 /* bonding master, 802.3ad. */ #define IFF_MASTER_ALB 0x10 /* bonding master, balance-alb. */ #define IFF_BONDING 0x20 /* bonding master or slave */ #define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */ #define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */ #define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */ #define IFF_WAN_HDLC 0x200 /* WAN HDLC device */ #define IFF_XMIT_DST_RELEASE 0x400 /* dev_hard_start_xmit() is allowed to * release skb->dst */ #define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */ #define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */ #define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */ #define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */ #define IFF_BRIDGE_PORT 0x8000 /* device used as bridge port */ #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 /* For definitions see hdlc.h */ #define IF_IFACE_V35 0x1000 /* V.35 serial interface */ #define IF_IFACE_V24 0x1001 /* V.24 serial interface */ #define IF_IFACE_X21 0x1002 /* X.21 serial interface */ #define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ #define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ #define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ #define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ /* For definitions see hdlc.h */ #define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ #define IF_PROTO_PPP 0x2001 /* PPP protocol */ #define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ #define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ #define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ #define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ #define IF_PROTO_X25 0x2006 /* X.25 */ #define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ #define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ #define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ #define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ #define IF_PROTO_FR_ETH_PVC 0x200B #define IF_PROTO_RAW 0x200C /* RAW Socket */ /* RFC 2863 operational status */ enum { IF_OPER_UNKNOWN, IF_OPER_NOTPRESENT, IF_OPER_DOWN, IF_OPER_LOWERLAYERDOWN, IF_OPER_TESTING, IF_OPER_DORMANT, IF_OPER_UP, }; /* link modes */ enum { IF_LINK_MODE_DEFAULT, IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ }; /* * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments * for driver options and along come the PCMCIA people 8) * * Ah well. The get() side of this is good for WDSETUP, and it'll * be handy for debugging things. The set side is fine for now and * being very small might be worth keeping for clean configuration. */ struct ifmap { unsigned long mem_start; unsigned long mem_end; unsigned short base_addr; unsigned char irq; unsigned char dma; unsigned char port; /* 3 bytes spare */ }; struct if_settings { unsigned int type; /* Type of physical device or protocol */ unsigned int size; /* Size of the data allocated by the caller */ union { /* {atm/eth/dsl}_settings anyone ? */ raw_hdlc_proto *raw_hdlc; cisco_proto *cisco; fr_proto *fr; fr_proto_pvc *fr_pvc; fr_proto_pvc_info *fr_pvc_info; /* interface settings */ sync_serial_settings *sync; te1_settings *te1; } ifs_ifsu; }; /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The * remainder may be interface specific. */ struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; void * ifru_data; struct if_settings ifru_settings; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ #define ifr_flags ifr_ifru.ifru_flags /* flags */ #define ifr_metric ifr_ifru.ifru_ivalue /* metric */ #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ #define ifr_map ifr_ifru.ifru_map /* device map */ #define ifr_slave ifr_ifru.ifru_slave /* slave device */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ #define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ /* * Structure used in SIOCGIFCONF request. * Used to retrieve interface configuration * for machine (useful for programs which * must know all networks accessible). */ struct ifconf { int ifc_len; /* size of buffer */ union { char *ifcu_buf; struct ifreq *ifcu_req; } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures */ #endif /* _LINUX_IF_H */ lldpad-0.9.46/include/linux/if_bonding.h000066400000000000000000000076271215142747300201250ustar00rootroot00000000000000/* * Bond several ethernet interfaces into a Cisco, running 'Etherchannel'. * * * Portions are (c) Copyright 1995 Simon "Guru Aleph-Null" Janes * NCM: Network and Communications Management, Inc. * * BUT, I'm the one who modified it for ethernet, so: * (c) Copyright 1999, Thomas Davis, tadavis@lbl.gov * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * * 2003/03/18 - Amir Noam * - Added support for getting slave's speed and duplex via ethtool. * Needed for 802.3ad and other future modes. * * 2003/03/18 - Tsippy Mendelson and * Shmulik Hen * - Enable support of modes that need to use the unique mac address of * each slave. * * 2003/03/18 - Tsippy Mendelson and * Amir Noam * - Moved driver's private data types to bonding.h * * 2003/03/18 - Amir Noam , * Tsippy Mendelson and * Shmulik Hen * - Added support for IEEE 802.3ad Dynamic link aggregation mode. * * 2003/05/01 - Amir Noam * - Added ABI version control to restore compatibility between * new/old ifenslave and new/old bonding. * * 2003/12/01 - Shmulik Hen * - Code cleanup and style changes * * 2005/05/05 - Jason Gabler * - added definitions for various XOR hashing policies */ #ifndef _LINUX_IF_BONDING_H #define _LINUX_IF_BONDING_H #include #include #include /* userland - kernel ABI version (2003/05/08) */ #define BOND_ABI_VERSION 2 /* * We can remove these ioctl definitions in 2.5. People should use the * SIOC*** versions of them instead */ #define BOND_ENSLAVE_OLD (SIOCDEVPRIVATE) #define BOND_RELEASE_OLD (SIOCDEVPRIVATE + 1) #define BOND_SETHWADDR_OLD (SIOCDEVPRIVATE + 2) #define BOND_SLAVE_INFO_QUERY_OLD (SIOCDEVPRIVATE + 11) #define BOND_INFO_QUERY_OLD (SIOCDEVPRIVATE + 12) #define BOND_CHANGE_ACTIVE_OLD (SIOCDEVPRIVATE + 13) #define BOND_CHECK_MII_STATUS (SIOCGMIIPHY) #define BOND_MODE_ROUNDROBIN 0 #define BOND_MODE_ACTIVEBACKUP 1 #define BOND_MODE_XOR 2 #define BOND_MODE_BROADCAST 3 #define BOND_MODE_8023AD 4 #define BOND_MODE_TLB 5 #define BOND_MODE_ALB 6 /* TLB + RLB (receive load balancing) */ /* each slave's link has 4 states */ #define BOND_LINK_UP 0 /* link is up and running */ #define BOND_LINK_FAIL 1 /* link has just gone down */ #define BOND_LINK_DOWN 2 /* link has been down for too long time */ #define BOND_LINK_BACK 3 /* link is going back */ /* each slave has several states */ #define BOND_STATE_ACTIVE 0 /* link is active */ #define BOND_STATE_BACKUP 1 /* link is backup */ #define BOND_DEFAULT_MAX_BONDS 1 /* Default maximum number of devices to support */ #define BOND_DEFAULT_TX_QUEUES 16 /* Default number of tx queues per device */ /* hashing types */ #define BOND_XMIT_POLICY_LAYER2 0 /* layer 2 (MAC only), default */ #define BOND_XMIT_POLICY_LAYER34 1 /* layer 3+4 (IP ^ (TCP || UDP)) */ #define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */ typedef struct ifbond { __s32 bond_mode; __s32 num_slaves; __s32 miimon; } ifbond; typedef struct ifslave { __s32 slave_id; /* Used as an IN param to the BOND_SLAVE_INFO_QUERY ioctl */ char slave_name[IFNAMSIZ]; __s8 link; __s8 state; __u32 link_failure_count; } ifslave; struct ad_info { __u16 aggregator_id; __u16 ports; __u16 actor_key; __u16 partner_key; __u8 partner_system[ETH_ALEN]; }; #endif /* _LINUX_IF_BONDING_H */ /* * Local variables: * version-control: t * kept-new-versions: 5 * c-indent-level: 8 * c-basic-offset: 8 * tab-width: 8 * End: */ lldpad-0.9.46/include/linux/if_bridge.h000066400000000000000000000044301215142747300177260ustar00rootroot00000000000000/* * Linux ethernet bridge * * Authors: * Lennert Buytenhek * * 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. */ #ifndef _LINUX_IF_BRIDGE_H #define _LINUX_IF_BRIDGE_H #include #define SYSFS_BRIDGE_ATTR "bridge" #define SYSFS_BRIDGE_FDB "brforward" #define SYSFS_BRIDGE_PORT_SUBDIR "brif" #define SYSFS_BRIDGE_PORT_ATTR "brport" #define SYSFS_BRIDGE_PORT_LINK "bridge" #define BRCTL_VERSION 1 #define BRCTL_GET_VERSION 0 #define BRCTL_GET_BRIDGES 1 #define BRCTL_ADD_BRIDGE 2 #define BRCTL_DEL_BRIDGE 3 #define BRCTL_ADD_IF 4 #define BRCTL_DEL_IF 5 #define BRCTL_GET_BRIDGE_INFO 6 #define BRCTL_GET_PORT_LIST 7 #define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 #define BRCTL_SET_BRIDGE_HELLO_TIME 9 #define BRCTL_SET_BRIDGE_MAX_AGE 10 #define BRCTL_SET_AGEING_TIME 11 #define BRCTL_SET_GC_INTERVAL 12 #define BRCTL_GET_PORT_INFO 13 #define BRCTL_SET_BRIDGE_STP_STATE 14 #define BRCTL_SET_BRIDGE_PRIORITY 15 #define BRCTL_SET_PORT_PRIORITY 16 #define BRCTL_SET_PATH_COST 17 #define BRCTL_GET_FDB_ENTRIES 18 #define BR_STATE_DISABLED 0 #define BR_STATE_LISTENING 1 #define BR_STATE_LEARNING 2 #define BR_STATE_FORWARDING 3 #define BR_STATE_BLOCKING 4 struct __bridge_info { __u64 designated_root; __u64 bridge_id; __u32 root_path_cost; __u32 max_age; __u32 hello_time; __u32 forward_delay; __u32 bridge_max_age; __u32 bridge_hello_time; __u32 bridge_forward_delay; __u8 topology_change; __u8 topology_change_detected; __u8 root_port; __u8 stp_enabled; __u32 ageing_time; __u32 gc_interval; __u32 hello_timer_value; __u32 tcn_timer_value; __u32 topology_change_timer_value; __u32 gc_timer_value; }; struct __port_info { __u64 designated_root; __u64 designated_bridge; __u16 port_id; __u16 designated_port; __u32 path_cost; __u32 designated_cost; __u8 state; __u8 top_change_ack; __u8 config_pending; __u8 unused0; __u32 message_age_timer_value; __u32 forward_delay_timer_value; __u32 hold_timer_value; }; struct __fdb_entry { __u8 mac_addr[6]; __u8 port_no; __u8 is_local; __u32 ageing_timer_value; __u8 port_hi; __u8 pad0; __u16 unused; }; #endif lldpad-0.9.46/include/linux/if_link.h000066400000000000000000000212211215142747300174240ustar00rootroot00000000000000#ifndef _LINUX_IF_LINK_H #define _LINUX_IF_LINK_H #include #include /* This struct should be in sync with struct rtnl_link_stats64 */ struct rtnl_link_stats { __u32 rx_packets; /* total packets received */ __u32 tx_packets; /* total packets transmitted */ __u32 rx_bytes; /* total bytes received */ __u32 tx_bytes; /* total bytes transmitted */ __u32 rx_errors; /* bad packets received */ __u32 tx_errors; /* packet transmit problems */ __u32 rx_dropped; /* no space in linux buffers */ __u32 tx_dropped; /* no space available in linux */ __u32 multicast; /* multicast packets received */ __u32 collisions; /* detailed rx_errors: */ __u32 rx_length_errors; __u32 rx_over_errors; /* receiver ring buff overflow */ __u32 rx_crc_errors; /* recved pkt with crc error */ __u32 rx_frame_errors; /* recv'd frame alignment error */ __u32 rx_fifo_errors; /* recv'r fifo overrun */ __u32 rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */ __u32 tx_aborted_errors; __u32 tx_carrier_errors; __u32 tx_fifo_errors; __u32 tx_heartbeat_errors; __u32 tx_window_errors; /* for cslip etc */ __u32 rx_compressed; __u32 tx_compressed; }; /* The main device statistics structure */ struct rtnl_link_stats64 { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ __u64 rx_bytes; /* total bytes received */ __u64 tx_bytes; /* total bytes transmitted */ __u64 rx_errors; /* bad packets received */ __u64 tx_errors; /* packet transmit problems */ __u64 rx_dropped; /* no space in linux buffers */ __u64 tx_dropped; /* no space available in linux */ __u64 multicast; /* multicast packets received */ __u64 collisions; /* detailed rx_errors: */ __u64 rx_length_errors; __u64 rx_over_errors; /* receiver ring buff overflow */ __u64 rx_crc_errors; /* recved pkt with crc error */ __u64 rx_frame_errors; /* recv'd frame alignment error */ __u64 rx_fifo_errors; /* recv'r fifo overrun */ __u64 rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */ __u64 tx_aborted_errors; __u64 tx_carrier_errors; __u64 tx_fifo_errors; __u64 tx_heartbeat_errors; __u64 tx_window_errors; /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; }; /* The struct should be in sync with struct ifmap */ struct rtnl_link_ifmap { __u64 mem_start; __u64 mem_end; __u64 base_addr; __u16 irq; __u8 dma; __u8 port; }; enum { IFLA_UNSPEC, IFLA_ADDRESS, IFLA_BROADCAST, IFLA_IFNAME, IFLA_MTU, IFLA_LINK, IFLA_QDISC, IFLA_STATS, IFLA_COST, #define IFLA_COST IFLA_COST IFLA_PRIORITY, #define IFLA_PRIORITY IFLA_PRIORITY IFLA_MASTER, #define IFLA_MASTER IFLA_MASTER IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ #define IFLA_WIRELESS IFLA_WIRELESS IFLA_PROTINFO, /* Protocol specific information for a link */ #define IFLA_PROTINFO IFLA_PROTINFO IFLA_TXQLEN, #define IFLA_TXQLEN IFLA_TXQLEN IFLA_MAP, #define IFLA_MAP IFLA_MAP IFLA_WEIGHT, #define IFLA_WEIGHT IFLA_WEIGHT IFLA_OPERSTATE, IFLA_LINKMODE, IFLA_LINKINFO, #define IFLA_LINKINFO IFLA_LINKINFO IFLA_NET_NS_PID, IFLA_IFALIAS, IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */ IFLA_VFINFO_LIST, IFLA_STATS64, IFLA_VF_PORTS, IFLA_PORT_SELF, IFLA_AF_SPEC, IFLA_GROUP, /* Group the device belongs to */ IFLA_NET_NS_FD, IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ #define IFLA_PROMISCUITY IFLA_PROMISCUITY IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, IFLA_CARRIER, __IFLA_MAX }; #define IFLA_MAX (__IFLA_MAX - 1) /* backwards compatibility for userspace */ #ifndef __KERNEL__ #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) #endif /* ifi_flags. IFF_* flags. The only change is: IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are more not changeable by user. They describe link media characteristics and set by device driver. Comments: - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid - If neither of these three flags are set; the interface is NBMA. - IFF_MULTICAST does not mean anything special: multicasts can be used on all not-NBMA links. IFF_MULTICAST means that this media uses special encapsulation for multicast frames. Apparently, all IFF_POINTOPOINT and IFF_BROADCAST devices are able to use multicasts too. */ /* IFLA_LINK. For usual devices it is equal ifi_index. If it is a "virtual interface" (f.e. tunnel), ifi_link can point to real physical interface (f.e. for bandwidth calculations), or maybe 0, what means, that real media is unknown (usual for IPIP tunnels, when route to endpoint is allowed to change) */ /* Subtype attributes for IFLA_PROTINFO */ enum { IFLA_INET6_UNSPEC, IFLA_INET6_FLAGS, /* link flags */ IFLA_INET6_CONF, /* sysctl parameters */ IFLA_INET6_STATS, /* statistics */ IFLA_INET6_MCAST, /* MC things. What of them? */ IFLA_INET6_CACHEINFO, /* time values and max reasm size */ IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ __IFLA_INET6_MAX }; #define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) struct ifla_cacheinfo { __u32 max_reasm_len; __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ __u32 reachable_time; __u32 retrans_time; }; enum { IFLA_INFO_UNSPEC, IFLA_INFO_KIND, IFLA_INFO_DATA, IFLA_INFO_XSTATS, __IFLA_INFO_MAX, }; #define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1) /* VLAN section */ enum { IFLA_VLAN_UNSPEC, IFLA_VLAN_ID, IFLA_VLAN_FLAGS, IFLA_VLAN_EGRESS_QOS, IFLA_VLAN_INGRESS_QOS, __IFLA_VLAN_MAX, }; #define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) struct ifla_vlan_flags { __u32 flags; __u32 mask; }; enum { IFLA_VLAN_QOS_UNSPEC, IFLA_VLAN_QOS_MAPPING, __IFLA_VLAN_QOS_MAX }; #define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1) struct ifla_vlan_qos_mapping { __u32 from; __u32 to; }; /* MACVLAN section */ enum { IFLA_MACVLAN_UNSPEC, IFLA_MACVLAN_MODE, __IFLA_MACVLAN_MAX, }; #define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) enum macvlan_mode { MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */ }; /* SR-IOV virtual function management section */ enum { IFLA_VF_INFO_UNSPEC, IFLA_VF_INFO, __IFLA_VF_INFO_MAX, }; #define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1) enum { IFLA_VF_UNSPEC, IFLA_VF_MAC, /* Hardware queue specific attributes */ IFLA_VF_VLAN, IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */ __IFLA_VF_MAX, }; #define IFLA_VF_MAX (__IFLA_VF_MAX - 1) struct ifla_vf_mac { __u32 vf; __u8 mac[32]; /* MAX_ADDR_LEN */ }; struct ifla_vf_vlan { __u32 vf; __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ __u32 qos; }; struct ifla_vf_tx_rate { __u32 vf; __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ }; struct ifla_vf_info { __u32 vf; __u8 mac[32]; __u32 vlan; __u32 qos; __u32 tx_rate; }; /* VF ports management section * * Nested layout of set/get msg is: * * [IFLA_NUM_VF] * [IFLA_VF_PORTS] * [IFLA_VF_PORT] * [IFLA_PORT_*], ... * [IFLA_VF_PORT] * [IFLA_PORT_*], ... * ... * [IFLA_PORT_SELF] * [IFLA_PORT_*], ... */ enum { IFLA_VF_PORT_UNSPEC, IFLA_VF_PORT, /* nest */ __IFLA_VF_PORT_MAX, }; #define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1) enum { IFLA_PORT_UNSPEC, IFLA_PORT_VF, /* __u32 */ IFLA_PORT_PROFILE, /* string */ IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */ IFLA_PORT_INSTANCE_UUID, /* binary UUID */ IFLA_PORT_HOST_UUID, /* binary UUID */ IFLA_PORT_REQUEST, /* __u8 */ IFLA_PORT_RESPONSE, /* __u16, output only */ __IFLA_PORT_MAX, }; #define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1) #define PORT_PROFILE_MAX 40 #define PORT_UUID_MAX 16 #define PORT_SELF_VF -1 enum { PORT_REQUEST_PREASSOCIATE = 0, PORT_REQUEST_PREASSOCIATE_RR, PORT_REQUEST_ASSOCIATE, PORT_REQUEST_DISASSOCIATE, }; enum { PORT_VDP_RESPONSE_SUCCESS = 0, PORT_VDP_RESPONSE_INVALID_FORMAT, PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES, PORT_VDP_RESPONSE_UNUSED_VTID, PORT_VDP_RESPONSE_VTID_VIOLATION, PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION, PORT_VDP_RESPONSE_OUT_OF_SYNC, /* 0x08-0xFF reserved for future VDP use */ PORT_PROFILE_RESPONSE_SUCCESS = 0x100, PORT_PROFILE_RESPONSE_INPROGRESS, PORT_PROFILE_RESPONSE_INVALID, PORT_PROFILE_RESPONSE_BADSTATE, PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES, PORT_PROFILE_RESPONSE_ERROR, }; struct ifla_port_vsi { __u8 vsi_mgr_id; __u8 vsi_type_id[3]; __u8 vsi_type_version; __u8 pad[3]; }; #endif /* _LINUX_IF_LINK_H */ lldpad-0.9.46/include/linux/if_vlan.h000066400000000000000000000032531215142747300174340ustar00rootroot00000000000000/* * VLAN An implementation of 802.1Q VLAN tagging. * * Authors: Ben Greear * * 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. * */ #ifndef _LINUX_IF_VLAN_H_ #define _LINUX_IF_VLAN_H_ /* VLAN IOCTLs are found in sockios.h */ /* Passed in vlan_ioctl_args structure to determine behaviour. */ enum vlan_ioctl_cmds { ADD_VLAN_CMD, DEL_VLAN_CMD, SET_VLAN_INGRESS_PRIORITY_CMD, SET_VLAN_EGRESS_PRIORITY_CMD, GET_VLAN_INGRESS_PRIORITY_CMD, GET_VLAN_EGRESS_PRIORITY_CMD, SET_VLAN_NAME_TYPE_CMD, SET_VLAN_FLAG_CMD, GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */ GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */ }; enum vlan_flags { VLAN_FLAG_REORDER_HDR = 0x1, VLAN_FLAG_GVRP = 0x2, VLAN_FLAG_LOOSE_BINDING = 0x4, }; enum vlan_name_types { VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */ VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */ VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */ VLAN_NAME_TYPE_HIGHEST }; struct vlan_ioctl_args { int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ char device1[24]; union { char device2[24]; int VID; unsigned int skb_priority; unsigned int name_type; unsigned int bind_type; unsigned int flag; /* Matches vlan_dev_info flags */ } u; short vlan_qos; }; #endif /* !(_LINUX_IF_VLAN_H_) */ lldpad-0.9.46/include/linux/netlink.h000066400000000000000000000113501215142747300174570ustar00rootroot00000000000000#ifndef __LINUX_NETLINK_H #define __LINUX_NETLINK_H #include /* for sa_family_t */ #include #define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_UNUSED 1 /* Unused number */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ #define NETLINK_INET_DIAG 4 /* INET socket monitoring */ #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ #define NETLINK_XFRM 6 /* ipsec */ #define NETLINK_SELINUX 7 /* SELinux event notifications */ #define NETLINK_ISCSI 8 /* Open-iSCSI */ #define NETLINK_AUDIT 9 /* auditing */ #define NETLINK_FIB_LOOKUP 10 #define NETLINK_CONNECTOR 11 #define NETLINK_NETFILTER 12 /* netfilter subsystem */ #define NETLINK_IP6_FW 13 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ #define NETLINK_GENERIC 16 /* leave room for NETLINK_DM (DM Events) */ #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ #define NETLINK_ECRYPTFS 19 #define MAX_LINKS 32 struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* zero */ __u32 nl_pid; /* port ID */ __u32 nl_groups; /* multicast groups mask */ }; struct nlmsghdr { __u32 nlmsg_len; /* Length of message including header */ __u16 nlmsg_type; /* Message content */ __u16 nlmsg_flags; /* Additional flags */ __u32 nlmsg_seq; /* Sequence number */ __u32 nlmsg_pid; /* Sending process port ID */ }; /* Flags values */ #define NLM_F_REQUEST 1 /* It is request message. */ #define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ #define NLM_F_ECHO 8 /* Echo this request */ /* Modifiers to GET request */ #define NLM_F_ROOT 0x100 /* specify tree root */ #define NLM_F_MATCH 0x200 /* return all matching */ #define NLM_F_ATOMIC 0x400 /* atomic GET */ #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) /* Modifiers to NEW request */ #define NLM_F_REPLACE 0x100 /* Override existing */ #define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_APPEND 0x800 /* Add to end of list */ /* 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL 4.4BSD CHANGE NLM_F_REPLACE True CHANGE NLM_F_CREATE|NLM_F_REPLACE Append NLM_F_CREATE Check NLM_F_EXCL */ #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len <= (len)) #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) #define NLMSG_NOOP 0x1 /* Nothing. */ #define NLMSG_ERROR 0x2 /* Error */ #define NLMSG_DONE 0x3 /* End of a dump */ #define NLMSG_OVERRUN 0x4 /* Data lost */ #define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ struct nlmsgerr { int error; struct nlmsghdr msg; }; #define NETLINK_ADD_MEMBERSHIP 1 #define NETLINK_DROP_MEMBERSHIP 2 #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 #define NETLINK_NO_ENOBUFS 5 struct nl_pktinfo { __u32 group; }; #define NET_MAJOR 36 /* Major 36 is reserved for networking */ enum { NETLINK_UNCONNECTED = 0, NETLINK_CONNECTED, }; /* * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> * +---------------------+- - -+- - - - - - - - - -+- - -+ * | Header | Pad | Payload | Pad | * | (struct nlattr) | ing | | ing | * +---------------------+- - -+- - - - - - - - - -+- - -+ * <-------------- nlattr->nla_len --------------> */ struct nlattr { __u16 nla_len; __u16 nla_type; }; /* * nla_type (16 bits) * +---+---+-------------------------------+ * | N | O | Attribute Type | * +---+---+-------------------------------+ * N := Carries nested attributes * O := Payload stored in network byte order * * Note: The N and O flag are mutually exclusive. */ #define NLA_F_NESTED (1 << 15) #define NLA_F_NET_BYTEORDER (1 << 14) #define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) #define NLA_ALIGNTO 4 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) #endif /* __LINUX_NETLINK_H */ lldpad-0.9.46/include/linux/rtnetlink.h000066400000000000000000000346771215142747300200460ustar00rootroot00000000000000#ifndef __LINUX_RTNETLINK_H #define __LINUX_RTNETLINK_H #include #include #include #include #include /* rtnetlink families. Values up to 127 are reserved for real address * families, values above 128 may be used arbitrarily. */ #define RTNL_FAMILY_IPMR 128 #define RTNL_FAMILY_IP6MR 129 #define RTNL_FAMILY_MAX 129 /**** * Routing/neighbour discovery messages. ****/ /* Types of messages */ enum { RTM_BASE = 16, #define RTM_BASE RTM_BASE RTM_NEWLINK = 16, #define RTM_NEWLINK RTM_NEWLINK RTM_DELLINK, #define RTM_DELLINK RTM_DELLINK RTM_GETLINK, #define RTM_GETLINK RTM_GETLINK RTM_SETLINK, #define RTM_SETLINK RTM_SETLINK RTM_NEWADDR = 20, #define RTM_NEWADDR RTM_NEWADDR RTM_DELADDR, #define RTM_DELADDR RTM_DELADDR RTM_GETADDR, #define RTM_GETADDR RTM_GETADDR RTM_NEWROUTE = 24, #define RTM_NEWROUTE RTM_NEWROUTE RTM_DELROUTE, #define RTM_DELROUTE RTM_DELROUTE RTM_GETROUTE, #define RTM_GETROUTE RTM_GETROUTE RTM_NEWNEIGH = 28, #define RTM_NEWNEIGH RTM_NEWNEIGH RTM_DELNEIGH, #define RTM_DELNEIGH RTM_DELNEIGH RTM_GETNEIGH, #define RTM_GETNEIGH RTM_GETNEIGH RTM_NEWRULE = 32, #define RTM_NEWRULE RTM_NEWRULE RTM_DELRULE, #define RTM_DELRULE RTM_DELRULE RTM_GETRULE, #define RTM_GETRULE RTM_GETRULE RTM_NEWQDISC = 36, #define RTM_NEWQDISC RTM_NEWQDISC RTM_DELQDISC, #define RTM_DELQDISC RTM_DELQDISC RTM_GETQDISC, #define RTM_GETQDISC RTM_GETQDISC RTM_NEWTCLASS = 40, #define RTM_NEWTCLASS RTM_NEWTCLASS RTM_DELTCLASS, #define RTM_DELTCLASS RTM_DELTCLASS RTM_GETTCLASS, #define RTM_GETTCLASS RTM_GETTCLASS RTM_NEWTFILTER = 44, #define RTM_NEWTFILTER RTM_NEWTFILTER RTM_DELTFILTER, #define RTM_DELTFILTER RTM_DELTFILTER RTM_GETTFILTER, #define RTM_GETTFILTER RTM_GETTFILTER RTM_NEWACTION = 48, #define RTM_NEWACTION RTM_NEWACTION RTM_DELACTION, #define RTM_DELACTION RTM_DELACTION RTM_GETACTION, #define RTM_GETACTION RTM_GETACTION RTM_NEWPREFIX = 52, #define RTM_NEWPREFIX RTM_NEWPREFIX RTM_GETMULTICAST = 58, #define RTM_GETMULTICAST RTM_GETMULTICAST RTM_GETANYCAST = 62, #define RTM_GETANYCAST RTM_GETANYCAST RTM_NEWNEIGHTBL = 64, #define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL RTM_GETNEIGHTBL = 66, #define RTM_GETNEIGHTBL RTM_GETNEIGHTBL RTM_SETNEIGHTBL, #define RTM_SETNEIGHTBL RTM_SETNEIGHTBL RTM_NEWNDUSEROPT = 68, #define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT RTM_NEWADDRLABEL = 72, #define RTM_NEWADDRLABEL RTM_NEWADDRLABEL RTM_DELADDRLABEL, #define RTM_DELADDRLABEL RTM_DELADDRLABEL RTM_GETADDRLABEL, #define RTM_GETADDRLABEL RTM_GETADDRLABEL RTM_GETDCB = 78, #define RTM_GETDCB RTM_GETDCB RTM_SETDCB, #define RTM_SETDCB RTM_SETDCB __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; #define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) #define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) #define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) /* Generic structure for encapsulation of optional route information. It is reminiscent of sockaddr, but with sa_family replaced with attribute type. */ struct rtattr { unsigned short rta_len; unsigned short rta_type; }; /* Macros to handle rtattributes */ #define RTA_ALIGNTO 4 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) #define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ (rta)->rta_len >= sizeof(struct rtattr) && \ (rta)->rta_len <= (len)) #define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) #define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) #define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) /****************************************************************************** * Definitions used in routing table administration. ****/ struct rtmsg { unsigned char rtm_family; unsigned char rtm_dst_len; unsigned char rtm_src_len; unsigned char rtm_tos; unsigned char rtm_table; /* Routing table id */ unsigned char rtm_protocol; /* Routing protocol; see below */ unsigned char rtm_scope; /* See below */ unsigned char rtm_type; /* See below */ unsigned rtm_flags; }; /* rtm_type */ enum { RTN_UNSPEC, RTN_UNICAST, /* Gateway or direct route */ RTN_LOCAL, /* Accept locally */ RTN_BROADCAST, /* Accept locally as broadcast, send as broadcast */ RTN_ANYCAST, /* Accept locally as broadcast, but send as unicast */ RTN_MULTICAST, /* Multicast route */ RTN_BLACKHOLE, /* Drop */ RTN_UNREACHABLE, /* Destination is unreachable */ RTN_PROHIBIT, /* Administratively prohibited */ RTN_THROW, /* Not in this table */ RTN_NAT, /* Translate this address */ RTN_XRESOLVE, /* Use external resolver */ __RTN_MAX }; #define RTN_MAX (__RTN_MAX - 1) /* rtm_protocol */ #define RTPROT_UNSPEC 0 #define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; not used by current IPv4 */ #define RTPROT_KERNEL 2 /* Route installed by kernel */ #define RTPROT_BOOT 3 /* Route installed during boot */ #define RTPROT_STATIC 4 /* Route installed by administrator */ /* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; they are just passed from user and back as is. It will be used by hypothetical multiple routing daemons. Note that protocol values should be standardized in order to avoid conflicts. */ #define RTPROT_GATED 8 /* Apparently, GateD */ #define RTPROT_RA 9 /* RDISC/ND router advertisements */ #define RTPROT_MRT 10 /* Merit MRT */ #define RTPROT_ZEBRA 11 /* Zebra */ #define RTPROT_BIRD 12 /* BIRD */ #define RTPROT_DNROUTED 13 /* DECnet routing daemon */ #define RTPROT_XORP 14 /* XORP */ #define RTPROT_NTK 15 /* Netsukuku */ #define RTPROT_DHCP 16 /* DHCP client */ /* rtm_scope Really it is not scope, but sort of distance to the destination. NOWHERE are reserved for not existing destinations, HOST is our local addresses, LINK are destinations, located on directly attached link and UNIVERSE is everywhere in the Universe. Intermediate values are also possible f.e. interior routes could be assigned a value between UNIVERSE and LINK. */ enum rt_scope_t { RT_SCOPE_UNIVERSE=0, /* User defined values */ RT_SCOPE_SITE=200, RT_SCOPE_LINK=253, RT_SCOPE_HOST=254, RT_SCOPE_NOWHERE=255 }; /* rtm_flags */ #define RTM_F_NOTIFY 0x100 /* Notify user of route change */ #define RTM_F_CLONED 0x200 /* This route is cloned */ #define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ #define RTM_F_PREFIX 0x800 /* Prefix addresses */ /* Reserved table identifiers */ enum rt_class_t { RT_TABLE_UNSPEC=0, /* User defined values */ RT_TABLE_COMPAT=252, RT_TABLE_DEFAULT=253, RT_TABLE_MAIN=254, RT_TABLE_LOCAL=255, RT_TABLE_MAX=0xFFFFFFFF }; /* Routing message attributes */ enum rtattr_type_t { RTA_UNSPEC, RTA_DST, RTA_SRC, RTA_IIF, RTA_OIF, RTA_GATEWAY, RTA_PRIORITY, RTA_PREFSRC, RTA_METRICS, RTA_MULTIPATH, RTA_PROTOINFO, /* no longer used */ RTA_FLOW, RTA_CACHEINFO, RTA_SESSION, /* no longer used */ RTA_MP_ALGO, /* no longer used */ RTA_TABLE, RTA_MARK, __RTA_MAX }; #define RTA_MAX (__RTA_MAX - 1) #define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) #define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) /* RTM_MULTIPATH --- array of struct rtnexthop. * * "struct rtnexthop" describes all necessary nexthop information, * i.e. parameters of path to a destination via this nexthop. * * At the moment it is impossible to set different prefsrc, mtu, window * and rtt for different paths from multipath. */ struct rtnexthop { unsigned short rtnh_len; unsigned char rtnh_flags; unsigned char rtnh_hops; int rtnh_ifindex; }; /* rtnh_flags */ #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ /* Macros to handle hexthops */ #define RTNH_ALIGNTO 4 #define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) #define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ ((int)(rtnh)->rtnh_len) <= (len)) #define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) #define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) #define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) #define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) /* RTM_CACHEINFO */ struct rta_cacheinfo { __u32 rta_clntref; __u32 rta_lastuse; __s32 rta_expires; __u32 rta_error; __u32 rta_used; #define RTNETLINK_HAVE_PEERINFO 1 __u32 rta_id; __u32 rta_ts; __u32 rta_tsage; }; /* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ enum { RTAX_UNSPEC, #define RTAX_UNSPEC RTAX_UNSPEC RTAX_LOCK, #define RTAX_LOCK RTAX_LOCK RTAX_MTU, #define RTAX_MTU RTAX_MTU RTAX_WINDOW, #define RTAX_WINDOW RTAX_WINDOW RTAX_RTT, #define RTAX_RTT RTAX_RTT RTAX_RTTVAR, #define RTAX_RTTVAR RTAX_RTTVAR RTAX_SSTHRESH, #define RTAX_SSTHRESH RTAX_SSTHRESH RTAX_CWND, #define RTAX_CWND RTAX_CWND RTAX_ADVMSS, #define RTAX_ADVMSS RTAX_ADVMSS RTAX_REORDERING, #define RTAX_REORDERING RTAX_REORDERING RTAX_HOPLIMIT, #define RTAX_HOPLIMIT RTAX_HOPLIMIT RTAX_INITCWND, #define RTAX_INITCWND RTAX_INITCWND RTAX_FEATURES, #define RTAX_FEATURES RTAX_FEATURES RTAX_RTO_MIN, #define RTAX_RTO_MIN RTAX_RTO_MIN RTAX_INITRWND, #define RTAX_INITRWND RTAX_INITRWND __RTAX_MAX }; #define RTAX_MAX (__RTAX_MAX - 1) #define RTAX_FEATURE_ECN 0x00000001 #define RTAX_FEATURE_SACK 0x00000002 #define RTAX_FEATURE_TIMESTAMP 0x00000004 #define RTAX_FEATURE_ALLFRAG 0x00000008 struct rta_session { __u8 proto; __u8 pad1; __u16 pad2; union { struct { __u16 sport; __u16 dport; } ports; struct { __u8 type; __u8 code; __u16 ident; } icmpt; __u32 spi; } u; }; /**** * General form of address family dependent message. ****/ struct rtgenmsg { unsigned char rtgen_family; }; /***************************************************************** * Link layer specific messages. ****/ /* struct ifinfomsg * passes link level specific information, not dependent * on network protocol. */ struct ifinfomsg { unsigned char ifi_family; unsigned char __ifi_pad; unsigned short ifi_type; /* ARPHRD_* */ int ifi_index; /* Link index */ unsigned ifi_flags; /* IFF_* flags */ unsigned ifi_change; /* IFF_* change mask */ }; /******************************************************************** * prefix information ****/ struct prefixmsg { unsigned char prefix_family; unsigned char prefix_pad1; unsigned short prefix_pad2; int prefix_ifindex; unsigned char prefix_type; unsigned char prefix_len; unsigned char prefix_flags; unsigned char prefix_pad3; }; enum { PREFIX_UNSPEC, PREFIX_ADDRESS, PREFIX_CACHEINFO, __PREFIX_MAX }; #define PREFIX_MAX (__PREFIX_MAX - 1) struct prefix_cacheinfo { __u32 preferred_time; __u32 valid_time; }; /***************************************************************** * Traffic control messages. ****/ struct tcmsg { unsigned char tcm_family; unsigned char tcm__pad1; unsigned short tcm__pad2; int tcm_ifindex; __u32 tcm_handle; __u32 tcm_parent; __u32 tcm_info; }; enum { TCA_UNSPEC, TCA_KIND, TCA_OPTIONS, TCA_STATS, TCA_XSTATS, TCA_RATE, TCA_FCNT, TCA_STATS2, TCA_STAB, __TCA_MAX }; #define TCA_MAX (__TCA_MAX - 1) #define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) #define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) /******************************************************************** * Neighbor Discovery userland options ****/ struct nduseroptmsg { unsigned char nduseropt_family; unsigned char nduseropt_pad1; unsigned short nduseropt_opts_len; /* Total length of options */ int nduseropt_ifindex; __u8 nduseropt_icmp_type; __u8 nduseropt_icmp_code; unsigned short nduseropt_pad2; unsigned int nduseropt_pad3; /* Followed by one or more ND options */ }; enum { NDUSEROPT_UNSPEC, NDUSEROPT_SRCADDR, __NDUSEROPT_MAX }; #define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) #ifndef __KERNEL__ /* RTnetlink multicast groups - backwards compatibility for userspace */ #define RTMGRP_LINK 1 #define RTMGRP_NOTIFY 2 #define RTMGRP_NEIGH 4 #define RTMGRP_TC 8 #define RTMGRP_IPV4_IFADDR 0x10 #define RTMGRP_IPV4_MROUTE 0x20 #define RTMGRP_IPV4_ROUTE 0x40 #define RTMGRP_IPV4_RULE 0x80 #define RTMGRP_IPV6_IFADDR 0x100 #define RTMGRP_IPV6_MROUTE 0x200 #define RTMGRP_IPV6_ROUTE 0x400 #define RTMGRP_IPV6_IFINFO 0x800 #define RTMGRP_DECnet_IFADDR 0x1000 #define RTMGRP_DECnet_ROUTE 0x4000 #define RTMGRP_IPV6_PREFIX 0x20000 #endif /* RTnetlink multicast groups */ enum rtnetlink_groups { RTNLGRP_NONE, #define RTNLGRP_NONE RTNLGRP_NONE RTNLGRP_LINK, #define RTNLGRP_LINK RTNLGRP_LINK RTNLGRP_NOTIFY, #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY RTNLGRP_NEIGH, #define RTNLGRP_NEIGH RTNLGRP_NEIGH RTNLGRP_TC, #define RTNLGRP_TC RTNLGRP_TC RTNLGRP_IPV4_IFADDR, #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_MROUTE, #define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_ROUTE, #define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_RULE, #define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE RTNLGRP_IPV6_IFADDR, #define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_MROUTE, #define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_ROUTE, #define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_IFINFO, #define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO RTNLGRP_DECnet_IFADDR, #define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR RTNLGRP_NOP2, RTNLGRP_DECnet_ROUTE, #define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_RULE, #define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE RTNLGRP_NOP4, RTNLGRP_IPV6_PREFIX, #define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_RULE, #define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE RTNLGRP_ND_USEROPT, #define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT RTNLGRP_PHONET_IFADDR, #define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_ROUTE, #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) /* TC action piece */ struct tcamsg { unsigned char tca_family; unsigned char tca__pad1; unsigned short tca__pad2; }; #define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) #define TCA_ACT_TAB 1 /* attr type must be >=1 */ #define TCAA_MAX 1 #endif /* __LINUX_RTNETLINK_H */ lldpad-0.9.46/include/list.h000066400000000000000000000032741215142747300156350ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LIST_H #define _LIST_H #include typedef enum { ls_ok = 0, ls_failed = -1, ls_exists = -2 } list_rvals; /* define a pointer of type y and initialize to the value of the list entry */ #define LV_PTR(x, y, z) y *x = (z)?((y *)z->value):(NULL); struct ls_entry { char *key; /* NULL terminated string */ void *value; LIST_ENTRY(ls_entry) entries; }; typedef struct ls_entry * lsp; LIST_HEAD(lshead, ls_entry); struct ls_entry *ls_find(struct lshead *, char *);; list_rvals ls_insert(struct lshead *, char *, void *); void ls_erase(struct lshead *, char *); void ls_remove_list(struct lshead *); #endif /* _LIST_H */ lldpad-0.9.46/include/lldp.h000066400000000000000000000173221215142747300156140ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_H #define _LLDP_H #include #include typedef __u8 u8; typedef __u16 u16; typedef __u32 u32; typedef __u64 u64; #define UNUSED __attribute__((__unused__)) #define MIN(x,y) \ ({ \ typeof (x) __x = (x); \ typeof (y) __y = (y); \ __x < __y ? __x : __y; \ }) #define MAX(x,y) \ ({ \ typeof (x) __x = (x); \ typeof (y) __y = (y); \ __x > __y ? __x : __y; \ }) /* * Organizationally Unique Identifier (OUI) * http://standards.ieee.org/regauth/oui/oui.txt */ #define OUI_SIZE 3 #define SUB_SIZE 1 #define OUI_SUB_SIZE (OUI_SIZE + SUB_SIZE) #define OUI_INTEL_CORP 0x001b21 #define OUI_CEE_DCBX OUI_INTEL_CORP #define OUI_IEEE_8021 0x0080c2 #define OUI_IEEE_8023 0x00120f #define OUI_IEEE_80211 0x000fac /* Telecommunications Industry Association TR-41 Committee */ #define OUI_TIA_TR41 0x0012bb /* Ciso OUI */ #define OUI_CISCO 0x000142 #define OUI_IEEE_8021Qbg 0x001b3f /* Draft 0.1 value */ #define OUI_IEEE_8021Qbg22 0x0080c2 /* Standardized value */ /* IEEE 802.3AB Clause 9: TLV Types */ #define CHASSIS_ID_TLV 1 #define PORT_ID_TLV 2 #define TIME_TO_LIVE_TLV 3 #define PORT_DESCRIPTION_TLV 4 #define SYSTEM_NAME_TLV 5 #define SYSTEM_DESCRIPTION_TLV 6 #define SYSTEM_CAPABILITIES_TLV 7 #define MANAGEMENT_ADDRESS_TLV 8 #define ORG_SPECIFIC_TLV 127 #define END_OF_LLDPDU_TLV 0 /* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ #define CHASSIS_ID_RESERVED 0 #define CHASSIS_ID_CHASSIS_COMPONENT 1 #define CHASSIS_ID_INTERFACE_ALIAS 2 #define CHASSIS_ID_PORT_COMPONENT 3 #define CHASSIS_ID_MAC_ADDRESS 4 #define CHASSIS_ID_NETWORK_ADDRESS 5 #define CHASSIS_ID_INTERFACE_NAME 6 #define CHASSIS_ID_LOCALLY_ASSIGNED 7 #define CHASSIS_ID_INVALID(t) (((t) == 0) || ((t) > 7)) /* IEEE 802.3AB Clause 9.5.3: Port subtype */ #define PORT_ID_RESERVED 0 #define PORT_ID_INTERFACE_ALIAS 1 #define PORT_ID_PORT_COMPONENT 2 #define PORT_ID_MAC_ADDRESS 3 #define PORT_ID_NETWORK_ADDRESS 4 #define PORT_ID_INTERFACE_NAME 5 #define PORT_ID_AGENT_CIRCUIT_ID 6 #define PORT_ID_LOCALLY_ASSIGNED 7 #define PORT_ID_INVALID(t) (((t) == 0) || ((t) > 7)) /* IEEE 802.1AB: Annex E, Table E.1: Organizationally Specific TLVs */ #define ORG_SPEC_PVID 1 #define ORG_SPEC_PPVID 2 #define ORG_SPEC_VLAN_NAME 3 #define ORG_SPEC_PROTO_ID 4 #define ORG_SPEC_VID_USAGE 5 #define ORG_SPEC_MGMT_VID 6 #define ORG_SPEC_LINK_AGGR 7 #define ORG_SPEC_INVALID(t) (((t) == 0) || ((t) > 7)) /* IEEE 802.1AB: 8.5.8, Table 8-4: System Capabilities */ #define SYSCAP_OTHER (1 << 0) #define SYSCAP_REPEATER (1 << 1) #define SYSCAP_BRIDGE (1 << 2) #define SYSCAP_WLAN (1 << 3) #define SYSCAP_ROUTER (1 << 4) #define SYSCAP_PHONE (1 << 5) #define SYSCAP_DOCSIS (1 << 6) #define SYSCAP_STATION (1 << 7) #define SYSCAP_CVLAN (1 << 8) /* TPID: 0x8100 */ #define SYSCAP_SVLAN (1 << 9) /* TPID: 0x88a8 */ #define SYSCAP_TPMR (1 << 10) #define SYSCAP_BITMASK 0x0fff /* * IETF RFC 3232: * http://www.iana.org/assignments/ianaaddressfamilynumbers-mib */ enum { MANADDR_OTHER = 0, MANADDR_IPV4 = 1, MANADDR_IPV6 = 2, MANADDR_NSAP = 3, MANADDR_HDLC = 4, MANADDR_BBN1822 = 5, MANADDR_ALL802 = 6, MANADDR_E163 = 7, MANADDR_E164 = 8, MANADDR_F69 = 9, MANADDR_X121 = 10, MANADDR_IPX = 11, MANADDR_APPLETALK = 12, MANADDR_DECNETIV = 13, MANADDR_BANYANVINES = 14, MANADDR_E164WITHNSAP = 15, MANADDR_DNS = 16, MANADDR_DISTINGUISHEDNAME = 17, MANADDR_ASNUMBER = 18, MANADDR_XTPOVERIPV4 = 19, MANADDR_XTPOVERIPV6 = 20, MANADDR_XTPNATIVEMODEXTP = 21, MANADDR_FIBRECHANNELWWPN = 22, MANADDR_FIBRECHANNELWWNN = 23, MANADDR_GWID = 24, MANADDR_AFI = 25, MANADDR_RESERVED = 65535 }; #define MANADDR_MAX (MANADDR_AFI - MANADDR_OTHER + 2) #define IFNUM_UNKNOWN 1 #define IFNUM_IFINDEX 2 #define IFNUM_SYS_PORT_NUM 3 /* LLDP-MED subtypes */ #define LLDP_MED_RESERVED 0 #define LLDP_MED_CAPABILITIES 1 #define LLDP_MED_NETWORK_POLICY 2 #define LLDP_MED_LOCATION_ID 3 #define LLDP_MED_EXTENDED_PVMDI 4 #define LLDP_MED_INV_HWREV 5 #define LLDP_MED_INV_FWREV 6 #define LLDP_MED_INV_SWREV 7 #define LLDP_MED_INV_SERIAL 8 #define LLDP_MED_INV_MANUFACTURER 9 #define LLDP_MED_INV_MODELNAME 10 #define LLDP_MED_INV_ASSETID 11 /* LLDP-MED Capability Values: ANSI/TIA-1057-2006, 10.2.2.1, Table 10 */ #define LLDP_MED_CAPABILITY_CAPAPILITIES (1 << 0) #define LLDP_MED_CAPABILITY_NETWORK_POLICY (1 << 1) #define LLDP_MED_CAPABILITY_LOCATION_ID (1 << 2) #define LLDP_MED_CAPABILITY_EXTENDED_PSE (1 << 3) #define LLDP_MED_CAPABILITY_EXTENDED_PD (1 << 4) #define LLDP_MED_CAPABILITY_INVENTORY (1 << 5) /* LLDP-MED Device Type Values: ANSI/TIA-1057-2006, 10.2.2.2, Table 11 */ #define LLDP_MED_DEVTYPE_NOT_DEFINED 0 #define LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I 1 #define LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II 2 #define LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III 3 #define LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY 4 #define LLDP_MED_DEVTYPE_INVALID(t) ((t) > 4) #define LLDP_MED_DEVTYPE_DEFINED(t) \ (((t) == LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I) || \ ((t) == LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II) || \ ((t) == LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III) ||\ ((t) == LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY)) #define LLDP_MED_LOCID_INVALID 0 #define LLDP_MED_LOCID_COORDINATE 1 #define LLDP_MED_LOCID_CIVIC_ADDRESS 2 #define LLDP_MED_LOCID_ECS_ELIN 3 #define LLDP_MED_LOCID_FORMAT_INVALID(t) (((t) == 0) || ((t) > 3)) /* IEEE 802.1Qaz Organizationally Specific TLV Subtypes */ #define LLDP_8021QAZ_ETSCFG 9 #define LLDP_8021QAZ_ETSREC 10 #define LLDP_8021QAZ_PFC 11 #define LLDP_8021QAZ_APP 12 /* IEEE 802.3 Organizationally Specific TLV Subtypes: 802.1AB-2005 Annex G */ #define LLDP_8023_RESERVED 0 #define LLDP_8023_MACPHY_CONFIG_STATUS 1 #define LLDP_8023_POWER_VIA_MDI 2 #define LLDP_8023_LINK_AGGREGATION 3 #define LLDP_8023_MAXIMUM_FRAME_SIZE 4 /* 802.3AB-2005 G.2.1 Table G-2 */ #define LLDP_8023_MACPHY_AUTONEG_SUPPORT (1 << 0) #define LLDP_8023_MACPHY_AUTONEG_ENABLED (1 << 1) /* 802.3AB-2005 G.4.1 Table G-4 */ #define LLDP_8023_LINKAGG_CAPABLE (1 << 0) #define LLDP_8023_LINKAGG_ENABLED (1 << 1) /* IEEE 802.1Qbg subtype */ #define LLDP_EVB_SUBTYPE 0 /* Draft 0.1 value */ #define LLDP_EVB22_SUBTYPE 0xd /* Standardized value */ #define LLDP_VDP_SUBTYPE 0x2 /* forwarding mode */ #define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7) #define LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY (1 << 6) /* EVB supported protocols */ #define LLDP_EVB_CAPABILITY_PROTOCOL_RTE (1 << 2) #define LLDP_EVB_CAPABILITY_PROTOCOL_ECP (1 << 1) #define LLDP_EVB_CAPABILITY_PROTOCOL_VDP (1 << 0) /* EVB specific values */ #define LLDP_EVB_DEFAULT_MAX_VSI 65535 #define LLDP_EVB_DEFAULT_SVSI 3295 #define LLDP_EVB_DEFAULT_RTE 15 #define LLDP_EVB_DEFAULT_MAX_RTE 31 void somethingChangedLocal(const char *ifname, int type); #endif /* _LLDP_H */ lldpad-0.9.46/include/lldp_8021qaz.h000066400000000000000000000144641215142747300170060ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_8021QAZ_H #define _LLDP_8021QAZ_H #include "lldp.h" #include "lldp_mod.h" #include "lldp_tlv.h" #include "linux/dcbnl.h" #define LLDP_MOD_8021QAZ ((OUI_IEEE_8021 << 8) | IEEE8021QAZ_ETSCFG_TLV) #define MAX_USER_PRIORITIES 8 #define MAX_TCS 8 #define BW_PERCENT 100 /* maximum number of application entries allowed to be * configured in an application TLV. */ #define MAX_APP_ENTRIES 32 #define DEFAULT_SUBTYPE 0 #define INIT_IEEE8021QAZ_OUI {0x00, 0x80, 0xc2} /* IEEE8021QAZ TLV Definitions */ #define IEEE8021QAZ_ETSCFG_TLV 9 #define IEEE8021QAZ_ETSREC_TLV 10 #define IEEE8021QAZ_PFC_TLV 11 #define IEEE8021QAZ_APP_TLV 12 #define IEEE8021QAZ_SETTING "ieee8021qaz" #define TLV_HEADER_LENGTH 2 /* Received TLV types */ #define RCVD_IEEE8021QAZ_TLV_ETSCFG 0x0001 #define RCVD_IEEE8021QAZ_TLV_ETSREC 0x0002 #define RCVD_IEEE8021QAZ_TLV_PFC 0x0004 #define RCVD_IEEE8021QAZ_TLV_APP 0x0008 #define RCVD_LLDP_IEEE8021QAZ_TLV 0x0200 /* Duplicate TLV types */ #define DUP_IEEE8021QAZ_TLV_ETSCFG 0x0001 #define DUP_IEEE8021QAZ_TLV_ETSREC 0x0002 #define DUP_IEEE8021QAZ_TLV_PFC 0x0004 #define DUP_IEEE8021QAZ_TLV_APP 0x0008 /* Transmission selection algorithm identifiers */ #define IEEE8021Q_TSA_STRICT 0x0 #define IEEE8021Q_TSA_CBSHAPER 0x1 #define IEEE8021Q_TSA_ETS 0x2 #define IEEE8021Q_TSA_VENDOR 0xFF /* Flags */ #define IEEE8021QAZ_SET_FLAGS(_FlagsVar, _BitsToSet) \ ((_FlagsVar) = (_FlagsVar) | (_BitsToSet)) #define IEEE8021QAZ_TEST_FLAGS(_FlagsVar, _Mask, _BitsToCheck) \ (((_FlagsVar) & (_Mask)) == (_BitsToCheck)) /* APP internal state */ #define IEEE_APP_SET 0 #define IEEE_APP_DEL 1 #define IEEE_APP_DONE 2 /* ETSCFG WCRT field's Shift values */ #define ETS_WILLING_SHIFT 7 #define ETS_CBS_SHIFT 6 enum state { INIT, RX_RECOMMEND }; /* Packed TLVs */ struct ieee8021qaz_tlv_etscfg { u8 oui[OUI_SIZE]; u8 subtype; u8 wcrt; /* Willing-Cbs-Reserved-maxTc fields */ u32 prio_map; /* Priority Assignment Table */ u8 tc_bw[MAX_TCS]; /* TC Bandwidth Table */ u8 tsa_map[MAX_TCS]; /* Transmission Selection Algorithm Table */ } __attribute__ ((__packed__)); struct ieee8021qaz_tlv_etsrec { u8 oui[OUI_SIZE]; u8 subtype; u8 reserved; u32 prio_map; /* Priority Assignment Table */ u8 tc_bw[MAX_TCS]; /* TC Bandwidth Table */ u8 tsa_map[MAX_TCS]; /* Transmission Selection Algorithm Table */ } __attribute__ ((__packed__)); struct ieee8021qaz_tlv_pfc { u8 oui[OUI_SIZE]; u8 subtype; u8 wmrc; /* Willing-Mbc-Reserved-pfcCap fields */ u8 pfc_enable; /* PFC Enable */ } __attribute__ ((__packed__)); struct ieee8021qaz_tlv_app { u8 oui[OUI_SIZE]; u8 subtype; u8 reserved; } __attribute__ ((__packed__)); struct app_prio { u8 prs; /* Priority-Reserved-Selection fields */ u16 pid; } __attribute__ ((__packed__)); /* ETS Configuration Object */ struct etscfg_obj { bool willing; bool cbs; u8 max_tcs; u32 prio_map; u8 tc_bw[MAX_TCS]; u8 tsa_map[MAX_TCS]; }; /* ETS Recommendation Object */ struct etsrec_obj { u32 prio_map; u8 tc_bw[MAX_TCS]; u8 tsa_map[MAX_TCS]; }; /* PFC Object */ struct pfc_obj { /* DCBX PFC Params */ bool willing; bool mbc; u8 pfc_cap; u8 pfc_enable; u32 delay; }; /* Application Objects */ struct app_obj { struct dcb_app app; bool peer; int hw; LIST_ENTRY(app_obj) entry; }; /* @oper_param - 0: local_params, 1: remote_params * @remote_param - 0: NULL */ struct ets_attrib { bool pending; bool current_state; struct etscfg_obj *cfgl; struct etsrec_obj *recl; struct etscfg_obj *cfgr; struct etsrec_obj *recr; }; struct pfc_attrib { bool pending; bool current_state; struct pfc_obj local; struct pfc_obj remote; bool remote_param; }; struct ieee8021qaz_unpkd_tlvs { struct unpacked_tlv *ieee8021qaz; struct unpacked_tlv *etscfg; struct unpacked_tlv *etsrec; struct unpacked_tlv *pfc; struct unpacked_tlv *app; }; struct ieee8021qaz_tlvs { bool active; bool pending; u16 ieee8021qazdu; u8 local_mac[ETH_ALEN]; u8 remote_mac[ETH_ALEN]; char ifname[IFNAMSIZ]; struct ieee8021qaz_unpkd_tlvs *rx; struct ets_attrib *ets; struct pfc_attrib *pfc; LIST_HEAD(app_tlv_head, app_obj) app_head; struct port *port; LIST_ENTRY(ieee8021qaz_tlvs) entry; }; struct ieee8021qaz_user_data { LIST_HEAD(ieee8021qaz_head, ieee8021qaz_tlvs) head; }; int ieee8021qaz_mod_app(struct app_tlv_head *head, int peer, u8 prio, u8 sel, u16 proto, u32 ops); int ieee8021qaz_app_sethw(char *ifname, struct app_tlv_head *head); inline int get_prio_map(u32 prio_map, int tc); inline void set_prio_map(u32 *prio_map, u8 prio, int tc); struct ieee8021qaz_tlvs *ieee8021qaz_data(const char *); int ieee8021qaz_tlvs_rxed(const char *ifname); int ieee8021qaz_check_active(const char *ifname); struct lldp_module *ieee8021qaz_register(void); void ieee8021qaz_unregister(struct lldp_module *mod); struct packed_tlv *ieee8021qaz_gettlv(struct port *port, struct lldp_agent *); int ieee8021qaz_rchange(struct port *port, struct lldp_agent *, struct unpacked_tlv *tlv); void ieee8021qaz_ifup(char *ifname, struct lldp_agent *); void ieee8021qaz_ifdown(char *ifname, struct lldp_agent *); u8 ieee8021qaz_mibDeleteObject(struct port *port, struct lldp_agent *); inline int ieee8021qaz_clif_cmd(void *data, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf); int ieee8021qaz_check_operstate(void); #endif /* _LLDP_8021QAZ_H */ lldpad-0.9.46/include/lldp_8021qaz_clif.h000066400000000000000000000024251215142747300177750ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_IEEE8021QAZ_CLIF_H #define _LLDP_IEEE8021QAZ_CLIF_H struct lldp_module *ieee8021qaz_cli_register(void); void ieee8021qaz_cli_unregister(struct lldp_module *); int ieee8021qaz_print_tlv(u32, u16, char *); #endif lldpad-0.9.46/include/lldp_8021qaz_cmds.h000066400000000000000000000030521215142747300200030ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_IEEE8021QAZ_CMDS_H #define _LLDP_IEEE8021QAZ_CMDS_H #include #include "clif_msgs.h" #define ARG_WILLING "willing" #define ARG_DCBX_MODE "mode" /* ETS */ #define ARG_ETS_NUMTCS "numtcs" #define ARG_ETS_UP2TC "up2tc" #define ARG_ETS_TCBW "tcbw" #define ARG_ETS_TSA "tsa" /* PFC */ #define ARG_PFC_ENABLED "enabled" #define ARG_PFC_DELAY "delay" #define ARG_PFC_NUMTCS "numtcs" /* APP */ #define ARG_APP "APP" struct arg_handlers *ieee8021qaz_get_arg_handlers(); #endif lldpad-0.9.46/include/lldp_8023.h000066400000000000000000000034201215142747300162620ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_8023_H #define _LLDP_8023_H #include "lldp_mod.h" #include "lldp_tlv.h" #define LLDP_MOD_8023 OUI_IEEE_8023 struct ieee8023_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; struct unpacked_tlv *maccfg; struct unpacked_tlv *powvmdi; struct unpacked_tlv *linkagg; struct unpacked_tlv *maxfs; LIST_ENTRY(ieee8023_data) entry; }; struct ieee8023_user_data { LIST_HEAD(ieee8023_head, ieee8023_data) head; }; struct lldp_module *ieee8023_register(void); void ieee8023_unregister(struct lldp_module *mod); struct packed_tlv *ieee8023_gettlv(struct port *, struct lldp_agent *); void ieee8023_ifdown(char *, struct lldp_agent *); void ieee8023_ifup(char *, struct lldp_agent *); #endif /* _LLDP_8023_H */ lldpad-0.9.46/include/lldp_8023_clif.h000066400000000000000000000023771215142747300172710ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_8023_CLIF_H #define _LLDP_8023_CLIF_H struct lldp_module *ieee8023_cli_register(void); void ieee8023_cli_unregister(struct lldp_module *); int ieee8023_print_tlv(u32, u16, char *); #endif lldpad-0.9.46/include/lldp_8023_cmds.h000066400000000000000000000022421215142747300172710ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_8023_CMDS_H #define _LLDP_8023_CMDS_H struct arg_handlers *ieee8023_get_arg_handlers(); #endif lldpad-0.9.46/include/lldp_basman.h000066400000000000000000000035051215142747300171330ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_BASMAN_H #define _LLDP_BASMAN_H #include "lldp.h" #include "lldp_mod.h" #include "lldp_tlv.h" #define LLDP_MOD_BASIC 2 struct basman_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; struct unpacked_tlv *portdesc; struct unpacked_tlv *sysname; struct unpacked_tlv *sysdesc; struct unpacked_tlv *syscaps; struct unpacked_tlv *manaddr[MANADDR_MAX]; int macnt; LIST_ENTRY(basman_data) entry; }; struct basman_user_data { LIST_HEAD(basman_head, basman_data) head; }; struct lldp_module *basman_register(void); void basman_unregister(struct lldp_module *mod); struct packed_tlv *basman_gettlv(struct port *, struct lldp_agent *); void basman_ifdown(char *, struct lldp_agent *); void basman_ifup(char *, struct lldp_agent *); #endif /* _LLDP_BASMAN_H */ lldpad-0.9.46/include/lldp_basman_clif.h000066400000000000000000000024701215142747300201300ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_BASMAN_CLIF_H #define _LLDP_BASMAN_CLIF_H struct lldp_module *basman_cli_register(void); void basman_cli_unregister(struct lldp_module *); int basman_print_tlv(u32, u16, char *); #define ARG_IPV4_ADDR "ipv4" #define ARG_IPV6_ADDR "ipv6" #endif lldpad-0.9.46/include/lldp_basman_cmds.h000066400000000000000000000022441215142747300201400ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_BASMAN_CMDS_H #define _LLDP_BASMAN_CMDS_H struct arg_handlers *basman_get_arg_handlers(); #endif lldpad-0.9.46/include/lldp_cisco_clif.h000066400000000000000000000024061215142747300177660ustar00rootroot00000000000000/******************************************************************************* Implementation of Cisco Specific TLVs for LLDP (c) Copyright SuSE Linux Products GmbH 2011 Author(s): Hannes Reinecke This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_CISCO_CLIF_H #define _LLDP_CISCO_CLIF_H struct lldp_module *cisco_cli_register(void); void cisco_cli_unregister(struct lldp_module *); int cisco_print_tlv(u32, u16, char *); #endif /* _LLDP_CISCO_CLIF_H */ lldpad-0.9.46/include/lldp_dcbx.h000066400000000000000000000052361215142747300166150ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_DCBX_H #define _LLDP_DCBX_H #include #include #include "lldpad.h" #define LLDP_MOD_DCBX 0x001b2101 struct dcbx_manifest { struct unpacked_tlv *dcbx1; struct unpacked_tlv *dcbx2; struct unpacked_tlv *dcbx_ctrl; struct unpacked_tlv *dcbx_pg; struct unpacked_tlv *dcbx_pfc; struct unpacked_tlv *dcbx_app; struct unpacked_tlv *dcbx_llink; }; struct dcbx_tlvs { bool active; bool rxed_tlvs; bool operup; u16 dcbdu; u8 dcbx_st; char ifname[IFNAMSIZ]; struct dcbx_manifest *manifest; struct unpacked_tlv *dcbx1; struct unpacked_tlv *dcbx2; struct unpacked_tlv *control; struct unpacked_tlv *pg1; struct unpacked_tlv *pg2; struct unpacked_tlv *pfc1; struct unpacked_tlv *pfc2; struct unpacked_tlv *app1; struct unpacked_tlv *app2; struct unpacked_tlv *llink; struct port *port; LIST_ENTRY(dcbx_tlvs) entry; }; struct dcbd_user_data { LIST_HEAD(dcbx_head, dcbx_tlvs) head; }; struct dcbx_tlvs *dcbx_data(const char *); int dcbx_tlvs_rxed(const char *ifname, struct lldp_agent *); int dcbx_check_active(const char *ifname); int dcbx_get_legacy_version(const char *ifname); struct packed_tlv *dcbx_gettlv(struct port *, struct lldp_agent *); int dcbx_rchange(struct port *, struct lldp_agent *, struct unpacked_tlv *); u8 dcbx_mibDeleteObjects(struct port *, struct lldp_agent *); void dcbx_ifup(char *, struct lldp_agent *); void dcbx_ifdown(char *, struct lldp_agent *); struct lldp_module *dcbx_register(void); void dcbx_unregister(struct lldp_module *); int dcbx_clif_cmd(void *, struct sockaddr_un *, socklen_t , char *, int, char *, int); #endif /* _LLDP_DCBX_H */ lldpad-0.9.46/include/lldp_dcbx_cfg.h000066400000000000000000000033241215142747300174300ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_DCBX_CFG_H_ #define _LLDP_DCBX_CFG_H_ #define DCBX_SETTING "dcbx" /* Enumerations used to set 'dcb_enable' configuration values. * The DCBX default case is to enable DCBX and configure hardware * when receiving a DCBX TLV from the peer. DISABLED and ENABLED * bits are provided to force a preferred strategy. */ enum { LLDP_DCBX_DISABLED = 0, LLDP_DCBX_ENABLED = 1, LLDP_DCBX_DEFAULT = 2 }; int dcbx_default_cfg_file(void); int get_dcb_enable_state(char *device_name, int *result); int save_dcb_enable_state(char *device_name, int dcb_enable); int get_dcbx_version(int *result); int save_dcbx_version(int dcbx_version); #endif // _LLDP_DCBX_CFG_H_ lldpad-0.9.46/include/lldp_dcbx_clif.h000066400000000000000000000023631215142747300176100ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_DCBX_CLIF_H #define _LLDP_DCBX_CLIF_H struct lldp_module *dcbx_cli_register(void); void dcbx_cli_unregister(struct lldp_module *); int dcbx_print_tlv(u32, u16, char *); #endif lldpad-0.9.46/include/lldp_dcbx_cmds.h000066400000000000000000000113341215142747300176170ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef CLIF_CMDS_H #define CLIF_CMDS_H #include #include #include "clif_msgs.h" struct arg_handlers *dcbx_get_arg_handlers(); void dont_advertise_dcbx_all(char *ifname, bool ad); #define CLIF_RSP_MSG_OFF 0 /* Client interface DCB command codes */ #define CMD_GET_CONFIG 1 #define CMD_SET_CONFIG 2 #define CMD_GET_OPER 3 #define CMD_GET_PEER 4 /* Client interface Feature codes */ #define FEATURE_DCB 1 #define FEATURE_PG 2 #define FEATURE_PFC 3 #define FEATURE_BCN 4 #define FEATURE_APP 5 #define FEATURE_LLINK 6 #define FEATURE_DCBX 0xfe #define FEATURE_PG_DESC 0xff /* DCB event message offsets (after port id field) */ #define EV_FEATURE_OFF 0 #define EV_SUBTYPE_OFF 2 #define EV_OP_MODE_CHG_OFF 4 #define EV_OP_CFG_CHG_OFF 5 /* Client interface DCB command message field offsets */ #define CLIF_CMD_OFF 0 #define CLIF_CMD_LEN 1 #define DCB_VER_OFF (CLIF_CMD_OFF + CLIF_CMD_LEN) #define DCB_VER_LEN 1 #define DCB_CMD_OFF (DCB_VER_OFF + DCB_VER_LEN) #define DCB_CMD_LEN 2 #define DCB_FEATURE_OFF (DCB_CMD_OFF + DCB_CMD_LEN) #define DCB_FEATURE_LEN 2 #define DCB_SUBTYPE_OFF (DCB_FEATURE_OFF + DCB_FEATURE_LEN) #define DCB_SUBTYPE_LEN 2 #define DCB_PORTLEN_OFF (DCB_SUBTYPE_OFF + DCB_SUBTYPE_LEN) #define DCB_PORTLEN_LEN 2 #define DCB_PORT_OFF (DCB_PORTLEN_OFF + DCB_PORTLEN_LEN) /* DCBX configuration commands do not need to have a port field */ #define DCBX_CFG_OFF (DCB_FEATURE_OFF + DCB_FEATURE_LEN) /* Offset into the DCB protocol data section of the client * interface message. * GetConfig and SetConfig commands */ #define CFG_ENABLE 0 #define CFG_ADVERTISE 1 #define CFG_WILLING 2 #define CFG_LEN 3 /* Offset into the DCB protocol data section of the client * interface message. * GetOper commands */ #define OPER_OPER_VER 0 #define OPER_MAX_VER 2 #define OPER_ERROR 4 #define OPER_OPER_MODE 6 #define OPER_SYNCD 7 #define OPER_LEN 8 /* Offset into the DCB protocol data section of the client * interface message. * GetPeer command */ #define PEER_ENABLE 0 #define PEER_WILLING 1 #define PEER_OPER_VER 2 #define PEER_MAX_VER 4 #define PEER_ERROR 6 #define PEER_SUBTYPE 7 #define PEER_LEN 8 /* DCB status data offset */ #define DCB_STATE 0 /* DCB State configuration data length */ #define CFG_DCB_DLEN 1 /* DCBX Version configuration data offset */ #define DCBX_VERSION 0 /* DCBX configuration data length * Does not include the Operational version */ #define CFG_DCBX_DLEN 1 /* Priority Flow Control configuration data offsets */ #define PFC_UP(i) (i) #define PFC_NUM_TC (PFC_UP(8)) /* Priority Flow Control configuration data length */ #define CFG_PFC_DLEN (PFC_NUM_TC+1) /* Priority Groups configuration data offsets */ #define PG_UP2TC(i) (i) #define PG_PG_PCNT(i) (PG_UP2TC(8) + (2*i)) #define PG_UP_PGID(i) (PG_PG_PCNT(8) + i) #define PG_UP_PCNT(i) (PG_UP_PGID(8) + (2*i)) #define PG_UP_STRICT(i) (PG_UP_PCNT(8) + i) #define PG_UP_NUM_TC (PG_UP_STRICT(8)) /* Priority Groups configuration data length */ #define CFG_PG_DLEN (PG_UP_NUM_TC+1) /* Application configuration data offsets */ #define APP_LEN 0 #define APP_DATA 2 /* Priority group description data offsets */ #define PG_DESC_PGID 0 #define PG_DESC_LEN 1 #define PG_DESC_DATA 3 #define PG_DESC_GET_DLEN 1 #define PG_DESC_SET_DLEN 3 /* Logical link data offsets */ #define LLINK_STATUS 0 #define LLINK_DLEN 1 #define CLIF_NOT_SUPPLIED 'x' #define CHANGED "changed" #define NOCHANGE "no change" #endif /* CLIF_CMDS_H */ lldpad-0.9.46/include/lldp_dcbx_nl.h000066400000000000000000000104421215142747300173010ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _DRV_CFG_H #define _DRV_CFG_H #include /* DCB configuration commands */ enum { DCB_C_UNDEFINED, DCB_C_GSTATE, DCB_C_SSTATE, DCB_C_PG_STATS, DCB_C_PGTX_GCFG, DCB_C_PGTX_SCFG, DCB_C_PGRX_GCFG, DCB_C_PGRX_SCFG, DCB_C_PFC_GCFG, DCB_C_PFC_SCFG, DCB_C_PFC_STATS, DCB_C_GLINK_SPD, DCB_C_SLINK_SPD, DCB_C_SET_ALL, DCB_C_GPERM_HWADDR, __DCB_C_ENUM_MAX, }; #define IXGBE_DCB_C_MAX (__DCB_C_ENUM_MAX - 1) /* DCB configuration attributes */ enum { DCB_A_UNDEFINED = 0, DCB_A_IFNAME, DCB_A_STATE, DCB_A_PFC_STATS, DCB_A_PFC_CFG, DCB_A_PG_STATS, DCB_A_PG_CFG, DCB_A_LINK_SPD, DCB_A_SET_ALL, DCB_A_PERM_HWADDR, __DCB_A_ENUM_MAX, }; #define IXGBE_DCB_A_MAX (__DCB_A_ENUM_MAX - 1) /* PERM HWADDR attributes */ enum { PERM_HW_A_UNDEFINED, PERM_HW_A_0, PERM_HW_A_1, PERM_HW_A_2, PERM_HW_A_3, PERM_HW_A_4, PERM_HW_A_5, PERM_HW_A_ALL, __PERM_HW_A_ENUM_MAX, }; #define IXGBE_DCB_PERM_HW_A_MAX (__PERM_HW_A_ENUM_MAX - 1) /* PFC configuration attributes */ enum { PFC_A_UP_UNDEFINED, PFC_A_UP_0, PFC_A_UP_1, PFC_A_UP_2, PFC_A_UP_3, PFC_A_UP_4, PFC_A_UP_5, PFC_A_UP_6, PFC_A_UP_7, PFC_A_UP_MAX, /* Used as an iterator cap */ PFC_A_UP_ALL, __PFC_A_UP_ENUM_MAX, }; #define IXGBE_DCB_PFC_A_UP_MAX (__PFC_A_UP_ENUM_MAX - 1) /* Priority Group Traffic Class and Bandwidth Group * configuration attributes */ enum { PG_A_UNDEFINED, PG_A_TC_0, PG_A_TC_1, PG_A_TC_2, PG_A_TC_3, PG_A_TC_4, PG_A_TC_5, PG_A_TC_6, PG_A_TC_7, PG_A_TC_MAX, /* Used as an iterator cap */ PG_A_TC_ALL, PG_A_BWG_0, PG_A_BWG_1, PG_A_BWG_2, PG_A_BWG_3, PG_A_BWG_4, PG_A_BWG_5, PG_A_BWG_6, PG_A_BWG_7, PG_A_BWG_MAX, /* Used as an iterator cap */ PG_A_BWG_ALL, __PG_A_ENUM_MAX, }; #define IXGBE_DCB_PG_A_MAX (__PG_A_ENUM_MAX - 1) enum { TC_A_PARAM_UNDEFINED, TC_A_PARAM_STRICT_PRIO, TC_A_PARAM_BW_GROUP_ID, TC_A_PARAM_BW_PCT_IN_GROUP, TC_A_PARAM_UP_MAPPING, TC_A_PARAM_MAX, /* Used as an iterator cap */ TC_A_PARAM_ALL, __TC_A_PARAM_ENUM_MAX, }; #define IXGBE_DCB_TC_A_PARAM_MAX (__TC_A_PARAM_ENUM_MAX - 1) #define IXGBE_NLTYPE_DCB_NAME "IXGBE_DCB" enum strict_prio_type { prio_none = 0, prio_group, prio_link }; struct tc_config { __u8 bwgid; __u8 up_to_tc_bitmap; __u8 prio_type; __u8 tc_percent; }; /* Data structures (and macros) needed that are not including in any library. */ #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) #define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN)) #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) /* Maximum size of response requested or message sent */ #define MAX_MSG_SIZE 4096 int deinit_drv_if(void); int get_perm_hwaddr(const char *ifname, u8 *buf_perm, u8 *buf_san); int set_hw_all(char *ifname); int set_hw_state(char *device_name, int dcb_state); int get_hw_state(char *device_name, int *dcb_state); int init_drv_if(void); bool check_port_dcb_mode(char *device_name); int set_dcbx_mode(char *ifname, __u8 mode); #endif lldpad-0.9.46/include/lldp_ecp.h000066400000000000000000000051621215142747300164420ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef LLDP_ECP_H #define LLDP_ECP_H #include #include "lldp_mod.h" #include "lldp_vdp.h" #define ECP_SUBTYPE 0x0 #define ECP_MAX_RETRIES 3 #define ECP_SEQUENCE_NR_START 0x0 #define MSECS 1000 #define SECS (1000 * MSECS) #define ECP_ACK_TIMER_DEFAULT (500 * MSECS) /* 500 ms */ #define ECP_LOCALCHANGE_TIMEOUT (1 * MSECS) /* 1 ms */ #define ECP_ACK_TIMER_STOPPED (-1) typedef enum { ECP_REQUEST = 0, ECP_ACK } ecp_mode; struct ecp_buffer { /* ECP payload buffer */ u8 frame[ETH_FRAME_LEN]; /* Payload buffer */ u16 frame_len; /* # of bytes of valid data */ u8 state; /* Buffer state */ u8 localChange; /* Status changed */ u8 rcvFrame; /* True if new frame received */ }; struct ecp { struct l2_packet_data *l2; int sequence; int retries; int ackReceived; int ackTimer; u16 lastSequence; u16 seqECPDU; struct ecp_buffer rx; /* Receive buffer */ struct ecp_buffer tx; /* Transmit buffer */ struct agentstats stats; char ifname[IFNAMSIZ]; /* Interface name */ }; struct ecp_hdr { u8 oui[3]; u8 pad1; u16 subtype; u8 mode; u16 seqnr; } __attribute__ ((__packed__)); enum { ECP_TX_INIT_TRANSMIT, ECP_TX_TRANSMIT_ECPDU, ECP_TX_WAIT_FOR_ACK, ECP_TX_REQUEST_PDU }; enum { ECP_RX_IDLE, ECP_RX_INIT_RECEIVE, ECP_RX_RECEIVE_WAIT, ECP_RX_RECEIVE_ECPDU, ECP_RX_SEND_ACK, ECP_RX_RESEND_ACK, }; struct vdp_data; void ecp_somethingChangedLocal(struct vdp_data *, bool); void ecp_rx_send_ack_frame(struct vdp_data *); int ecp_init(char *); int ecp_deinit(char *); #endif /* _ECP_H */ lldpad-0.9.46/include/lldp_ecp22.h000066400000000000000000000114641215142747300166100ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef LLDP_ECP22_H #define LLDP_ECP22_H #include #include "lldp_mod.h" #include "lldp_qbg22.h" enum { /* ECP Receive states */ ECP22_RX_BEGIN, ECP22_RX_WAIT, ECP22_RX_WAIT2, ECP22_RX_FIRST, ECP22_RX_REC_ECPDU, ECP22_RX_NEW_ECPDU, ECP22_RX_SEND_ACK }; enum { /* ECP Transmit states */ ECP22_TX_BEGIN, ECP22_TX_INIT, ECP22_TX_TXMIT_ECPDU, ECP22_TX_WAIT_FORREQ, ECP22_TX_WAIT_ONDATA, ECP22_TX_ERROR }; enum { ECP22_REQUEST = 0, ECP22_ACK } ecp22_mode; struct ecp22_hdr { /* ECP22 header */ u16 ver_op_sub; /* ECP22 version, operation, subtype */ u16 seqno; /* ECP22 sequence number */ } __attribute__ ((__packed__)); /* * Define maximum ECP protocol payload length. Leave room for END TLV. */ #define ECP22_MAXPAYLOAD_LEN (ETH_DATA_LEN - sizeof(struct ecp22_hdr) - 2) struct ecp22_buffer { /* ECP payload buffer */ unsigned char frame[ETH_FRAME_LEN]; /* Payload buffer */ unsigned short frame_len; /* # of bytes of valid data */ unsigned char state; /* Buffer state machine */ unsigned char ecpdu_received; /* True when packet received */ unsigned char ack_received; /* True when packet acknowledged */ unsigned char retries; /* # of retries */ unsigned short last_seqno; /* Seqno last acknowledged packet */ unsigned short seqno; /* Seqno this packet */ unsigned long errors; /* # of transmit errors */ }; struct ecp22_payload_node { /* ECP Payload node */ struct packed_tlv *ptlv; /* Pointer to packed TLV to send */ unsigned short subtype; /* ECP subtype*/ unsigned char mac[ETH_ALEN]; /* Destination MAC address */ LIST_ENTRY(ecp22_payload_node) node; }; /* * ECP22 payload data */ typedef LIST_HEAD(ecp22_list, ecp22_payload_node) ecp22_list; struct ecp22_usedlist { /* List of valid ecp_payload_nodes */ ecp22_list head; /* ECP payload data free list */ struct ecp22_payload_node *last; /* Ptr to last entry in list */ }; struct ecp22_freelist { /* List of free ecp_payload_nodes */ ecp22_list head; /* ECP payload data free list */ u16 freecnt; /* # of nodes on freelist */ }; enum { ecp22_maxpayload = 64 }; struct ecp22 { /* ECP protocol data per interface */ struct l2_packet_data *l2; char ifname[IFNAMSIZ]; /* Interface name */ LIST_ENTRY(ecp22) node; /* Successor */ struct ecp22_buffer rx; /* Receive buffer */ struct ecp22_buffer tx; /* Transmit buffer */ struct agentstats stats; struct ecp22_usedlist inuse; /* List of payload data */ struct ecp22_freelist isfree; /* List of free payload nodes */ unsigned char max_retries; /* Max # of retries (via EVB) */ unsigned char max_rte; /* Wait time for ack (via EVB) */ }; struct ecp22_user_data { /* ECP module data per interface */ LIST_HEAD(ecp_head, ecp22) head; }; /* * Function prototypes */ struct lldp_module *ecp22_register(void); void ecp22_unregister(struct lldp_module *); void ecp22_stop(char *); void ecp22_start(char *); /* * Functions to set and read ecp header operations field. */ static inline void ecp22_hdr_set_op(struct ecp22_hdr *p, unsigned int op) { p->ver_op_sub &= 0xf3ff; p->ver_op_sub |= (op & 0x3) << 10; } static inline unsigned int ecp22_hdr_read_op(struct ecp22_hdr *p) { return (p->ver_op_sub >> 10) & 3; } /* * Functions to set and read ecp header subtype field. */ static inline void ecp22_hdr_set_subtype(struct ecp22_hdr *p, unsigned int sub) { p->ver_op_sub &= 0xfc00; p->ver_op_sub |= sub & 0x3ff; } static inline unsigned int ecp22_hdr_read_subtype(struct ecp22_hdr *p) { return p->ver_op_sub & 0x3ff; } /* * Functions to set and read ecp header version field. */ static inline void ecp22_hdr_set_version(struct ecp22_hdr *p, unsigned int ver) { p->ver_op_sub &= 0xfff; p->ver_op_sub |= (ver & 0xf) << 12; } static inline unsigned int ecp22_hdr_read_version(struct ecp22_hdr *p) { return (p->ver_op_sub >> 12) & 0xf; } #endif lldpad-0.9.46/include/lldp_evb.h000066400000000000000000000046641215142747300164550ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_EVB_H #define _LLDP_EVB_H #include "lldp_mod.h" #define LLDP_MOD_EVB OUI_IEEE_8021Qbg #define LLDP_OUI_SUBTYPE { 0x00, 0x1b, 0x3f, 0x00 } struct tlv_info_evb { /* EVB TLV definition */ u8 oui[3]; u8 sub; u8 smode; /* supported forwarding mode */ u8 scap; /* supported capabilities */ u8 cmode; /* currently configured forwarding mode */ u8 ccap; /* currently configured capabilities */ u16 svsi; /* supported no. of vsi */ u16 cvsi; /* currently configured no. of vsi */ u8 rte; /* retransmission exponent */ } __attribute__ ((__packed__)); struct evb_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; bool txmit; /* True when EVB transmits enabled */ bool vdp_start; /* True when VDP module started */ struct tlv_info_evb tie; /* Currently supported */ struct tlv_info_evb last; /* Last received */ struct tlv_info_evb policy; /* Local policy */ LIST_ENTRY(evb_data) entry; }; struct evb_user_data { LIST_HEAD(evb_head, evb_data) head; }; struct lldp_module *evb_register(void); void evb_unregister(struct lldp_module *); u8 evb_conf_fmode(char *, enum agent_type); u8 evb_conf_capa(char *, enum agent_type); u8 evb_conf_rte(char *, enum agent_type); u16 evb_conf_vsis(char *, enum agent_type); int evb_conf_enabletx(char *, enum agent_type); struct evb_data *evb_data(char *, enum agent_type); #endif /* _LLDP_EVB_H */ lldpad-0.9.46/include/lldp_evb22.h000066400000000000000000000144401215142747300166120ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2012 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_EVB22_H #define _LLDP_EVB22_H #include "lldp_mod.h" #include "lldp_qbg22.h" #define LLDP_MOD_EVB22_SUBTYPE 0xd #define LLDP_MOD_EVB22_OUI { 0x00, 0x80, 0xc2, LLDP_MOD_EVB22_SUBTYPE } enum { /* EVB bit definitions defines */ EVB_BGID = 0x4, /* Bridge group ID */ EVB_RRCAP = 0x2, /* Bridge reflective relay capability */ EVB_RRCTR = 0x1, /* Bridge reflective relay control */ EVB_SGID = 0x8, /* Station group ID */ EVB_RRREQ = 0x4, /* Station reflective relay request */ EVB_RRSTAT_YES = 0x2, /* Station reflective relay status TRUE */ EVB_RRSTAT_NO = 0x0, /* Station reflective relay status NO */ EVB_RRSTAT_DONT = 0x1, /* Station reflective relay status unknown */ EVB_ROL = 0x20, /* Remote or local indicator */ EVB_BRIDGE = 0x1, /* EVB Bridge */ EVB_STATION = 0x2 /* EVB Station */ }; /* * Function to set and extract maximum retries */ static inline u8 evb_maskoff_retries(u8 value) { return value & 0x1f; } static inline u8 evb_set_retries(u8 value) { return (value & 7) << 5; } static inline u8 evb_ex_retries(u8 value) { return (value >> 5) & 7; } /* * Function to set and extract retransmission exponent. */ static inline u8 evb_maskoff_rte(u8 value) { return value & 0xe0; } static inline u8 evb_set_rte(u8 value) { return value & 0x1f; } static inline u8 evb_ex_rte(u8 value) { return value & 0x1f; } /* * Function to set and extract evb mode. */ static inline u8 evb_maskoff_evbmode(u8 value) { return value & 0x3f; } static inline u8 evb_set_evbmode(u8 value) { return (value & 3) << 6; } static inline u8 evb_ex_evbmode(u8 value) { return (value >> 6) & 3; } /* * Function to set and extract remote/local flag. */ static inline u8 evb_set_rol(u8 value) { return (value & 1) << 5; } static inline u8 evb_ex_rol(u8 value) { return (value >> 5) & 1; } /* * Function to set and extract resource wait delay. */ static inline u8 evb_maskoff_rwd(u8 value) { return value & 0xe0; } static inline u8 evb_set_rwd(u8 value) { return value & 0x1f; } static inline u8 evb_ex_rwd(u8 value) { return value & 0x1f; } /* * Function to set and extract reinit keep alive. */ static inline u8 evb_maskoff_rka(u8 value) { return value & 0xe0; } static inline u8 evb_set_rka(int value) { return value & 0x1f; } static inline u8 evb_ex_rka(u8 value) { return value & 0x1f; } /* * Function to set and extract reflective relay status. */ static inline u8 evb_maskoff_rrstat(u8 value) { return value & 0xfc; } static inline u8 evb_set_rrstat(int value) { return value & 3; } static inline u8 evb_ex_rrstat(u8 value) { return value & 3; } /* * Function to set and extract reflective relay request. */ static inline u8 evb_maskoff_rrreq(int value) { return value & 0xfb; } static inline u8 evb_set_rrreq(int value) { return (value & 1) << 2; } static inline u8 evb_ex_rrreq(u8 value) { return (value >> 2) & 1; } /* * Function to set and extract station group id. */ static inline u8 evb_maskoff_sgid(int value) { return value & 0xf7; } static inline u8 evb_set_sgid(int value) { return (value & 1) << 3; } static inline u8 evb_ex_sgid(u8 value) { return (value >> 3) & 1; } /* * Function to set and extract bridge reflective relay capability. */ static inline u8 evb_maskoff_rrcap(int value) { return value & 0xfd; } static inline u8 evb_set_rrcap(int value) { return (value & 1) << 1; } static inline u8 evb_ex_rrcap(u8 value) { return (value >> 1) & 1; } /* * Function to set and extract reflective relay control. */ static inline u8 evb_set_rrctr(int value) { return value & 1; } static inline u8 evb_ex_rrctr(u8 value) { return value & 1; } /* * Function to set and extract bridge group id. */ static inline u8 evb_maskoff_bgid(int value) { return value & 0xfb; } static inline u8 evb_set_bgid(int value) { return (value & 1) << 2; } static inline u8 evb_ex_bgid(u8 value) { return (value >> 2) & 1; } struct evb22_tlv { /* EVB TLV definition */ u8 oui[3]; u8 sub; u8 bridge_s; /* Bridge status */ u8 station_s; /* Station status */ u8 r_rte; /* Retries and retransmssion exponent */ u8 evb_mode; /* Evb-mode, remove/local and resource wait delay */ u8 rl_rka; /* Remote/local and reinit keep alive */ } __attribute__ ((__packed__)); struct evb22_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; bool txmit; /* True when EVB transmits enabled */ bool vdp_start; /* True when VDP module started */ struct evb22_tlv out; /* Currently supported */ struct evb22_tlv last; /* Last received */ struct evb22_tlv policy; /* Local policy */ LIST_ENTRY(evb22_data) entry; }; struct evb22_user_data { LIST_HEAD(evb22_head, evb22_data) head; }; /* * Function Prototypes */ struct lldp_module *evb22_register(void); void evb22_unregister(struct lldp_module *); struct evb22_data *evb22_data(char *, enum agent_type); struct arg_handlers *evb22_get_arg_handlers(void); int evb22_conf_enabletx(char *, enum agent_type); int evb22_conf_evbmode(char *, enum agent_type); int evb22_conf_rrreq(char *, enum agent_type); int evb22_conf_rrcap(char *, enum agent_type); int evb22_conf_retries(char *, enum agent_type); int evb22_conf_rwd(char *, enum agent_type); int evb22_conf_rte(char *, enum agent_type); int evb22_conf_rka(char *, enum agent_type); int evb22_conf_retries(char *, enum agent_type); int evb22_conf_gid(char *, enum agent_type); #endif lldpad-0.9.46/include/lldp_evb22_clif.h000066400000000000000000000023011215142747300176000ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #ifndef _LLDP_EVB22_CLIF_H #define _LLDP_EVB22_CLIF_H struct lldp_module *evb22_cli_register(void); #endif lldpad-0.9.46/include/lldp_evb_clif.h000066400000000000000000000022731215142747300174440ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #ifndef _LLDP_EVB_CLIF_H #define _LLDP_EVB_CLIF_H struct lldp_module *evb_cli_register(void); #endif lldpad-0.9.46/include/lldp_evb_cmds.h000066400000000000000000000023021215142747300174460ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_EVB_CMDS_H #define _LLDP_EVB_CMDS_H struct arg_handlers *evb_get_arg_handlers(void); #endif lldpad-0.9.46/include/lldp_mand.h000066400000000000000000000050731215142747300166130ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MAND_H #define _LLDP_MAND_H #include "lldp_mod.h" #include "lldp_mand_cmds.h" #define LLDP_MOD_MAND 1 struct tlv_info_chassis { u8 sub; union { u8 ccomp[255]; u8 ifalias[255]; u8 pcomp[255]; u8 mac[6]; struct { u8 type; union { struct in_addr v4; struct in6_addr v6; } __attribute__ ((__packed__)) ip; } __attribute__ ((__packed__)) na; u8 ifname[255]; u8 local[255]; } __attribute__ ((__packed__)) id; } __attribute__ ((__packed__)); struct tlv_info_portid { u8 sub; union { u8 ifalias[255]; u8 pcomp[255]; u8 mac[6]; struct { u8 type; union { struct in_addr v4; struct in6_addr v6; } __attribute__ ((__packed__)) ip; } __attribute__ ((__packed__)) na; u8 ifname[255]; u8 circuit[255]; u8 local[255]; } __attribute__ ((__packed__)) id; } __attribute__ ((__packed__)); struct mand_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; struct unpacked_tlv *chassis; struct unpacked_tlv *portid; struct unpacked_tlv *ttl; struct unpacked_tlv *end; u8 rebuild_chassis:1; u8 rebuild_portid:1; bool read_shm; LIST_ENTRY(mand_data) entry; }; struct mand_user_data { LIST_HEAD(mand_head, mand_data) head; }; struct mand_data *mand_data(const char *, enum agent_type); struct lldp_module *mand_register(void); void mand_unregister(struct lldp_module *mod); struct packed_tlv *mand_gettlv(struct port *, struct lldp_agent *); void mand_ifdown(char *, struct lldp_agent *); void mand_ifup(char *, struct lldp_agent *); #endif /* _LLDP_MAND_H */ lldpad-0.9.46/include/lldp_mand_clif.h000066400000000000000000000037501215142747300176100ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MAND_CLIF_H #define _LLDP_MAND_CLIF_H #include "lldp.h" struct lldp_module *mand_cli_register(void); void mand_cli_unregister(struct lldp_module *); int mand_print_tlv(u32, u16, char *); typedef enum { cmd_getstats, cmd_gettlv, cmd_settlv, cmd_get_lldp, cmd_set_lldp, cmd_quit, cmd_license, cmd_version, cmd_help, cmd_ping, cmd_nop, } lldp_cmd; typedef enum { op_local = 0x0001, op_neighbor = 0x0002, op_arg = 0x0004, op_argval = 0x0008, op_config = 0x0010, op_delete = 0x0020, } lldp_op; struct tlv { u32 tlvid; char *infostr; }; #define TLVID_PREFIX "tlvid" #define ARG_ADMINSTATUS "adminStatus" #define VAL_RXTX "rxtx" #define VAL_RX "rx" #define VAL_TX "tx" #define VAL_DISABLED "disabled" #define VAL_INVALID "invalid" #define ARG_TLVTXENABLE "enableTx" #define ARG_TLVINFO "info" #define VAL_YES "yes" #define VAL_NO "no" #endif lldpad-0.9.46/include/lldp_mand_cmds.h000066400000000000000000000025131215142747300176150ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MAND_CMDS_H #define _LLDP_MAND_CMDS_H #define ARG_MAND_SUBTYPE "subtype" struct arg_handlers *mand_get_arg_handlers(); int mand_clif_cmd(void *data, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); #endif lldpad-0.9.46/include/lldp_med.h000066400000000000000000000037401215142747300164400ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MED_H #define _LLDP_MED_H #include "lldp.h" #include "lldp_mod.h" #include "lldp_tlv.h" #define LLDP_MOD_MED OUI_TIA_TR41 struct med_data { char ifname[IFNAMSIZ]; enum agent_type agenttype; struct unpacked_tlv *medcaps; struct unpacked_tlv *netpoli; struct unpacked_tlv *locid; struct unpacked_tlv *extpvm; struct unpacked_tlv *inv_hwrev; struct unpacked_tlv *inv_fwrev; struct unpacked_tlv *inv_swrev; struct unpacked_tlv *inv_serial; struct unpacked_tlv *inv_manufacturer; struct unpacked_tlv *inv_modelname; struct unpacked_tlv *inv_assetid; LIST_ENTRY(med_data) entry; }; struct med_user_data { LIST_HEAD(med_head, med_data) head; }; struct lldp_module *med_register(void); void med_unregister(struct lldp_module *mod); struct packed_tlv *med_gettlv(struct port *, struct lldp_agent *); void med_ifdown(char *, struct lldp_agent *); void med_ifup(char *, struct lldp_agent *); #endif /* _LLDP_MED_H */ lldpad-0.9.46/include/lldp_med_clif.h000066400000000000000000000027061215142747300174360ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MED_CLIF_H #define _LLDP_MED_CLIF_H struct lldp_module *med_cli_register(void); void med_cli_unregister(struct lldp_module *); int med_print_tlv(u32, u16, char *); #define ARG_MED_DEVTYPE "devtype" #define VAL_MED_NOT "none" #define VAL_MED_CLASS_I "class1" #define VAL_MED_CLASS_II "class2" #define VAL_MED_CLASS_III "class3" #define VAL_MED_NETCON "netcon" #endif lldpad-0.9.46/include/lldp_med_cmds.h000066400000000000000000000022331215142747300174420ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MED_CMDS_H #define _LLDP_MED_CMDS_H struct arg_handlers *med_get_arg_handlers(); #endif lldpad-0.9.46/include/lldp_mod.h000066400000000000000000000101131215142747300164420ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_MOD_H #define _LLDP_MOD_H #include #include #include "lldp_util.h" #include "ctrl_iface.h" /* * Ops structure for lldp module callbacks. * * @lldp_mod_register: return lldp_module struct with tlv or out+subtype * match types * @lldp_mod_unregister: cleanup * @lldp_mod_gettlv: return packed_tlv for core to append and xmit, * module is responsible for sanity checks the core * will only verify length. * @lldp_mod_rchange: core recv function passing changed tlv to module * @lldp_mod_utlv: update tlv called before each xmit * @lldp_mod_ifup: notification of rtnetlink LINK_UP event * @lldp_mod_ifdown: notification of rtnetlink LINK_DOWN event * @lldp_mod_recvrt: core passes raw rtnetlink messages * @client_register: any setup required for client interface * @client_cmd: process client commands from core lldp * @print_tlv: routine for client to pretty print TLVs * @lookup_tlv_name: find tlvid given a tlv 'name' * @get_arg_handler: return an arg handler list * @lldp_mod_notify: send data to a module */ struct lldp_mod_ops { struct lldp_module * (* lldp_mod_register)(void); void (* lldp_mod_unregister)(struct lldp_module *); struct packed_tlv * (* lldp_mod_gettlv)(struct port *, struct lldp_agent *); int (* lldp_mod_rchange)(struct port *, struct lldp_agent *, struct unpacked_tlv *); void (* lldp_mod_utlv)(struct port *); void (* lldp_mod_ifup)(char *, struct lldp_agent *); void (* lldp_mod_ifdown)(char *, struct lldp_agent *); u8 (* lldp_mod_mibdelete)(struct port *port, struct lldp_agent *); u32 (* client_register)(void); int (* client_cmd)(void *data, struct sockaddr_un *from, socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen); int (* print_tlv)(u32, u16, char *); u32 (* lookup_tlv_name)(char *); int (* print_help)(); int (* timer)(struct port *, struct lldp_agent *); struct arg_handlers * (* get_arg_handler)(void); int (*lldp_mod_notify)(int, char *, void *); }; /* * The lldp module structure * * lldp module per instance structure. Used by lldp core to * track available modules. Expect lldp core to create link * list of modules types per port. * */ struct lldp_module { int id; /* match tlv or oui+subtype */ u8 enable; /* TX only, RX only, TX+RX, Disabled */ char *path; /* shared library path */ void *dlhandle; /* dlopen handle for closing */ void *data; /* module specific data */ const struct lldp_mod_ops *ops; LIST_ENTRY(lldp_module) lldp; }; LIST_HEAD(lldp_head, lldp_module); struct lldp_head lldp_head; static inline struct lldp_module *find_module_by_id(struct lldp_head *head, int id) { struct lldp_module *mod; LIST_FOREACH(mod, head, lldp) { if (mod->id == id) return mod; } return NULL; } static inline void *find_module_user_data_by_id(struct lldp_head *head, int id) { struct lldp_module *mod; mod = find_module_by_id(head, id); if (mod) return mod->data; return NULL; } #endif /* _LLDP_MOD_H */ lldpad-0.9.46/include/lldp_orgspec_clif.h000066400000000000000000000024311215142747300203260ustar00rootroot00000000000000/******************************************************************************* Implementation of Organisation Specific TLVs for LLDP (c) Copyright SuSE Linux Products GmbH 2011 Author(s): Hannes Reinecke This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_ORGSPEC_CLIF_H #define _LLDP_ORGSPEC_CLIF_H struct lldp_module *orgspec_cli_register(void); void orgspec_cli_unregister(struct lldp_module *); int orgspec_print_tlv(u32, u16, char *); #endif /* _LLDP_ORGSPEC_CLIF_H */ lldpad-0.9.46/include/lldp_qbg22.h000066400000000000000000000055361215142747300166150ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ /* * Define IEEE 802.1Qbg module identification numbers and module interface * structures which are exchanged between all the qbg modules. * * Messages are sent from: * EVB --> ECP: Max number of retries (R) and retransmit timeout (RTE). * * EVB --> VDP: Max number of Reinit-keep-Alive (RKA) Resource wait delay (RWD) * and groupid support. * * VDP --> ECP: VSI Information as payload * ECP --> VDP: VSI Information as payload * * This is used in the module notify call back function. */ #ifndef LLDP_QBG22_H #define LLDP_QBG22_H /* * Modules Identifications */ #define LLDP_MOD_EVB22 0x80c2 #define LLDP_MOD_ECP22 0x80c3 #define LLDP_MOD_VDP22 0x80c4 enum { /* Identify data type in union below */ EVB22_TO_ECP22 = 1, /* Data from EVB to ECP */ EVB22_TO_VDP22 = 2, /* Data from EVB to VDP */ ECP22_TO_ULP = 3, /* Data from ECP to VDP, etc */ VDP22_TO_ECP22 = 4, /* Data from VDP to ECP */ /* ECP22 subtypes */ ECP22_VDP = 1, /* VDP protocol */ ECP22_PECSP = 2 /* Port extender control and status protocol */ }; struct evb22_to_ecp22 { /* Notification from EVB to ECP */ unsigned char max_retry;/* Max number of retries */ unsigned char max_rte; /* Max number of acknowledgement wait */ }; struct evb22_to_vdp22 { /* Notification from EVB to VDP */ unsigned char max_rwd; /* Max number of resource wait delay */ unsigned char max_rka; /* Max number of reinit keep alive */ unsigned char gpid; /* Support group ids in VDP */ }; struct ecp22_to_ulp { /* Notification from ECP to VDP, etc */ unsigned short len; /* Size of bytestream */ void *data; /* Pointer to data */ }; struct qbg22_imm { /* Intermodule message data structure */ int data_type; /* Identifies union data */ union { /* Overlay possible data */ struct evb22_to_ecp22 a; struct evb22_to_vdp22 b; struct ecp22_to_ulp c; } u; }; #endif lldpad-0.9.46/include/lldp_qbg_utils.h000066400000000000000000000027401215142747300176630ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ /* * Header file for small utility functions called throught qbg modules. */ #ifndef LLDP_QBG_UTILS_H #define LLDP_QBG_UTILS_H void hexdump_frame(const char *, char *, const unsigned char *, size_t); int modules_notify(int, int, char *, void *); /* * Required buffer space to display a UUID. * VDP_UUID_STRLEN = strlen("fa9b7fff-b0a0-4893-abcd-beef4ff18f8f") */ #define VDP_UUID_STRLEN 36 int vdp_uuid2str(const u8 *, char *, size_t); #endif lldpad-0.9.46/include/lldp_rtnl.h000066400000000000000000000032121215142747300166440ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_RTNL_H #define _LLDP_RTNL_H #ifndef IFNAMSIZ #define IFNAMSIZ 16 #endif #include "include/linux/netlink.h" /* * Helper functions to construct a netlink message. */ void mynla_nest_end(struct nlmsghdr *, struct nlattr *); struct nlattr *mynla_nest_start(struct nlmsghdr *, int); void mynla_put(struct nlmsghdr *, int, size_t, void *); void mynla_put_u16(struct nlmsghdr *, int, __u16); void mynla_put_u32(struct nlmsghdr *, int, __u32); int get_operstate(char *ifname); int set_operstate(char *ifname, __u8 operstate); int set_linkmode(const char *ifname, __u8 linkmode); #endif lldpad-0.9.46/include/lldp_tlv.h000066400000000000000000000102271215142747300164760ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_TLV_H #define _LLDP_TLV_H #include "lldp.h" #include "lldp/ports.h" #define ADDR2TLVSTR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define TLVSTR "%02x%02x%02x%02x%02x%02x" /* Localization OK */ #define TLV_OK 0 #define TLV_ERR 1 #define SUBTYPE_INVALID 2 /* Bit Mappers */ #define BIT0 0x01 #define BIT1 0x02 #define BIT2 0x04 #define BIT3 0x08 #define BIT4 0x10 #define BIT5 0x20 #define BIT6 0x40 #define BIT7 0x80 #define TYPE_0 0 #define TYPE_1 1 #define TYPE_2 2 #define TYPE_3 3 #define TYPE_4 4 #define TYPE_5 5 #define TYPE_6 6 #define TYPE_7 7 #define TYPE_8 8 #define TYPE_127 127 /* Received TLV types */ #define RCVD_LLDP_TLV_TYPE0 0x0001 #define RCVD_LLDP_TLV_TYPE1 0x0002 #define RCVD_LLDP_TLV_TYPE2 0x0004 #define RCVD_LLDP_TLV_TYPE3 0x0008 #define RCVD_LLDP_TLV_TYPE4 0x0010 #define RCVD_LLDP_TLV_TYPE5 0x0020 #define RCVD_LLDP_TLV_TYPE6 0x0040 #define RCVD_LLDP_TLV_TYPE7 0x0080 #define RCVD_LLDP_TLV_TYPE8 0x0100 /* Protocol EtherTypes */ #define PROTO_ID_FCOE 0x0689 /* network byte order */ #define PROTO_ID_ISCSI 0xBC0C /* network byte order, 3260 dec */ #define PROTO_ID_FIP 0x1489 /* network byte order */ /* Protocol Selector Field */ #define PROTO_ID_L2_ETH_TYPE 0x00 #define PROTO_ID_SOCK_NUM 0x01 #define PROTO_ID_RESERVED1 0x02 #define PROTO_ID_RESERVED2 0x03 #define PROTO_ID_OUI_MASK 0xFC #define PROTO_ID_SF_TYPE 0x03 enum { CURRENT_PEER, LAST_PEER }; struct packed_tlv { u16 size; /* Of the entire tlv block */ u8 *tlv; /* tlv block */ }; struct unpacked_tlv { u8 type; u16 length; u8 *info; }; struct packed_tlv *pack_tlv(struct unpacked_tlv *tlv); struct unpacked_tlv *unpack_tlv(struct packed_tlv *tlv); int pack_tlv_after(struct unpacked_tlv *, struct packed_tlv *, int); struct unpacked_tlv *free_unpkd_tlv(struct unpacked_tlv *tlv); struct packed_tlv *free_pkd_tlv(struct packed_tlv *tlv); struct unpacked_tlv *create_tlv(void); struct packed_tlv *create_ptlv(void); struct unpacked_tlv *bld_end_tlv(void); struct packed_tlv *pack_end_tlv(void); int tlv_ok(struct unpacked_tlv *tlv); #define FREE_UNPKD_TLV(d, f) \ { \ if ((d)->f) \ (d)->f = free_unpkd_tlv((d)->f); \ } #define FREE_PKD_TLV(d, f) \ { \ if ((d)->f) \ (d)->f = free_pkd_tlv((d)->f); \ } #define PACK_TLV_AFTER(t, p, l, g) \ if (pack_tlv_after((t), (p), (l))) { \ fprintf(stderr, "### failed to pack " #t "\n"); \ goto g; \ } /* TLVID is oui << 8 | subtype */ #define TLVID(oui, sub) ((((oui) << 8) & 0xfffff00) | ((sub) & 0xff)) #define TLVID_NOUI(sub) TLVID(0, (sub)) #define TLVID_DCBX(sub) TLVID(OUI_IEEE_DCBX, (sub)) #define TLVID_8021(sub) TLVID(OUI_IEEE_8021, (sub)) #define TLVID_8023(sub) TLVID(OUI_IEEE_8023, (sub)) #define TLVID_MED(sub) TLVID(OUI_TIA_TR41, (sub)) #define TLVID_8021Qbg(sub) TLVID(OUI_IEEE_8021Qbg, (sub)) /* the size in bytes needed for a packed tlv from unpacked tlv */ #define TLVSIZE(t) ((t) ? (2 + (t)->length) : 0) #endif /* TLVS_H */ lldpad-0.9.46/include/lldp_util.h000066400000000000000000000147131215142747300166520ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDP_UTIL_H #define _LLDP_UTIL_H #include "lldp.h" #include "lldp/ports.h" #include #include #define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */ #define max(x,y) ((x>y)?(x):(y)) /* IETF RFC 4836 dot3MauType: http://tools.ietf.org/rfc/rfc4836.txt */ #define DOT3MAUTYPE_AUI 1 #define DOT3MAUTYPE_10Base5 2 #define DOT3MAUTYPE_Foirl 3 #define DOT3MAUTYPE_10Base2 4 #define DOT3MAUTYPE_10BaseT 5 #define DOT3MAUTYPE_10BaseFP 6 #define DOT3MAUTYPE_10BaseFB 7 #define DOT3MAUTYPE_10BaseFL 8 #define DOT3MAUTYPE_10Broad36 9 #define DOT3MAUTYPE_10BaseTHD 10 #define DOT3MAUTYPE_10BaseTFD 11 #define DOT3MAUTYPE_10BaseFLHD 12 #define DOT3MAUTYPE_10BaseFLFD 13 #define DOT3MAUTYPE_100BaseT4 14 #define DOT3MAUTYPE_100BaseTXHD 15 #define DOT3MAUTYPE_100BaseTXFD 16 #define DOT3MAUTYPE_100BaseFXHD 17 #define DOT3MAUTYPE_100BaseFXFD 18 #define DOT3MAUTYPE_100BaseT2HD 19 #define DOT3MAUTYPE_100BaseT2FD 20 #define DOT3MAUTYPE_1000BaseXHD 21 #define DOT3MAUTYPE_1000BaseXFD 22 #define DOT3MAUTYPE_1000BaseLXHD 23 #define DOT3MAUTYPE_1000BaseLXFD 24 #define DOT3MAUTYPE_1000BaseSXHD 25 #define DOT3MAUTYPE_1000BaseSXFD 26 #define DOT3MAUTYPE_1000BaseCXHD 27 #define DOT3MAUTYPE_1000BaseCXFD 28 #define DOT3MAUTYPE_1000BaseTHD 29 #define DOT3MAUTYPE_1000BaseTFD 30 #define DOT3MAUTYPE_10GBaseX 31 #define DOT3MAUTYPE_10GBaseLX4 32 #define DOT3MAUTYPE_10GBaseR 33 #define DOT3MAUTYPE_10GBaseER 34 #define DOT3MAUTYPE_10GBaseLR 35 #define DOT3MAUTYPE_10GBaseSR 36 #define DOT3MAUTYPE_10GBaseW 37 #define DOT3MAUTYPE_10GBaseEW 38 #define DOT3MAUTYPE_10GBaseLW 39 #define DOT3MAUTYPE_10GBaseSW 40 #define DOT3MAUTYPE_10GBaseCX4 41 #define DOT3MAUTYPE_2BaseTL 42 #define DOT3MAUTYPE_10PassTS 43 #define DOT3MAUTYPE_100BaseBX10D 44 #define DOT3MAUTYPE_100BaseBX10U 45 #define DOT3MAUTYPE_100BaseLX10 46 #define DOT3MAUTYPE_1000BaseBX10D 47 #define DOT3MAUTYPE_1000BaseBX10U 48 #define DOT3MAUTYPE_1000BaseLX10 49 #define DOT3MAUTYPE_1000BasePX10D 50 #define DOT3MAUTYPE_1000BasePX10U 51 #define DOT3MAUTYPE_1000BasePX20D 52 #define DOT3MAUTYPE_1000BasePX20U 53 /* IANA dot3MauType extension */ #define DOT3MAUTYPE_10GBaseT 54 #define DOT3MAUTYPE_10GBaseLRM 55 #define DOT3MAUTYPE_1000BaseKX 56 #define DOT3MAUTYPE_10GBaseKX4 57 #define DOT3MAUTYPE_10GBaseKR 58 #define DOT3MAUTYPE_10_1GBasePRXD1 59 #define DOT3MAUTYPE_10_1GBasePRXD2 60 #define DOT3MAUTYPE_10_1GBasePRXD3 61 #define DOT3MAUTYPE_10_1GBasePRXU1 62 #define DOT3MAUTYPE_10_1GBasePRXU2 63 #define DOT3MAUTYPE_10_1GBasePRXU3 64 #define DOT3MAUTYPE_10GBasePRD1 65 #define DOT3MAUTYPE_10GBasePRD2 66 #define DOT3MAUTYPE_10GBasePRD3 67 #define DOT3MAUTYPE_10GBasePRU1 68 #define DOT3MAUTYPE_10GBasePRU3 69 #define DOT3MAUTYPE_40GBaseKR4 70 #define DOT3MAUTYPE_40GBaseCR4 71 #define DOT3MAUTYPE_40GBaseSR4 72 #define DOT3MAUTYPE_40GBaseFR 73 #define DOT3MAUTYPE_40GBaseLR4 74 #define DOT3MAUTYPE_100GBaseCR10 75 #define DOT3MAUTYPE_100GBaseSR10 76 #define DOT3MAUTYPE_100GBaseLR4 77 #define DOT3MAUTYPE_100GBaseER4 78 int hexstr2bin(const char *hex, u8 *buf, size_t len); int bin2hexstr(const u8 *hex, size_t hexlen, char *buf, size_t buflen); int is_valid_lldp_device(const char *ifname); int is_active(const char *ifname); int is_bond(const char *ifname); int is_san_mac(u8 *addr); int is_bridge(const char *ifname); int is_vlan(const char *ifname); int is_vlan_capable(const char *ifname); int is_wlan(const char *ifname); int is_macvtap(const char *ifname); int is_valid_mac(const u8 *mac); int is_san_mac(u8 *addr); int is_ether(const char *ifname); int is_loopback(const char *ifname); int is_p2p(const char *ifname); int is_noarp(const char *ifname); int is_mbond(const char *ifname); int is_sbond(const char *ifname); int is_autoneg_enabled(const char *ifname); int is_autoneg_supported(const char *ifname); int get_mtu(const char *); int get_mfs(const char *); int get_ifflags(const char *); int get_maucaps(const char *); int get_mautype(const char *); int get_ifpflags(const char *); int get_iftype(const char *); int get_src_mac_from_bond(struct port *bond_port, char *ifname, u8 *addr); int get_mac(const char *ifname, u8 mac[]); int get_macstr(const char *ifname, char *addr, size_t size); int get_saddr(const char *ifname, struct sockaddr_in *saddr); int get_ipaddr(const char *ifname, struct in_addr *); int get_ipaddrstr(const char *ifname, char *ipaddr, size_t size); int get_saddr6(const char *ifname, struct sockaddr_in6 *saddr); int get_ipaddr6(const char *ifname, struct in6_addr *); int get_ipaddr6str(const char *ifname, char *ipaddr, size_t size); u16 get_caps(const char *ifname); int mac2str(const u8 *mac, char *dst, size_t size); int str2mac(const char *src, u8 *mac, size_t size); int str2addr(int domain, const char *src, void *dst, size_t size); int addr2str(int domain, const void *src, char *dst, size_t size); int is_slave(const char *ifmaster, const char *ifslave); int get_ifidx(const char *ifname); int get_master(const char *ifname); int get_addr(const char *ifname, int domain, void *buf); int check_link_status(const char *ifname); int get_arg_val_list(char *ibuf, int ilen, int *ioff, char **args, char **argvals); int get_arg_list(char *ibuf, int ilen, int *ioff, char **args); #define ntohll(x) be64_to_cpu(x) #define htonll(x) cpu_to_be64(x) #define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2])) #define hton24(p, v) do { \ p[0] = (((v) >> 16) & 0xFF); \ p[1] = (((v) >> 8) & 0xFF); \ p[2] = ((v) & 0xFF); \ } while (0) #endif /* _LLDP_UTIL_H */ lldpad-0.9.46/include/lldp_vdp.h000066400000000000000000000124531215142747300164650ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_VDP_H #define _LLDP_VDP_H #include "lldp_mod.h" #include "lldp_ecp.h" #define LLDP_MOD_VDP (OUI_IEEE_8021Qbg + 1) #define VDP_MODE_PREASSOCIATE 0x0 #define VDP_MODE_PREASSOCIATE_WITH_RR 0x1 #define VDP_MODE_ASSOCIATE 0x2 #define VDP_MODE_DEASSOCIATE 0x3 #define VDP_RESPONSE_SUCCESS 0x0 #define VDP_RESPONSE_INVALID_FORMAT 0x1 #define VDP_RESPONSE_INSUFF_RESOURCES 0x2 #define VDP_RESPONSE_UNUSED_VTID 0x3 #define VDP_RESPONSE_VTID_VIOLATION 0x4 #define VDP_RESPONSE_VTID_VER_VIOLATION 0x5 #define VDP_RESPONSE_OUT_OF_SYNC 0x6 #define VDP_RESPONSE_UNKNOWN 0xfe #define VDP_RESPONSE_NO_RESPONSE 0xff extern const char * const vsi_states[]; #define VDP_FILTER_INFO_FORMAT_VID 0x1 #define VDP_FILTER_INFO_FORMAT_MACVID 0x2 #define VDP_FILTER_INFO_FORMAT_GROUPVID 0x3 #define VDP_FILTER_INFO_FORMAT_GROUPMACVID 0x4 #define VDP_TIMER_GRANULARITY (100 * MSECS) /* 100 ms */ #define VDP_KEEPALIVE_TIMER_DEFAULT (10 * SECS) /* 10s */ #define VDP_ACK_TIMER_DEFAULT (2 * ECP_ACK_TIMER_DEFAULT * ECP_MAX_RETRIES) #define VDP_KEEPALIVE_TIMER_STOPPED (-1) #define VDP_ACK_TIMER_STOPPED (-1) #define VDP_LOCALCHANGE_TIMEOUT (1 * MSECS) /* 1 ms */ #define VDP_ROLE_STATION 0 #define VDP_ROLE_BRIDGE 1 enum { VSI_UNASSOCIATED = 0, VSI_ASSOC_PROCESSING, VSI_ASSOCIATED, VSI_PREASSOC_PROCESSING, VSI_PREASSOCIATED, VSI_DEASSOC_PROCESSING, VSI_EXIT, }; struct mac_vlan_p { u8 mac[6]; u16 vlan; } __attribute__ ((__packed__)); struct mac_vlan { /* MAC,VLAN entry anchored by profiles */ u8 mac[6]; u16 vlan; u8 qos; /* QOS field */ pid_t req_pid; /* PID of requester for this profile */ u32 req_seq; /* Seq # of requester for this profile */ LIST_ENTRY(mac_vlan) entry; }; struct tlv_info_vdp { /* VSI information in packet format */ u8 oui[3]; u8 sub; u8 mode; u8 response; u8 mgrid; u8 id[3]; u8 version; u8 instance[16]; u8 format; u16 entries; } __attribute__ ((__packed__)); struct vsi_profile { int mode; /* VSI profile association command */ int response; /* Response from switch */ u8 no_nlmsg; /* Don't send netlink msg on VSI_EXIT */ u8 mgrid; /* Profile mgr id */ int id; /* Profile id */ u8 version; /* Profile id version number */ u8 instance[16]; /* Profile UUID */ u8 format; /* Format of MAC,VLAN list */ u16 entries; /* Number of MAC,VLAN entries in macvid_head */ LIST_HEAD(macvid_head, mac_vlan) macvid_head; struct port *port; int ackTimer; /* VDP ACK timer interval */ int ackReceived; /* VDP ACK received for this profile */ int keepaliveTimer; /* VDP keepalive timer interval */ int state; /* State of VDP state machine for profile */ int seqnr; /* Seqnr of ECP packet this profile was sent */ bool localChange; /* True when state needs change */ bool remoteChange; /* True when switch caused profile change */ bool txmit; /* Profile transmitted */ LIST_ENTRY(vsi_profile) profile; }; struct vdp_data { char ifname[IFNAMSIZ]; u8 enabletx; u8 vdpbit_on; /* Enable VDP Protocol */ struct ecp ecp; struct unpacked_tlv *vdp; int role; int keepaliveTimer; int ackTimer; int nroftimers; LIST_HEAD(profile_head, vsi_profile) profile_head; LIST_ENTRY(vdp_data) entry; }; struct vdp_user_data { LIST_HEAD(vdp_head, vdp_data) head; }; struct lldp_module *vdp_register(void); void vdp_unregister(struct lldp_module *); struct vdp_data *vdp_data(char *); struct packed_tlv *vdp_gettlv(struct vdp_data *, struct vsi_profile *); void vdp_vsi_sm_station(struct vsi_profile *); struct vsi_profile *vdp_add_profile(struct vdp_data *, struct vsi_profile *); int vdp_remove_profile(struct vsi_profile *); void vdp_somethingChangedLocal(struct vsi_profile *, bool); void vdp_update(char *, u8); void vdp_ifup(char *, struct lldp_agent *); void vdp_ifdown(char *, struct lldp_agent *); void vdp_ack_profiles(struct vdp_data *, int); void vdp_advance_sm(struct vdp_data *); int vdp_indicate(struct vdp_data *, struct unpacked_tlv *); int vdp_vsis_pending(struct vdp_data *); int vdp_vsis(char *); const char *vdp_response2str(int); void vdp_trace_profile(struct vsi_profile *); struct vsi_profile *vdp_alloc_profile(void); void vdp_delete_profile(struct vsi_profile *); struct vsi_profile *vdp_find_profile(struct vdp_data *, struct vsi_profile *); #define MAC_ADDR_STRLEN 18 #endif /* _LLDP_VDP_H */ lldpad-0.9.46/include/lldp_vdp22.h000066400000000000000000000040351215142747300166260ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ /* * External interface definition for the ratified standard VDP protocol. */ #ifndef LLDP_VDP22_H #define LLDP_VDP22_H #include #include #include "lldp_mod.h" struct vsi22_profile { LIST_ENTRY(vsi22_profile) prof22_entry; }; struct vdp22 { /* Per interface VSI/VDP data */ char ifname[IFNAMSIZ]; /* Interface name */ unsigned char max_rwd; /* Max number of resource wait delay */ unsigned char max_rka; /* Max number of reinit keep alive */ unsigned char gpid; /* Supports group ids in VDP */ unsigned short input_len; /* Length of input data from ECP */ unsigned char input[ETH_DATA_LEN]; /* Input data from ECP */ LIST_HEAD(profile22_head, vsi22_profile) prof22_head; LIST_ENTRY(vdp22) entry; }; struct vdp22_user_data { /* Head for all VDP data */ LIST_HEAD(vdp22_head, vdp22) head; }; struct lldp_module *vdp22_register(void); void vdp22_unregister(struct lldp_module *); void vdp22_start(const char *); void vdp22_stop(char *); int vdp22_query(const char *); #endif lldpad-0.9.46/include/lldp_vdp_clif.h000066400000000000000000000023041215142747300174540ustar00rootroot00000000000000/******************************************************************************* Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_VDP_CLIF_H #define _LLDP_VDP_CLIF_H struct lldp_module *vdp_cli_register(void); #endif lldpad-0.9.46/include/lldp_vdp_cmds.h000066400000000000000000000027101215142747300174660ustar00rootroot00000000000000/******************************************************************************* implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2010 Author(s): Jens Osterkamp This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #ifndef _LLDP_VDP_CMDS_H #define _LLDP_VDP_CMDS_H struct arg_handlers *vdp_get_arg_handlers(); int vdp_clif_cmd(char *, int, char *, int); enum { MODE = 0, MGRID, TYPEID, TYPEIDVERSION, INSTANCEID, FORMAT, }; #define VAL_STATION "station" #define VAL_BRIDGE "bridge" #define ARG_VDP_MODE "mode" #define ARG_VDP_ROLE "role" #define VDP_PREFIX "vdp" #define VDP_BUF_SIZE 256 #endif lldpad-0.9.46/include/lldp_vdpnl.h000066400000000000000000000042631215142747300170170ustar00rootroot00000000000000/******************************************************************************* Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ /* * Definition of the VSI data structure received via netlink interface */ #ifndef LLDP_VDPNL_H #define LLDP_VDPNL_H #include #include #define MAX_PAYLOAD 4096 /* Maximum Payload Size */ struct vdpnl_mac { /* MAC-VLAN pair */ unsigned short vlan; /* Vlan identifier */ unsigned char mac[ETH_ALEN]; /* Mac address */ unsigned char qos; /* Quality of service */ }; struct vdpnl_vsi { /* Data structure for VSI data via netlink */ char ifname[IFNAMSIZ]; /* Interface name */ int ifindex; /* Index number */ unsigned char request; /* VSI request mode */ unsigned short response; /* VSI response code */ unsigned char vsi_mgrid; unsigned char vsi_typeversion; unsigned char vsi_uuid[PORT_UUID_MAX]; unsigned long vsi_typeid; unsigned long req_seq; pid_t req_pid; int macsz; /* Entries in mac-vlan pair list */ struct vdpnl_mac *maclist; /* List of MAC-VLAN pairs */ }; int vdpnl_recv(unsigned char *, size_t); int vdpnl_send(struct vdpnl_vsi *); int vdp_request(struct vdpnl_vsi *); int vdp22_request(struct vdpnl_vsi *); int vdp_status(int, struct vdpnl_vsi *); int event_trigger(struct nlmsghdr *, pid_t); #endif lldpad-0.9.46/include/lldpad.h000066400000000000000000000025711215142747300161210ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef LLDPAD_H #define LLDPAD_H #include #include #define PID_FILE "/var/run/lldpad.pid" void send_event(int level, __u32 type, char *ebuf); #endif /* LLDPAD_H */ lldpad-0.9.46/include/lldpad_shm.h000066400000000000000000000057701215142747300167740ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef LLDPAD_SHM_H #define LLDPAD_SHM_H #include #include "lldpad.h" #include "lldp_rtnl.h" #define LLDPAD_SHM_KEY ((('l'<<24) | ('l'<<16) | ('d'<<8) | ('p')) + 'a' + 'd' + 1) #define LLDPAD_SHM_SIZE 4096 /* PID value used to indicate pid field is uninitialized */ #define PID_NOT_SET 0 /* PID used to indicate that -k option has already run */ #define DONT_KILL_PID 1 void mark_lldpad_shm_for_removal(); pid_t lldpad_shm_getpid(); int lldpad_shm_setpid(pid_t pid); int lldpad_shm_get_msap(const char *device_name, int type, char *info, size_t *len); int lldpad_shm_set_msap(const char *device_name, int type, char *info, size_t len); int lldpad_shm_get_dcbx(const char *device_name); int lldpad_shm_set_dcbx(const char *device_name, int dcbx_mode); #define SHM_CHASSISID_LEN 32 #define SHM_PORTID_LEN 32 #define DCBX_AUTO 0 /* start DCBX in IEEE DCBX mode */ #define DCBX_LEGACY 1 /* start DCBX in legacy DCBX mode */ struct lldpad_shm_entry { char ifname[IFNAMSIZ+1]; char chassisid[SHM_CHASSISID_LEN]; int chassisid_len; char portid[SHM_PORTID_LEN]; int portid_len; dcbx_state st; u8 dcbx_mode; /* added to version 1 */ }; /* Version 0 of the SHM entry structure */ struct lldpad_shm_entry_ver0 { char ifname[IFNAMSIZ+1]; char chassisid[SHM_CHASSISID_LEN]; int chassisid_len; char portid[SHM_PORTID_LEN]; int portid_len; dcbx_state st; }; #define MAX_LLDPAD_SHM_ENTRIES_VER0 \ (LLDPAD_SHM_SIZE/sizeof(struct lldpad_shm_entry_ver0) - 1) #define MAX_LLDPAD_SHM_ENTRIES \ (LLDPAD_SHM_SIZE/sizeof(struct lldpad_shm_entry) - 1) #define SHM_NUM_ENT_MASK 0x0ffff #define SHM_VER_MASK 0x0ffff0000 #define SHM_VER_SHIFT 16 struct lldpad_shm_tbl { pid_t pid; u32 num_entries; /* High order 16 bits used as a version # */ struct lldpad_shm_entry ent[MAX_LLDPAD_SHM_ENTRIES]; }; struct lldpad_shm_tbl_ver0 { pid_t pid; u32 num_entries; /* High order 16 bits used as a version # */ struct lldpad_shm_entry_ver0 ent[MAX_LLDPAD_SHM_ENTRIES]; }; #endif lldpad-0.9.46/include/lldpad_status.h000066400000000000000000000030321215142747300175150ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef LLDPAD_STATUS_H #define LLDPAD_STATUS_H typedef enum { cmd_success = 0, cmd_failed, cmd_device_not_found, cmd_agent_not_found, cmd_invalid, cmd_bad_params, cmd_peer_not_present, cmd_ctrl_vers_not_compatible, cmd_not_capable, cmd_not_applicable, cmd_no_access, } cmd_status; #endif /* LLDPAD_STATUS_H */ lldpad-0.9.46/include/lldptool.h000066400000000000000000000031141215142747300165040ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDPTOOL_H #define _LLDPTOOL_H #include "clif.h" struct lldp_head lldp_cli_head; int hex2int(char *b); int clif_command(struct clif *clif, char *cmd, int raw); void print_raw_message(char *msg, int print); int parse_print_message(char *msg, int print); void print_event_msg(char *buf); void print_response(char *buf, int status); int parse_response(char *buf); /* void print_dcb_cmd_response(char *buf, cmd_status status); int handle_dcb_cmds(struct clif *clif, int argc, char *argv[], int raw); */ #endif /* _LLDPTOOL_H */ lldpad-0.9.46/include/lldptool_cli.h000066400000000000000000000027661215142747300173470ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _LLDPTOOL_CLI_H_ #define _LLDPTOOL_CLI_H_ #include "clif_msgs.h" int cli_cmd_getstats(struct clif *, int, char **, struct cmd *, int); int cli_cmd_gettlv(struct clif *, int, char **, struct cmd *, int); int cli_cmd_settlv(struct clif *, int, char **, struct cmd *, int); int cli_cmd_getlldp(struct clif *, int, char **, struct cmd *, int); int cli_cmd_setlldp(struct clif *, int, char **, struct cmd *, int); #endif /* _LLDPTOOL_CLI_H_ */ lldpad-0.9.46/include/messages.h000066400000000000000000000030241215142747300164620ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _MESSAGES_H_ #define _MESSAGES_H_ #include #include extern bool daemonize; extern int loglvl; void log_message(int loglvl, const char *pFormat, ...) __attribute__((__format__(__printf__, 2, 3))); #define LLDPAD_ERR(...) log_message(LOG_ERR, __VA_ARGS__) #define LLDPAD_WARN(...) log_message(LOG_WARNING, __VA_ARGS__) #define LLDPAD_INFO(...) log_message(LOG_INFO, __VA_ARGS__) #define LLDPAD_DBG(...) log_message(LOG_DEBUG, __VA_ARGS__) #endif lldpad-0.9.46/include/parse_cli.h000066400000000000000000000047451215142747300166270ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _PARSE_CLI_H_ #define _PARSE_CLI_H_ #define GOT_ADVERTISE 0x0001 #define GOT_ENABLE 0x0002 #define GOT_WILLING 0x0004 #define GOT_UP2TC 0x0008 #define GOT_PGPCT 0x0010 #define GOT_PGID 0x0020 #define GOT_UPPCT 0x0040 #define GOT_STRICT 0x0080 #define GOT_LLSTATUS 0x0100 #define GOT_APPCFG 0x0200 #define GOT_PFCUP 0x0400 int get_port_len(void); int *get_up2tc(void); int *get_pgpct(void); int *get_pgid(void); int *get_uppct(void); int *get_strict(void); int *get_pfcup(void); char *get_appcfg(void); int get_enable(void); int get_dcb_param(void); int get_dcbx_param(void); int get_advertise(void); int get_willing(void); int get_cmd(void); int get_fargs(void); int get_feature(void); int get_subtype(void); int get_desc_id(void); char *get_desc_str(void); void free_desc_str(void); int *get_rp(void); int *get_bcna(void); int get_rp_alpha(float *); int get_rp_beta(float *); int get_rp_gd(float *); int get_rp_gi(float *); int get_rp_tmax(unsigned int *); int get_rp_td(unsigned int *); int get_rp_rmin(unsigned int *); int get_rp_w(unsigned int *); int get_rp_rd(unsigned int *); int get_rp_ru(unsigned int *); int get_rp_wrtt(unsigned int *); int get_rp_ri(unsigned int *); int get_llstatus(void); char *get_port(void); void free_port(void); void free_appcfg(void); char *get_parse_error(void); void free_parse_error(void); void init_parse_state(void); int parse_dcb_cmd(char *buf); #endif /* _PARSE_CLI_H_ */ lldpad-0.9.46/include/tlv_dcbx.h000066400000000000000000000202721215142747300164640ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _TLV_DCBX_H_ #define _TLV_DCBX_H_ #include "lldp.h" #include "dcb_types.h" #include "lldp_dcbx.h" /* Received TLV types */ #define RCVD_LLDP_DCBX1_TLV 0x0200 #define RCVD_LLDP_DCBX2_TLV 0x0400 #define RCVD_DCBX1_TLV_CTRL 0x0001 #define RCVD_DCBX2_TLV_CTRL 0x0002 #define RCVD_DCBX_TLV_PG 0x0004 #define RCVD_DCBX_TLV_PFC 0x0008 #define RCVD_DCBX_TLV_APP 0x0020 #define RCVD_DCBX_TLV_LLINK 0x0040 /* DCB TLV Definitions */ #define DCB_CONTROL_TLV 1 #define DCB_PRIORITY_GROUPS_TLV 2 #define DCB_PRIORITY_FLOW_CONTROL_TLV 3 #define DCB_BCN_TLV 4 #define DCB_APPLICATION_TLV 5 #define DCB_LLINK_TLV 6 #define DCB_CONTROL_TLV2 1 #define DCB_PRIORITY_GROUPS_TLV2 2 #define DCB_PRIORITY_FLOW_CONTROL_TLV2 3 #define DCB_APPLICATION_TLV2 4 /* DCBX CTRL TLV byte offsets */ #define DCBX_CTRL_OPER_VER_OFFSET 0 #define DCBX_CTRL_MAX_VER_OFFSET (DCBX_CTRL_OPER_VER_OFFSET + sizeof(u8)) #define DCBX_CTRL_SEQNO_OFFSET (DCBX_CTRL_MAX_VER_OFFSET + sizeof(u8)) #define DCBX_CTRL_ACKNO_OFFSET (DCBX_CTRL_SEQNO_OFFSET + sizeof(u32)) #define DCBX_CTRL_LEN sizeof(struct dcb_control_info) struct dcbx_tlv_header { u8 oper_version; u8 max_version; u8 ewe; u8 sub_type; }; /* DCBX TLV HEADER byte offsets */ #define DCBX_HDR_OPER_VERSION_OFFSET 0 #define DCBX_HDR_MAX_VERSION_OFFSET 1 #define DCBX_HDR_EWE_OFFSET 2 #define DCBX_HDR_SUB_TYPE_OFFSET 3 #define DCB_PGID_BYTES MAX_BANDWIDTH_GROUPS/2 #pragma pack(1) /*packon*/ struct dcbx1_pg_cfg { u8 pg_percent[MAX_BANDWIDTH_GROUPS];/* % of link BW per BWG */ struct { u8 byte1; /* :3 BWG ID */ /* :2 strict_prio 2 (1: LSP, 2: GSP, 3: reserved */ /* :3 padding */ u8 byte2; /* percentage of BWG bandwidth */ } up_cfg[MAX_USER_PRIORITIES];/* Index is user priority */ }; struct dcbx2_pg_cfg { u8 pg_ids[DCB_PGID_BYTES]; /* byte 0 :4 PG ID for UP 0 */ /* :4 PG ID for UP 1 */ /* byte 1 :4 PG ID for UP 2 */ /* :4 PG ID for UP 3 */ /* byte 2 :4 PG ID for UP 4 */ /* :4 PG ID for UP 5 */ /* byte 3 :4 PG ID for UP 6 */ /* :4 PG ID for UP 7 */ u8 pg_percent[MAX_BANDWIDTH_GROUPS];/* % of link BW per PGID */ u8 num_tcs; }; #pragma pack() /*packoff*/ /* DCBX PG TLV byte offsets */ #define DCBX1_PG_PERCENT_OFFSET sizeof(struct dcbx_tlv_header) #define DCBX1_PG_SETTINGS_OFFSET (DCBX1_PG_PERCENT_OFFSET + \ sizeof(u8)*MAX_BANDWIDTH_GROUPS) #define BYTE1_OFFSET 0 #define BYTE2_OFFSET 1 #define DCBX2_PG_PGID_UP sizeof(struct dcbx_tlv_header) #define DCBX2_PG_PERCENT_OFFSET (DCBX2_PG_PGID_UP + \ sizeof(u8)*DCB_PGID_BYTES) #define DCBX2_PG_NUM_TC_OFFSET (DCBX2_PG_PERCENT_OFFSET + \ sizeof(u8)*MAX_BANDWIDTH_GROUPS) #pragma pack(1) /*packon*/ struct dcbx1_pfc_cfg { u8 admin_map; /* bitmap of admin mode, bit position is user priority */ }; struct dcbx2_pfc_cfg { u8 admin_map; /* bitmap of admin mode, bit position is user priority */ u8 num_tcs; }; #pragma pack() /*packoff*/ /* DCBX PFC TLV byte offsets */ #define DCBX_PFC_MAP_OFFSET sizeof(struct dcbx_tlv_header) #define DCBX2_PFC__NUM_TC_OFFSET (DCBX_PFC_MAP_OFFSET + sizeof(u8)) #pragma pack(1) /*packon*/ struct dcbx2_app_cfg { u16 prot_id; u8 byte1; /* :6 high 7 bits of OUI */ /* :2 selector field */ u16 low_oui; /* low 16 bits of OUI */ u8 up_map; }; #pragma pack() /*packoff*/ /* DCBX APP TLV byte offsets */ #define DCBX1_APP_DATA_OFFSET sizeof(struct dcbx_tlv_header) #define DCBX2_APP_DATA_OFFSET sizeof(struct dcbx_tlv_header) /* To support looping, these do *not* include the header */ #define DCBX2_APP_PROTO_OFFSET 0 #define DCBX2_APP_BYTE1_OFFSET (DCBX2_APP_PROTO_OFFSET + sizeof(u16)) #define DCBX2_APP_LOW_OUI_OFFSET1 (DCBX2_APP_BYTE1_OFFSET + sizeof(u8)) #define DCBX2_APP_LOW_OUI_OFFSET2 (DCBX2_APP_LOW_OUI_OFFSET1 + sizeof(u8)) #define DCBX2_APP_UP_MAP_OFFSET (DCBX2_APP_LOW_OUI_OFFSET2 + sizeof(u8)) #pragma pack(1) /*packon*/ struct dcbx1_pg_info { struct dcbx_tlv_header hdr; struct dcbx1_pg_cfg data; }; struct dcbx2_pg_info { struct dcbx_tlv_header hdr; struct dcbx2_pg_cfg data; }; #pragma pack() /*packoff*/ #define DCBX1_PG_LEN sizeof(struct dcbx1_pg_info) #define DCBX2_PG_LEN sizeof(struct dcbx2_pg_info) #pragma pack(1) /*packon*/ struct dcbx1_pfc_info { struct dcbx_tlv_header hdr; struct dcbx1_pfc_cfg data; }; struct dcbx2_pfc_info { struct dcbx_tlv_header hdr; struct dcbx2_pfc_cfg data; }; #pragma pack() /*packoff*/ #define DCBX1_PFC_LEN sizeof(struct dcbx1_pfc_info) #define DCBX2_PFC_LEN sizeof(struct dcbx2_pfc_info) #pragma pack(1) /*packon*/ struct dcbx1_app_info { struct dcbx_tlv_header hdr; u8 data[]; }; struct dcbx2_app_info { struct dcbx_tlv_header hdr; struct dcbx2_app_cfg data[]; }; #pragma pack() /*packoff*/ #define DCBX1_APP_LEN DCBX1_APP_DATA_OFFSET #define DCBX2_APP_SIZE sizeof(struct dcbx2_app_cfg) #define DCBX2_APP_LEN (sizeof(struct dcbx2_app_info)) #pragma pack(1) /*packon*/ struct dcbx_llink_cfg { u8 byte1; /* :1 - logical link status */ }; /* :7 - reserved */ #pragma pack() /*packoff*/ /* DCB_TLV TYPE 6 byte offset */ #define DCBX_LLINK_STATUS_OFFSET sizeof(struct dcbx_tlv_header) #pragma pack(1) /*packon*/ struct dcbx_llink_info { struct dcbx_tlv_header hdr; struct dcbx_llink_cfg data; }; #pragma pack() /*packoff*/ #define DCBX_LLINK_LEN sizeof(struct dcbx_llink_info) /* Organizationally Unique Identifier */ #define DCB_OUI_LEN 3 #define OUI_SUBTYPE_LEN 1 struct dcb_tlv { u8 oui[DCB_OUI_LEN]; u8 oui_subtype; }; #pragma pack(1) /*packon*/ struct dcb_control_info { u8 oper_version; u8 max_version; u32 seqno; u32 ackno; }; #pragma pack() /*packoff*/ struct unpacked_tlv *bld_dcbx1_tlv(struct dcbx_tlvs *dcbx); struct unpacked_tlv *bld_dcbx2_tlv(struct dcbx_tlvs *dcbx); struct unpacked_tlv *bld_dcbx_ctrl_tlv(struct dcbx_tlvs *dcbx); struct unpacked_tlv *bld_dcbx1_pg_tlv(struct dcbx_tlvs *, bool *success); struct unpacked_tlv *bld_dcbx2_pg_tlv(struct dcbx_tlvs *, bool *success); struct unpacked_tlv *bld_dcbx1_pfc_tlv(struct dcbx_tlvs *, bool *success); struct unpacked_tlv *bld_dcbx2_pfc_tlv(struct dcbx_tlvs *, bool *success); struct unpacked_tlv *bld_dcbx1_app_tlv(struct dcbx_tlvs *dcbx, u32 sub_type, bool *success); struct unpacked_tlv *bld_dcbx2_app_tlv(struct dcbx_tlvs *, bool *success); struct unpacked_tlv *bld_dcbx_llink_tlv(struct dcbx_tlvs *, u32 sub_type, bool *success); bool unpack_dcbx1_tlvs(struct port *, struct lldp_agent *, struct unpacked_tlv *); bool unpack_dcbx2_tlvs(struct port *, struct lldp_agent *, struct unpacked_tlv *); bool process_dcbx_ctrl_tlv(struct port *, struct lldp_agent *); bool process_dcbx_pg_tlv(struct port *, struct lldp_agent *); bool process_dcbx_pfc_tlv(struct port *, struct lldp_agent *); bool process_dcbx_app_tlv(struct port *, struct lldp_agent *); bool process_dcbx_llink_tlv(struct port *, struct lldp_agent *); #endif lldpad-0.9.46/include/version.h.in000066400000000000000000000023311215142747300167450ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef VERSION_H #define VERSION_H #define VERSION_STR "@VERSION@" #define DCBTOOL_VERSION VERSION_STR #define LLDPTOOL_VERSION VERSION_STR #endif /* VERSION_H */ lldpad-0.9.46/liblldp_clif.pc.in000066400000000000000000000003261215142747300164310ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: liblldp_clif Description: A Shared library to communicate with lldpad Version: 1.0.0 Libs: -L${libdir} -llldp_clif Cflags: lldpad-0.9.46/list.c000066400000000000000000000035511215142747300142030ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include "list.h" struct ls_entry *ls_find(struct lshead *head, char *key) { struct ls_entry *p = NULL; for (p = head->lh_first; p != NULL; p = p->entries.le_next) if (!strcmp(p->key, key)) return p; return p; } list_rvals ls_insert(struct lshead *head, char *key, void *value) { struct ls_entry *entry; entry = (struct ls_entry *)malloc(sizeof(struct ls_entry)); if (!entry) return ls_failed; entry->key = key; entry->value = value; LIST_INSERT_HEAD(head, entry, entries); return ls_ok; } void ls_erase(struct lshead *head, char *key) { struct ls_entry *p; p = ls_find(head, key); if (p) LIST_REMOVE(p, entries); } void ls_remove_list(struct lshead *head) { while (head->lh_first != NULL) LIST_REMOVE(head->lh_first, entries); } lldpad-0.9.46/lldp/000077500000000000000000000000001215142747300140135ustar00rootroot00000000000000lldpad-0.9.46/lldp/agent.c000066400000000000000000000122251215142747300152570ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include "ports.h" #include "eloop.h" #include "states.h" #include "lldp_tlv.h" #include "messages.h" #include "lldp/l2_packet.h" #include "lldp_mod.h" #include "config.h" #include "lldp_mand_clif.h" #include "lldp/agent.h" static const u8 * agent_groupmacs[AGENT_MAX] = { nearest_bridge, nearest_nontpmr_bridge, nearest_customer_bridge, }; static const char *agent_sections[AGENT_MAX] = { [NEAREST_BRIDGE] = "nearest_bridge", [NEAREST_NONTPMR_BRIDGE] = "nearest_nontpmr_bridge", [NEAREST_CUSTOMER_BRIDGE] = "nearest_customer_bridge", }; struct lldp_agent * lldp_agent_find_by_type(const char *ifname, enum agent_type type) { struct port *port; struct lldp_agent *agent; port = port_find_by_name(ifname); if (port == NULL) return NULL; LIST_FOREACH(agent, &port->agent_head, entry) { if (agent->type == type) return agent; } return NULL; } const char *agent_type2section(int agenttype) { if ((agenttype > NEAREST_BRIDGE) && (agenttype < AGENT_MAX)) return agent_sections[agenttype]; else return LLDP_SETTING; } void lldp_init_agent(struct port *port, struct lldp_agent *agent, int type) { char macstring[30]; memset(agent, 0, sizeof(struct lldp_agent)); memcpy(&agent->mac_addr, agent_groupmacs[type], ETH_ALEN); mac2str(agent->mac_addr, macstring, 30); LLDPAD_DBG("%s: creating new agent for %s (%s).\n", __func__, port->ifname, macstring); /* Initialize relevant agent variables */ agent->tx.state = TX_LLDP_INITIALIZE; agent->rx.state = LLDP_WAIT_PORT_OPERATIONAL; agent->type = type; if (get_config_setting(port->ifname, type, ARG_ADMINSTATUS, (void *)&agent->adminStatus, CONFIG_TYPE_INT)) { LLDPAD_DBG("%s: agent->adminStatus = disabled.\n", __func__); agent->adminStatus = disabled; } /* init & enable RX path */ rxInitializeLLDP(port, agent); /* init TX path */ txInitializeTimers(agent); txInitializeLLDP(agent); } int lldp_add_agent(const char *ifname, enum agent_type type) { int count; struct port *port; struct lldp_agent *agent, *newagent; port = port_find_by_name(ifname); if (port == NULL) return -1; /* check if lldp_agents for this if already exist */ count = 0; LIST_FOREACH(agent, &port->agent_head, entry) { count++; if (agent->type != type) continue; else return -1; } /* if not, create one and initialize it */ LLDPAD_DBG("%s(%i): creating new agent for port %s.\n", __func__, __LINE__, ifname); newagent = (struct lldp_agent *)malloc(sizeof(struct lldp_agent)); if (newagent == NULL) { LLDPAD_DBG("%s(%i): creation of new agent failed !.\n", __func__, __LINE__); return -1; } lldp_init_agent(port, newagent, type); LIST_INSERT_HEAD(&port->agent_head, newagent, entry); LLDPAD_DBG("%s: %i agents on if %s.\n", __func__, count, port->ifname); return 0; } static void timer(UNUSED void *eloop_data, UNUSED void *user_ctx) { struct lldp_module *n; struct lldp_agent *agent; struct port *port = porthead; while (port != NULL) { /* execute rx and tx sm for all agents on a port */ LIST_FOREACH(agent, &port->agent_head, entry) { char macstring[30]; mac2str(&agent->mac_addr[0], macstring, 29); update_tx_timers(agent); run_tx_timers_sm(port, agent); run_tx_sm(port, agent); run_rx_sm(port, agent); update_rx_timers(agent); LIST_FOREACH(n, &lldp_head, lldp) { if (n->ops && n->ops->timer) n->ops->timer(port, agent); } } if (port->dormantDelay) port->dormantDelay--; port = port->next; }; /* Load new timer */ eloop_register_timeout(1, 0, timer, NULL, NULL); } int start_lldp_agents(void) { eloop_register_timeout(1, 0, timer, NULL, NULL); return 1; } void stop_lldp_agents(void) { eloop_cancel_timeout(timer, NULL, NULL); } void clean_lldp_agents(void) { struct port *port = porthead; struct lldp_agent *agent; while (port != NULL) { if (port_needs_shutdown(port)) { LLDPAD_DBG("Send shutdown frame on port %s\n", port->ifname); LIST_FOREACH(agent, &port->agent_head, entry) { process_tx_shutdown_frame(port, agent); } } else { LLDPAD_DBG("No shutdown frame is sent on port %s\n", port->ifname); } port = port->next; } } lldpad-0.9.46/lldp/agent.h000066400000000000000000000075321215142747300152710ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef AGENT_H #define AGENT_H #include "lldp.h" #include "mibdata.h" #ifndef ETH_ALEN #define ETH_ALEN 6 #endif #ifndef IFNAMSIZ #define IFNAMSIZ 16 /* must match MAX_DEVICE_NAME_LEN */ #endif #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif enum agent_type { NEAREST_BRIDGE = 0, NEAREST_NONTPMR_BRIDGE, NEAREST_CUSTOMER_BRIDGE, AGENT_MAX, }; /* IEEE 802.1AB-2009 - Table 7-1: group MAC addresses used by LLDP */ static const u8 nearest_bridge[ETH_ALEN] = {0x01,0x80,0xc2,0x00,0x00,0x0e}; static const u8 nearest_nontpmr_bridge[ETH_ALEN] = {0x01,0x80,0xc2,0x00,0x00,0x03}; static const u8 nearest_customer_bridge[ETH_ALEN] = {0x01,0x80,0xc2,0x00,0x00,0x00}; struct agenttimers { /* Tx */ u16 state; u16 reinitDelay; u16 msgTxHold; u16 msgTxInterval; u16 msgFastTx; u16 txFastInit; u16 txTTR; u16 txShutdownWhile; u16 txCredit; u16 txMaxCredit; bool txTick; /* Rx */ u16 tooManyNghbrsTimer; u16 rxTTL; u16 lastrxTTL; /* cache last received */ }; struct agenttx { u8 *frameout; u32 sizeout; u8 state; u8 localChange; u16 txTTL; bool txNow; u16 txFast; }; /* per agent statistical counter as in chapter 9.2.6 * of IEEE 802.1AB-2009 */ struct agentstats { /* Tx */ u32 statsFramesOutTotal; /* Rx */ u32 statsAgeoutsTotal; u32 statsFramesDiscardedTotal; u32 statsFramesInErrorsTotal; u32 statsFramesInTotal; u32 statsTLVsDiscardedTotal; u32 statsTLVsUnrecognizedTotal; }; typedef struct rxmanifest{ struct unpacked_tlv *chassis; struct unpacked_tlv *portid; struct unpacked_tlv *ttl; struct unpacked_tlv *portdesc; struct unpacked_tlv *sysname; struct unpacked_tlv *sysdesc; struct unpacked_tlv *syscap; struct unpacked_tlv *mgmtadd; } rxmanifest; struct agentrx { u8 *framein; u16 sizein; u8 state; u8 badFrame; u8 rcvFrame; u8 rxInfoAge; u8 remoteChange; u8 tooManyNghbrs; u8 dupTlvs; u8 dcbx_st; bool newNeighbor; rxmanifest *manifest; }; enum agentAdminStatus { disabled, enabledTxOnly, enabledRxOnly, enabledRxTx, }; /* lldp agent specific structure as in chapter 9.2.5 * of IEEE 802.1AB-2009 */ struct lldp_agent { int adminStatus; int pad; u8 mac_addr[ETH_ALEN]; struct agentrx rx; struct agenttx tx; struct agentstats stats; struct agenttimers timers; u8 rxChanges; u16 lldpdu; struct msap msap; enum agent_type type; LIST_ENTRY(lldp_agent) entry; }; struct lldp_agent *lldp_agent_find_by_type(const char *, enum agent_type); int lldp_add_agent(const char *ifname, enum agent_type); void set_lldp_agent_admin(const char *ifname, int type, int enable); int get_lldp_agent_admin(const char *ifname, int type); int get_lldp_agent_statistics(const char *ifname, struct agentstats *, int); const char *agent_type2section(int agenttype); int start_lldp_agents(void); void stop_lldp_agents(void); void clean_lldp_agents(void); #endif /* AGENT_H */ lldpad-0.9.46/lldp/l2_packet.h000066400000000000000000000114071215142747300160330ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef L2_PACKET_H #define L2_PACKET_H #include #include #include "lldp.h" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define IP2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define IPSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define ETH_P_LLDP 0x88cc #define ETH_P_ECP 0x88b7 /* Draft 0.2 */ #define ETH_P_ECP22 0x8890 /* Ratified standard */ #ifndef ETH_MIN_DATA_LEN #define ETH_MIN_DATA_LEN (ETH_ZLEN - ETH_HLEN) #endif /** * struct l2_packet_data - Internal l2_packet data structure * * This structure is used by the l2_packet implementation to store its private * data. Other files use a pointer to this data when calling the l2_packet * functions, but the contents of this structure should not be used directly * outside l2_packet implementation. */ struct l2_packet_data; struct l2_ethhdr { u8 h_dest[ETH_ALEN]; u8 h_source[ETH_ALEN]; u16 h_proto; } STRUCT_PACKED; /** * l2_packet_init - Initialize l2_packet interface * @ifname: Interface name * @own_addr: Optional own MAC address if available from driver interface or * %NULL if not available * @protocol: Ethernet protocol number in host byte order * @rx_callback: Callback function that will be called for each received packet * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() * @l2_hdr: 1 = include layer 2 header, 0 = do not include header * Returns: Pointer to internal data or %NULL on failure * * rx_callback function will be called with ifindex pointing to the ifindex * of the receive interface. If l2_hdr is set to 0, buf * points to len bytes of the payload after the layer 2 header and similarly, * TX buffers start with payload. This behavior can be changed by setting * l2_hdr=1 to include the layer 2 header in the data buffer. */ struct l2_packet_data *l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, int ifindex, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr); /** * l2_packet_deinit - Deinitialize l2_packet interface * @l2: Pointer to internal l2_packet data from l2_packet_init() */ void l2_packet_deinit(struct l2_packet_data *l2); /** * l2_packet_get_own_src_addr - Get own src layer 2 address * Checks to see if the port is part of a bond and makes and * appropriate selection for the layer 2 src address to use. * @l2: Pointer to internal l2_packet data from l2_packet_init() * @addr: Buffer for the own address (6 bytes) * Returns: 0 on success, -1 on failure */ int l2_packet_get_own_src_addr(struct l2_packet_data *l2, u8 *addr); /** * l2_packet_get_own_addr - Get own layer 2 address * @l2: Pointer to internal l2_packet data from l2_packet_init() * @addr: Buffer for the own address (6 bytes) * Returns: 0 on success, -1 on failure */ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); void get_remote_peer_mac_addr(struct port *port, struct lldp_agent *); void l2_packet_get_remote_addr(struct l2_packet_data *l2, u8 *addr); /** * l2_packet_send - Send a packet * @l2: Pointer to internal l2_packet data from l2_packet_init() * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) * @proto: Protocol/ethertype for the packet in host byte order (only used if * l2_hdr == 0) * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet * is included. * @len: Length of the buffer (including l2 header only if l2_hdr == 1) * Returns: >=0 on success, <0 on failure */ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len); #endif /* L2_PACKET_H */ lldpad-0.9.46/lldp/l2_packet_linux.c000066400000000000000000000163361215142747300172530ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "eloop.h" #include "ports.h" #include "messages.h" #include "l2_packet.h" #include "lldp_util.h" #include "dcb_types.h" #include "lldp/states.h" #include "lldp_dcbx_nl.h" struct l2_packet_data { int fd; char ifname[IFNAMSIZ + 1]; int ifindex; u8 perm_mac_addr[ETH_ALEN]; u8 curr_mac_addr[ETH_ALEN]; u8 san_mac_addr[ETH_ALEN]; u8 remote_mac_addr[ETH_ALEN]; void (*rx_callback)(void *ctx, int ifindex, const u8 *buf, size_t len); void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ }; int l2_packet_get_own_src_addr(struct l2_packet_data *l2, u8 *addr) { if (is_san_mac(l2->san_mac_addr)) memcpy(addr, l2->san_mac_addr, ETH_ALEN); else { /* get an appropriate src MAC to use if the port is * part of a bond. */ struct port *bond_port = porthead; while (bond_port != NULL) { if (bond_port->bond_master && get_src_mac_from_bond(bond_port, l2->ifname, addr)) return 0; bond_port = bond_port->next; } memcpy(addr, l2->curr_mac_addr, ETH_ALEN); } return 0; } /* * Extracts the remote peer's MAC address from the rx frame and * puts it in the l2_packet_data */ void get_remote_peer_mac_addr(struct port *port, struct lldp_agent *agent) { int offset = ETH_ALEN; /* points to remote MAC address in RX frame */ memcpy(port->l2->remote_mac_addr, &agent->rx.framein[offset], ETH_ALEN); } void l2_packet_get_remote_addr(struct l2_packet_data *l2, u8 *addr) { memcpy(addr, l2->remote_mac_addr, ETH_ALEN); } int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { memcpy(addr, l2->perm_mac_addr, ETH_ALEN); return 0; } int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; if (l2 == NULL) return -1; if (l2->l2_hdr) { ret = send(l2->fd, buf, len, 0); if (ret < 0) perror("l2_packet_send - send"); } else { struct sockaddr_ll ll; memset(&ll, 0, sizeof(ll)); ll.sll_family = AF_PACKET; ll.sll_ifindex = l2->ifindex; ll.sll_protocol = htons(proto); ll.sll_halen = ETH_ALEN; memcpy(ll.sll_addr, dst_addr, ETH_ALEN); ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, sizeof(ll)); if (ret < 0) perror("l2_packet_send - sendto"); } return ret; } static void l2_packet_receive(int sock, void *eloop_ctx, UNUSED void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; u8 buf[2300]; int res; struct sockaddr_ll ll; socklen_t fromlen; memset(&ll, 0, sizeof(ll)); fromlen = sizeof(ll); res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen); if (res < 0) { LLDPAD_INFO("receive ERROR = %d\n", res); perror("l2_packet_receive - recvfrom"); return; } l2->rx_callback(l2->rx_callback_ctx, ll.sll_ifindex, buf, res); } struct l2_packet_data * l2_packet_init( const char *ifname, UNUSED const u8 *own_addr, unsigned short protocol, void (*rx_callback)(void *ctx, int ifindex, const u8 *buf, size_t len), void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; struct ifreq ifr; struct sockaddr_ll ll; l2 = malloc(sizeof(struct l2_packet_data)); if (l2 == NULL) return NULL; memset(l2, 0, sizeof(*l2)); strncpy(l2->ifname, ifname, sizeof(l2->ifname)); l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); if (l2->fd < 0) { perror("socket(PF_PACKET)"); free(l2); return NULL; } strncpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl[SIOCGIFINDEX]"); close(l2->fd); free(l2); return NULL; } l2->ifindex = ifr.ifr_ifindex; memset(&ll, 0, sizeof(ll)); ll.sll_family = PF_PACKET; ll.sll_ifindex = ifr.ifr_ifindex; ll.sll_protocol = htons(protocol); if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { perror("bind[PF_PACKET]"); close(l2->fd); free(l2); return NULL; } /* current hw address */ if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { perror("ioctl[SIOCGIFHWADDR]"); close(l2->fd); free(l2); return NULL; } memcpy(l2->curr_mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); if (get_perm_hwaddr(ifname, l2->perm_mac_addr, l2->san_mac_addr) != 0) { memcpy(l2->perm_mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); memset(l2->san_mac_addr, 0xff, ETH_ALEN); } LLDPAD_DBG("%s mac:" MACSTR " perm:" MACSTR " san:" MACSTR "\n", ifname, MAC2STR(l2->curr_mac_addr), MAC2STR(l2->perm_mac_addr), MAC2STR(l2->san_mac_addr)); struct packet_mreq mr; memset(&mr, 0, sizeof(mr)); mr.mr_ifindex = l2->ifindex; mr.mr_alen = ETH_ALEN; memcpy(mr.mr_address, &nearest_bridge, ETH_ALEN); mr.mr_type = PACKET_MR_MULTICAST; if (setsockopt(l2->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) { perror("setsockopt nearest_bridge"); close(l2->fd); free(l2); return NULL; } memcpy(mr.mr_address, &nearest_customer_bridge, ETH_ALEN); if (setsockopt(l2->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) perror("setsockopt nearest_customer_bridge"); memcpy(mr.mr_address, &nearest_nontpmr_bridge, ETH_ALEN); if (setsockopt(l2->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) perror("setsockopt nearest_nontpmr_bridge"); int option = 1; int option_size = sizeof(option); if (setsockopt(l2->fd, SOL_PACKET, PACKET_ORIGDEV, &option, option_size) < 0) { perror("setsockopt SOL_PACKET"); close(l2->fd); free(l2); return NULL; } option = TC_PRIO_CONTROL; if ( setsockopt(l2->fd, SOL_SOCKET, SO_PRIORITY, &option, sizeof(option_size)) < 0) { perror("setsockopt SOL_PRIORITY"); close(l2->fd); free(l2); return NULL; } LLDPAD_INFO("%s MAC address is " MACSTR "\n", ifname, MAC2STR(l2->perm_mac_addr)); eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); return l2; } void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) return; if (l2->fd >= 0) { eloop_unregister_read_sock(l2->fd); close(l2->fd); } free(l2); } lldpad-0.9.46/lldp/mibdata.h000066400000000000000000000023131215142747300155640ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef MIBDATA_H #define MIBDATA_H #include "lldp.h" struct msap { u16 length1; u8 *msap1; u16 length2; u8 *msap2; }; #endif /* MIBDATA_H */ lldpad-0.9.46/lldp/ports.c000066400000000000000000000216331215142747300153330ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include "states.h" #include "lldp_tlv.h" #include "lldp_rtnl.h" #include "ports.h" #include "l2_packet.h" #include "libconfig.h" #include "lldp_mand_clif.h" #include "lldp.h" #include "config.h" #include "messages.h" #include "lldpad_status.h" #include "lldp_rtnl.h" #include "lldp_dcbx_nl.h" #include "agent.h" #include "lldp_dcbx_nl.h" #include "lldp_util.h" struct port *porthead = NULL; /* port Head pointer */ void agent_receive(void *, const u8 *, const u8 *, size_t); /* Port routines used for command processing -- return cmd_xxx codes */ int get_lldp_agent_statistics(const char *ifname, struct agentstats *stats, int type) { struct lldp_agent *agent; agent = lldp_agent_find_by_type(ifname, type); if (!agent) return cmd_agent_not_found; memcpy((void *)stats, (void *)&agent->stats, sizeof(struct agentstats)); return 0; } int get_local_tlvs(char *ifname, int type, unsigned char *tlvs, int *size) { struct lldp_agent *agent; agent = lldp_agent_find_by_type(ifname, type); if (!agent) return cmd_agent_not_found; if (agent->tx.frameout == NULL) { *size = 0; return cmd_success; } *size = agent->tx.sizeout - sizeof(struct l2_ethhdr); if (*size < 0) return cmd_invalid; memcpy((void *)tlvs, (void *)agent->tx.frameout + sizeof(struct l2_ethhdr), *size); return cmd_success; } int get_neighbor_tlvs(char *ifname, int type, unsigned char *tlvs, int *size) { struct lldp_agent *agent; agent = lldp_agent_find_by_type(ifname, type); if (!agent) return cmd_agent_not_found; if (agent->rx.framein == NULL) { *size = 0; return cmd_success; } *size = agent->rx.sizein - sizeof(struct l2_ethhdr); if (*size < 0) return cmd_invalid; memcpy((void *)tlvs, (void *)agent->rx.framein + sizeof(struct l2_ethhdr), *size); return cmd_success; } /* Routines used for managing interfaces -- return std C return values */ int get_lldp_agent_admin(const char *ifname, int type) { struct lldp_agent *agent = NULL; agent = lldp_agent_find_by_type(ifname, type); if (agent == NULL) return disabled; return agent->adminStatus; } void set_lldp_agent_admin(const char *ifname, int type, int admin) { struct port *port = NULL; struct lldp_agent *agent; int all = 0; int tmp; all = !strlen(ifname); port = porthead; while (port != NULL) { if (all || !strncmp(ifname, port->ifname, IFNAMSIZ)) { /* don't change a port which has an explicit setting * on a global setting change */ if (all && (!get_config_setting(port->ifname, type, ARG_ADMINSTATUS, (void *)&tmp, CONFIG_TYPE_INT))) { port = port->next; continue; } agent = lldp_agent_find_by_type(port->ifname, type); if (!agent) { port = port->next; continue; } if (agent->adminStatus != admin) { agent->adminStatus = admin; somethingChangedLocal(ifname, type); run_tx_sm(port, agent); run_rx_sm(port, agent); } if (!all) break; } port = port->next; } } void set_lldp_port_enable(const char *ifname, int enable) { struct port *port = NULL; struct lldp_agent *agent = NULL; port = port_find_by_name(ifname); if (port == NULL) return; port->portEnabled = (u8)enable; if (!enable) { /* port->adminStatus = disabled; */ LIST_FOREACH(agent, &port->agent_head, entry) { agent->rx.rxInfoAge = false; } } LIST_FOREACH(agent, &port->agent_head, entry) { run_tx_sm(port, agent); run_rx_sm(port, agent); } } void set_port_oper_delay(const char *ifname) { struct port *port = port_find_by_name(ifname); if (port == NULL) return; port->dormantDelay = DORMANT_DELAY; return; } int set_port_hw_resetting(const char *ifname, int resetting) { struct port *port = NULL; port = port_find_by_name(ifname); if (port == NULL) return -1; port->hw_resetting = (u8)resetting; return port->hw_resetting; } int get_port_hw_resetting(const char *ifname) { struct port *port = NULL; port = port_find_by_name(ifname); if (port) return port->hw_resetting; else return 0; } int reinit_port(const char *ifname) { struct lldp_agent *agent; struct port *port; port = port_find_by_name(ifname); if (!port) return -1; /* Reset relevant port variables */ port->hw_resetting = false; port->portEnabled = false; port->dormantDelay = DORMANT_DELAY; LIST_FOREACH(agent, &port->agent_head, entry) { /* init TX path */ /* Reset relevant state variables */ agent->tx.state = TX_LLDP_INITIALIZE; agent->rx.state = LLDP_WAIT_PORT_OPERATIONAL; agent->tx.txTTL = 0; agent->msap.length1 = 0; agent->msap.msap1 = NULL; agent->msap.length2 = 0; agent->msap.msap2 = NULL; agent->lldpdu = false; agent->timers.state = TX_TIMER_BEGIN; /* init & enable RX path */ rxInitializeLLDP(port, agent); /* init TX path */ txInitializeTimers(agent); txInitializeLLDP(agent); } return 0; } struct port *add_port(const char *ifname) { struct port *newport; newport = porthead; while (newport != NULL) { if (!strncmp(ifname, newport->ifname, IFNAMSIZ)) return 0; newport = newport->next; } newport = (struct port *)malloc(sizeof(struct port)); if (newport == NULL) { LLDPAD_DBG("new port malloc failed\n"); goto fail; } memset(newport,0,sizeof(struct port)); newport->next = NULL; newport->ifname = strdup(ifname); if (newport->ifname == NULL) { LLDPAD_DBG("new port name malloc failed\n"); goto fail; } newport->bond_master = is_bond(ifname); /* Initialize relevant port variables */ newport->hw_resetting = false; newport->portEnabled = false; LIST_INIT(&newport->agent_head); newport->l2 = l2_packet_init(newport->ifname, NULL, ETH_P_LLDP, rxReceiveFrame, newport, 1); if (newport->l2 == NULL) { LLDPAD_DBG("Failed to open register layer 2 access to " "ETH_P_LLDP\n"); goto fail; } /* init TX path */ newport->dormantDelay = DORMANT_DELAY; /* enable TX path */ if (porthead) newport->next = porthead; porthead = newport; return newport; fail: if(newport) { if(newport->ifname) free(newport->ifname); free(newport); } return NULL; } int remove_port(char *ifname) { struct port *port; /* Pointer to port to remove */ struct port *parent = NULL; /* Pointer to previous on port stack */ struct lldp_agent *agent; for (port = porthead; port; port = port->next) { if (!strncmp(ifname, port->ifname, IFNAMSIZ)) { LLDPAD_DBG("In %s: Found port %s\n", __func__, port->ifname); break; } parent = port; } if (!port) { LLDPAD_DBG("%s: port not present\n", __func__); return -1; } LLDPAD_DBG("In %s: Found port %s\n", __func__, port->ifname); /* Set linkmode to off */ set_linkmode(ifname, 0); /* Close down the socket */ l2_packet_deinit(port->l2); port->portEnabled = false; while (!LIST_EMPTY(&port->agent_head)) { agent = LIST_FIRST(&port->agent_head); /* Re-initialize relevant port variables */ agent->tx.state = TX_LLDP_INITIALIZE; agent->rx.state = LLDP_WAIT_PORT_OPERATIONAL; agent->timers.state = TX_TIMER_BEGIN; agent->adminStatus = disabled; agent->tx.txTTL = 0; /* Remove the tlvs */ if (agent->msap.msap1) { free(agent->msap.msap1); agent->msap.msap1 = NULL; } if (agent->msap.msap2) { free(agent->msap.msap2); agent->msap.msap2 = NULL; } if (agent->rx.framein) free(agent->rx.framein); if (agent->tx.frameout) free(agent->tx.frameout); LIST_REMOVE(agent, entry); free(agent); } /* Take this port out of the chain */ if (parent == NULL) porthead = port->next; else if (parent->next == port) /* Sanity check */ parent->next = port->next; else return -1; if (port->ifname) free(port->ifname); free(port); return 0; } /* * port_needs_shutdown - check if we need send LLDP shutdown frame on this port * @port: the port struct * * Return 1 for yes and 0 for no. * * No shutdown frame for port that has dcb enabled */ int port_needs_shutdown(struct port *port) { return !check_port_dcb_mode(port->ifname); } lldpad-0.9.46/lldp/ports.h000066400000000000000000000062471215142747300153440ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef PORTS_H #define PORTS_H #include #include #include "lldp.h" #include "agent.h" #ifndef ETH_ALEN #define ETH_ALEN 6 #endif #ifndef IFNAMSIZ #define IFNAMSIZ 16 /* must match MAX_DEVICE_NAME_LEN */ #endif #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif #ifndef BIT #define BIT(x) (1 << (x)) #endif #define MAX_INTERFACES 16 #define DEFAULT_TX_HOLD 4 #define DEFAULT_TX_INTERVAL 30 #define FAST_TX_INTERVAL 1 #define TX_FAST_INIT 4 #define DEFAULT_TX_DELAY 1 #define FASTSTART_TX_DELAY 1 #define REINIT_DELAY 2 #define TX_CREDIT_MAX 5 #define DORMANT_DELAY 15 struct porttimers { u16 dormantDelay; }; struct eth_hdr { char dst[6]; char src[6]; u16 ethertype; }; enum portEnableStatus { no = 0, yes, }; /* lldp port specific structure */ struct port { char *ifname; u8 hw_resetting; u8 portEnabled; u8 prevPortEnabled; u8 bond_master; /* True if a bond master */ struct porttimers *timers; u16 dormantDelay; LIST_HEAD(agent_head, lldp_agent) agent_head; struct l2_packet_data *l2; struct port *next; }; extern struct port *porthead; #ifdef __cplusplus extern "C" { #endif struct port *add_port(const char *); int remove_port(char *); #ifdef __cplusplus } #endif int set_port_hw_resetting(const char *ifname, int resetting); int get_port_hw_resetting(const char *ifname); void set_lldp_port_enable(const char *ifname, int enable); int get_local_tlvs(char *ifname, int type, unsigned char *tlvs, int *size); int get_neighbor_tlvs(char *ifname, int type, unsigned char *tlvs, int *size); int port_needs_shutdown(struct port *port); void set_port_operstate(const char *ifname, int operstate); int get_port_operstate(const char *ifname); void set_port_oper_delay(const char *ifname); int reinit_port(const char *ifname); void set_agent_oper_delay(const char *ifname, int type); static inline struct port *port_find_by_name(const char *ifname) { struct port *port = porthead; while (port) { if (!strncmp(ifname, port->ifname, IFNAMSIZ)) return port; port = port->next; } return NULL; } #endif /* PORTS_H */ lldpad-0.9.46/lldp/rx.c000066400000000000000000000414101215142747300146100ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include "ports.h" #include "l2_packet.h" #include "states.h" #include "mibdata.h" #include "messages.h" #include "lldp.h" #include "lldpad.h" #include "lldp_mod.h" #include "clif_msgs.h" #include "lldp_mand.h" #include "lldp_tlv.h" #include "agent.h" void rxInitializeLLDP(struct port *port, struct lldp_agent *agent) { agent->rx.rcvFrame = false; agent->rx.badFrame = false; agent->rx.tooManyNghbrs = false; agent->rx.rxInfoAge = false; if (agent->rx.framein) { free(agent->rx.framein); agent->rx.framein = NULL; } agent->rx.sizein = 0; mibDeleteObjects(port, agent); return; } void rxReceiveFrame(void *ctx, UNUSED int ifindex, const u8 *buf, size_t len) { struct port * port; struct lldp_agent *agent; u8 frame_error = 0; struct l2_ethhdr *hdr; struct l2_ethhdr example_hdr,*ex; /* Drop and ignore zero length frames */ if (!len) return; port = (struct port *)ctx; /* walk through the list of agents for this interface and see if we * can find a matching agent */ LIST_FOREACH(agent, &port->agent_head, entry) { if (agent->rx.framein && agent->rx.sizein == len && (memcmp(buf, agent->rx.framein, len) == 0)) { agent->timers.rxTTL = agent->timers.lastrxTTL; agent->stats.statsFramesInTotal++; return; } ex = &example_hdr; memcpy(ex->h_dest, agent->mac_addr, ETH_ALEN); ex->h_proto = htons(ETH_P_LLDP); hdr = (struct l2_ethhdr *)buf; if (hdr->h_proto != example_hdr.h_proto) { LLDPAD_INFO("ERROR Ethertype not LLDP ethertype but ethertype " "'%x' in incoming frame.\n", htons(hdr->h_proto)); frame_error++; return; } if ((!memcmp(hdr->h_dest,ex->h_dest, ETH_ALEN))) break; } if (agent == NULL) return; if (agent->adminStatus == disabled || agent->adminStatus == enabledTxOnly) return; if (agent->rx.framein) free(agent->rx.framein); agent->rx.sizein = (u16)len; agent->rx.framein = (u8 *)malloc(len); if (agent->rx.framein == NULL) { LLDPAD_DBG("ERROR - could not allocate memory for rx'ed frame\n"); return; } memcpy(agent->rx.framein, buf, len); if (!frame_error) { agent->stats.statsFramesInTotal++; agent->rx.rcvFrame = 1; } run_rx_sm(port, agent); } void rxProcessFrame(struct port *port, struct lldp_agent *agent) { u16 tlv_cnt = 0; u8 tlv_type = 0; u16 tlv_length = 0; u16 tlv_offset = 0; u16 *tlv_head_ptr = NULL; u8 frame_error = 0; bool msap_compare_1 = false; bool msap_compare_2 = false; bool good_neighbor = false; bool tlv_stored = false; int err; struct lldp_module *np; assert(agent->rx.framein && agent->rx.sizein); agent->lldpdu = 0; agent->rx.dupTlvs = 0; agent->rx.dcbx_st = 0; agent->rx.manifest = (rxmanifest *)malloc(sizeof(rxmanifest)); if (agent->rx.manifest == NULL) { LLDPAD_DBG("ERROR - could not allocate memory for receive " "manifest\n"); return; } memset(agent->rx.manifest, 0, sizeof(rxmanifest)); get_remote_peer_mac_addr(port, agent); tlv_offset = sizeof(struct l2_ethhdr); /* Points to 1st TLV */ do { tlv_cnt++; if (tlv_offset > agent->rx.sizein) { LLDPAD_INFO("ERROR: Frame overrun!\n"); frame_error++; goto out; } tlv_head_ptr = (u16 *)&agent->rx.framein[tlv_offset]; tlv_length = htons(*tlv_head_ptr) & 0x01FF; tlv_type = (u8)(htons(*tlv_head_ptr) >> 9); if (tlv_cnt <= 3) { if (tlv_cnt != tlv_type) { LLDPAD_INFO("ERROR:TLV missing or TLVs out " "of order!\n"); frame_error++; goto out; } } if (tlv_cnt > 3) { if ((tlv_type == 1) || (tlv_type == 2) || (tlv_type == 3)) { LLDPAD_INFO("ERROR: Extra Type 1 Type2, or " "Type 3 TLV!\n"); frame_error++; goto out; } } if ((tlv_type == TIME_TO_LIVE_TLV) && (tlv_length != 2)) { LLDPAD_INFO("ERROR:TTL TLV validation error! \n"); frame_error++; goto out; } u16 tmp_offset = tlv_offset + tlv_length; if (tmp_offset > agent->rx.sizein) { LLDPAD_INFO("ERROR: Frame overflow error: offset=%d, " "rx.size=%d \n", tmp_offset, agent->rx.sizein); frame_error++; goto out; } u8 *info = (u8 *)&agent->rx.framein[tlv_offset + sizeof(*tlv_head_ptr)]; struct unpacked_tlv *tlv = create_tlv(); if (!tlv) { LLDPAD_DBG("ERROR: Failed to malloc space for " "incoming TLV. \n"); goto out; } if ((tlv_length == 0) && (tlv->type != TYPE_0)) { LLDPAD_INFO("ERROR: tlv_length == 0\n"); free_unpkd_tlv(tlv); goto out; } tlv->type = tlv_type; tlv->length = tlv_length; tlv->info = (u8 *)malloc(tlv_length); if (tlv->info) { memset(tlv->info,0, tlv_length); memcpy(tlv->info, info, tlv_length); } else { LLDPAD_DBG("ERROR: Failed to malloc space for incoming " "TLV info \n"); free_unpkd_tlv(tlv); goto out; } /* Validate the TLV */ tlv_offset += sizeof(*tlv_head_ptr) + tlv_length; /* Get MSAP info */ if (tlv->type == TYPE_1) { /* chassis ID */ if (agent->lldpdu & RCVD_LLDP_TLV_TYPE1) { LLDPAD_INFO("Received multiple Chassis ID" "TLVs in this LLDPDU\n"); frame_error++; free_unpkd_tlv(tlv); goto out; } else { agent->lldpdu |= RCVD_LLDP_TLV_TYPE1; agent->rx.manifest->chassis = tlv; tlv_stored = true; } if (agent->msap.msap1 == NULL) { agent->msap.length1 = tlv->length; agent->msap.msap1 = (u8 *)malloc(tlv->length); if (!(agent->msap.msap1)) { LLDPAD_DBG("ERROR: Failed to malloc " "space for msap1\n"); free_unpkd_tlv(tlv); goto out; } memcpy(agent->msap.msap1, tlv->info, tlv->length); } else { if (tlv->length == agent->msap.length1) { if ((memcmp(tlv->info,agent->msap.msap1, tlv->length) == 0)) msap_compare_1 = true; } } } if (tlv->type == TYPE_2) { /* port ID */ if (agent->lldpdu & RCVD_LLDP_TLV_TYPE2) { LLDPAD_INFO("Received multiple Port ID " "TLVs in this LLDPDU\n"); frame_error++; free_unpkd_tlv(tlv); goto out; } else { agent->lldpdu |= RCVD_LLDP_TLV_TYPE2; agent->rx.manifest->portid = tlv; tlv_stored = true; } if (agent->msap.msap2 == NULL) { agent->msap.length2 = tlv->length; agent->msap.msap2 = (u8 *)malloc(tlv->length); if (!(agent->msap.msap2)) { LLDPAD_DBG("ERROR: Failed to malloc " "space for msap2\n"); free_unpkd_tlv(tlv); goto out; } memcpy(agent->msap.msap2, tlv->info, tlv->length); agent->rx.newNeighbor = true; } else { if (tlv->length == agent->msap.length2) { if ((memcmp(tlv->info,agent->msap.msap2, tlv->length) == 0)) msap_compare_2 = true; } if ((msap_compare_1 == true) && (msap_compare_2 == true)) { msap_compare_1 = false; msap_compare_2 = false; good_neighbor = true; } else { /* New neighbor */ agent->rx.tooManyNghbrs = true; agent->rx.newNeighbor = true; LLDPAD_INFO("** TOO_MANY_NGHBRS\n"); } } } if (tlv->type == TYPE_3) { /* time to live */ if (agent->lldpdu & RCVD_LLDP_TLV_TYPE3) { LLDPAD_INFO("Received multiple TTL TLVs in this" " LLDPDU\n"); frame_error++; free_unpkd_tlv(tlv); goto out; } else { agent->lldpdu |= RCVD_LLDP_TLV_TYPE3; agent->rx.manifest->ttl = tlv; tlv_stored = true; } if ((agent->rx.tooManyNghbrs == true) && (good_neighbor == false)) { LLDPAD_INFO("** set tooManyNghbrsTimer\n"); agent->timers.tooManyNghbrsTimer = max(ntohs(*(u16 *)tlv->info), agent->timers.tooManyNghbrsTimer); msap_compare_1 = false; msap_compare_2 = false; } else { agent->timers.rxTTL = ntohs(*(u16 *)tlv->info); agent->timers.lastrxTTL = agent->timers.rxTTL; good_neighbor = false; } } if (tlv->type == TYPE_4) { /* port description */ agent->lldpdu |= RCVD_LLDP_TLV_TYPE4; agent->rx.manifest->portdesc = tlv; tlv_stored = true; } if (tlv->type == TYPE_5) { /* system name */ agent->lldpdu |= RCVD_LLDP_TLV_TYPE5; agent->rx.manifest->sysname = tlv; tlv_stored = true; } if (tlv->type == TYPE_6) { /* system description */ agent->lldpdu |= RCVD_LLDP_TLV_TYPE6; agent->rx.manifest->sysdesc = tlv; tlv_stored = true; } if (tlv->type == TYPE_7) { /* system capabilities */ agent->lldpdu |= RCVD_LLDP_TLV_TYPE7; agent->rx.manifest->syscap = tlv; tlv_stored = true; } if (tlv->type == TYPE_8) { /* mgmt address */ agent->lldpdu |= RCVD_LLDP_TLV_TYPE8; agent->rx.manifest->mgmtadd = tlv; tlv_stored = true; } /* rx per lldp module */ LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops || !np->ops->lldp_mod_rchange) continue; err = np->ops->lldp_mod_rchange(port, agent, tlv); if (!err) tlv_stored = true; else if (err == TLV_ERR) { frame_error++; free_unpkd_tlv(tlv); goto out; } } if (!tlv_stored) { LLDPAD_INFO("%s: allocated TLV %u was not stored! %p\n", __func__, tlv->type, tlv); tlv = free_unpkd_tlv(tlv); agent->stats.statsTLVsUnrecognizedTotal++; } tlv = NULL; tlv_stored = false; } while(tlv_type != 0); out: if (frame_error) { /* discard the frame because of errors. */ agent->stats.statsFramesDiscardedTotal++; agent->stats.statsFramesInErrorsTotal++; agent->rx.badFrame = true; } agent->lldpdu = 0; clear_manifest(agent); return; } u8 mibDeleteObjects(struct port *port, struct lldp_agent *agent) { struct lldp_module *np; LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops || !np->ops->lldp_mod_mibdelete) continue; np->ops->lldp_mod_mibdelete(port, agent); } /* Clear history */ agent->msap.length1 = 0; if (agent->msap.msap1) { free(agent->msap.msap1); agent->msap.msap1 = NULL; } agent->msap.length2 = 0; if (agent->msap.msap2) { free(agent->msap.msap2); agent->msap.msap2 = NULL; } return 0; } void run_rx_sm(struct port *port, struct lldp_agent *agent) { set_rx_state(port, agent); do { switch(agent->rx.state) { case LLDP_WAIT_PORT_OPERATIONAL: break; case DELETE_AGED_INFO: process_delete_aged_info(port, agent); break; case RX_LLDP_INITIALIZE: process_rx_lldp_initialize(port, agent); break; case RX_WAIT_FOR_FRAME: process_wait_for_frame(agent); break; case RX_FRAME: process_rx_frame(port, agent); break; case DELETE_INFO: process_delete_info(port, agent); break; case UPDATE_INFO: process_update_info(agent); break; default: LLDPAD_DBG("ERROR: The RX State Machine is broken!\n"); } } while (set_rx_state(port, agent) == true); } bool set_rx_state(struct port *port, struct lldp_agent *agent) { if ((agent->rx.rxInfoAge == false) && (port->portEnabled == false)) { rx_change_state(agent, LLDP_WAIT_PORT_OPERATIONAL); } switch(agent->rx.state) { case LLDP_WAIT_PORT_OPERATIONAL: if (agent->rx.rxInfoAge == true) { rx_change_state(agent, DELETE_AGED_INFO); return true; } else if (port->portEnabled == true) { rx_change_state(agent, RX_LLDP_INITIALIZE); return true; } return false; case DELETE_AGED_INFO: rx_change_state(agent, LLDP_WAIT_PORT_OPERATIONAL); return true; case RX_LLDP_INITIALIZE: if ((agent->adminStatus == enabledRxTx) || (agent->adminStatus == enabledRxOnly)) { rx_change_state(agent, RX_WAIT_FOR_FRAME); return true; } return false; case RX_WAIT_FOR_FRAME: if ((agent->adminStatus == disabled) || (agent->adminStatus == enabledTxOnly)) { rx_change_state(agent, RX_LLDP_INITIALIZE); return true; } if (agent->rx.rxInfoAge == true) { rx_change_state(agent, DELETE_INFO); return true; } else if (agent->rx.rcvFrame == true) { rx_change_state(agent, RX_FRAME); return true; } return false; case DELETE_INFO: rx_change_state(agent, RX_WAIT_FOR_FRAME); return true; case RX_FRAME: if (agent->timers.rxTTL == 0) { rx_change_state(agent, DELETE_INFO); return true; } else if ((agent->timers.rxTTL != 0) && (agent->rxChanges == true)) { rx_change_state(agent, UPDATE_INFO); return true; } rx_change_state(agent, RX_WAIT_FOR_FRAME); return true; case UPDATE_INFO: rx_change_state(agent, RX_WAIT_FOR_FRAME); return true; default: LLDPAD_DBG("ERROR: The RX State Machine is broken!\n"); return false; } } void process_delete_aged_info(struct port *port, struct lldp_agent *agent) { mibDeleteObjects(port, agent); agent->rx.rxInfoAge = false; agent->rx.remoteChange = true; return; } void process_rx_lldp_initialize(struct port *port, struct lldp_agent *agent) { rxInitializeLLDP(port, agent); agent->rx.rcvFrame = false; return; } void process_wait_for_frame(struct lldp_agent *agent) { agent->rx.badFrame = false; agent->rx.rxInfoAge = false; return; } void process_rx_frame(struct port *port, struct lldp_agent *agent) { agent->rx.remoteChange = false; agent->rxChanges = false; agent->rx.rcvFrame = false; rxProcessFrame(port, agent); return; } void process_delete_info(struct port *port, struct lldp_agent *agent) { mibDeleteObjects(port, agent); if (agent->rx.framein) { free(agent->rx.framein); agent->rx.framein = NULL; } agent->rx.sizein = 0; agent->rx.remoteChange = true; return; } void process_update_info(struct lldp_agent *agent) { agent->rx.remoteChange = true; return; } void update_rx_timers(struct lldp_agent *agent) { if (agent->timers.rxTTL) { agent->timers.rxTTL--; if (agent->timers.rxTTL == 0) { agent->rx.rxInfoAge = true; if (agent->timers.tooManyNghbrsTimer != 0) { LLDPAD_DBG("** clear tooManyNghbrsTimer\n"); agent->timers.tooManyNghbrsTimer = 0; agent->rx.tooManyNghbrs = false; } } } if (agent->timers.tooManyNghbrsTimer) { agent->timers.tooManyNghbrsTimer--; if (agent->timers.tooManyNghbrsTimer == 0) { LLDPAD_DBG("** tooManyNghbrsTimer timeout\n"); agent->rx.tooManyNghbrs = false; } } } void rx_change_state(struct lldp_agent *agent, u8 newstate) { switch(newstate) { case LLDP_WAIT_PORT_OPERATIONAL: break; case RX_LLDP_INITIALIZE: assert((agent->rx.state == LLDP_WAIT_PORT_OPERATIONAL) || (agent->rx.state == RX_WAIT_FOR_FRAME)); break; case DELETE_AGED_INFO: assert(agent->rx.state == LLDP_WAIT_PORT_OPERATIONAL); break; case RX_WAIT_FOR_FRAME: if (!(agent->rx.state == RX_LLDP_INITIALIZE || agent->rx.state == DELETE_INFO || agent->rx.state == UPDATE_INFO || agent->rx.state == RX_FRAME)) { assert(agent->rx.state != RX_LLDP_INITIALIZE); assert(agent->rx.state != DELETE_INFO); assert(agent->rx.state != UPDATE_INFO); assert(agent->rx.state != RX_FRAME); } break; case RX_FRAME: assert(agent->rx.state == RX_WAIT_FOR_FRAME); break; case DELETE_INFO: if (!(agent->rx.state == RX_WAIT_FOR_FRAME || agent->rx.state == RX_FRAME)) { assert(agent->rx.state == RX_WAIT_FOR_FRAME); assert(agent->rx.state == RX_FRAME); } break; case UPDATE_INFO: assert(agent->rx.state == RX_FRAME); break; default: LLDPAD_DBG("ERROR: The RX State Machine is broken!\n"); } agent->rx.state = newstate; } void clear_manifest(struct lldp_agent *agent) { if (agent->rx.manifest->mgmtadd) agent->rx.manifest->mgmtadd = free_unpkd_tlv(agent->rx.manifest->mgmtadd); if (agent->rx.manifest->syscap) agent->rx.manifest->syscap = free_unpkd_tlv(agent->rx.manifest->syscap); if (agent->rx.manifest->sysdesc) agent->rx.manifest->sysdesc = free_unpkd_tlv(agent->rx.manifest->sysdesc); if (agent->rx.manifest->sysname) agent->rx.manifest->sysname = free_unpkd_tlv(agent->rx.manifest->sysname); if (agent->rx.manifest->portdesc) agent->rx.manifest->portdesc = free_unpkd_tlv(agent->rx.manifest->portdesc); if (agent->rx.manifest->ttl) agent->rx.manifest->ttl = free_unpkd_tlv(agent->rx.manifest->ttl); if (agent->rx.manifest->portid) agent->rx.manifest->portid = free_unpkd_tlv(agent->rx.manifest->portid); if (agent->rx.manifest->chassis) agent->rx.manifest->chassis = free_unpkd_tlv(agent->rx.manifest->chassis); free(agent->rx.manifest); agent->rx.manifest = NULL; } lldpad-0.9.46/lldp/states.h000066400000000000000000000145361215142747300155000ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef STATES_H #define STATES_H #include "ports.h" /* Tx Timer States */ enum { TX_TIMER_BEGIN, TX_TIMER_INITIALIZE, TX_TIMER_IDLE, TX_TIMER_EXPIRES, TX_TICK, SIGNAL_TX, TX_FAST_START }; /* Tx States */ enum { TX_LLDP_INITIALIZE, TX_IDLE, TX_SHUTDOWN_FRAME, TX_INFO_FRAME }; /** * The txInitializeLLDP () procedure initializes the LLDP transmit module as * defined in 10.1.1. */ void txInitializeLLDP(struct lldp_agent *agent); /** * The mibConstrInfoLLDPDU () procedure constructs an information LLDPDU as * defined in 10.2.1.1 according to the LLDPDU and associated basic TLV * formats as specified in 9.2 and 9.4 plus any optional * Organizationally Specific TLVs as specified in 9.6 and their associated * individual organizationally defined formats (as, for example, in Annex F * and Annex G). * * NOTE Because selection of which specific TLVs to include in an LLDPDU is a * LLDP MIB management function, the transmit state machine does not include a * separate procedure for this purpose (see 10.2.1.1). */ bool mibConstrInfoLLDPDU(struct port *, struct lldp_agent *); /** * The mibConstrShutdownLLDPDU () procedure constructs a shutdown LLDPDU as * defined in 10.2.1.2 and according to the LLDPDU and the associated TLV * formats specified in 9.2 and 9.5. */ bool mibConstrShutdownLLDPDU(struct port *, struct lldp_agent *); /** * The txFrame () procedure prepends the source and destinations addresses * and the LLDP Ethertype to each LLDPDU as defined in 10.2.2 before it is * sent to the MAC for transmission. */ u8 txFrame(struct port *port, struct lldp_agent *); void run_tx_sm(struct port *, struct lldp_agent *); void process_tx_initialize_sm(struct port *); void process_tx_idle(struct lldp_agent *); void process_tx_shutdown_frame(struct port *, struct lldp_agent *); void process_tx_info_frame(struct port *, struct lldp_agent *); void update_tx_timers(struct lldp_agent *); void run_tx_timers_sm(struct port *, struct lldp_agent *); bool set_tx_state(struct port *, struct lldp_agent *); void txInitializeTimers(struct lldp_agent *); void tx_change_state(struct port *, struct lldp_agent *, u8 ); /******************************************************************************/ /* Rx States */ enum { LLDP_WAIT_PORT_OPERATIONAL = 4, DELETE_AGED_INFO, RX_LLDP_INITIALIZE, RX_WAIT_FOR_FRAME, RX_FRAME, DELETE_INFO, UPDATE_INFO }; /** * The rxInitializeLLDP () procedure initializes the LLDP receive module * as defined in 10.1.2. */ void rxInitializeLLDP(struct port *port, struct lldp_agent *); /** * The rxProcessFrame () procedure: * a) Strips the protocol identification fields from the received frame * and validates the TLVs contained in * the LLDPDU as defined in 10.3.1 and 10.3.2. * b) Determines whether or not a MIB update may be required as defined in * 10.3.3. * 1) If an update is required and sufficient space is available to * store the LLDPDU information in the LLDP remote systems MIB, the * control variable rxChanges is set to TRUE. * 2) If an update is not required, the control variable rxChanges is * set to FALSE. * c) If sufficient space is not available, determines whether to discard * the incoming LLDPDU from a new neighbor or to delete information from * an existing neighbor that is already in the LLDP remote systems MIB, as * defined in 10.3.4. The tooManyNghbrsTimer and the tooManyNghbrs * flag variable are both set during this process. * NOTES * 1: The variable badFrame is set to FALSE in the receive state machine state * RX_FRAME before entering the procedure rxProcessFrame(). It is set to * TRUE by rxProcessFrame() if the LLDPDU fails validation (see 10.3.2). * 2: The flag variable tooManyNghbrs is automatically reset when the * tooManyNghbrsTimer expires. */ void rxReceiveFrame(void *, int ifindex, const u8 *, size_t); void rxProcessFrame(struct port *, struct lldp_agent *); /** * The mibDeleteObjects () procedure deletes all information in the LLDP * remote systems MIB associated with the MSAP identifier if an LLDPDU is * received with an rxTTL value of zero (see 10.3.2) or the timing counter * rxInfoTTL expires. (see 10.3.6). */ u8 mibDeleteObjects(struct port *port, struct lldp_agent *); /** * The mibUpdateObjects () procedure updates the MIB objects corresponding to * the TLVs contained in the received LLDPDU for the LLDP remote system * indicated by the LLDP remote systems update process defined in 10.3.5. * NOTE To avoid a race condition, the flag variable remoteChange * is not set to TRUE until after the information in the LLDP remote systems * MIB has been updated. */ void mibUpdateObjects(struct port *, struct lldp_agent *); void run_rx_sm(struct port *, struct lldp_agent *); bool set_rx_state(struct port *, struct lldp_agent *); void rx_change_state(struct lldp_agent *, u8 ); void process_delete_aged_info(struct port *, struct lldp_agent *); void process_rx_lldp_initialize(struct port *, struct lldp_agent *); void process_wait_for_frame(struct lldp_agent *); void process_rx_frame(struct port *, struct lldp_agent *); void process_delete_info(struct port *, struct lldp_agent *); void process_update_info(struct lldp_agent *); void update_rx_timers(struct lldp_agent *); void clear_manifest(struct lldp_agent *); #endif /* STATES_H */ lldpad-0.9.46/lldp/tx.c000066400000000000000000000301641215142747300146160ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include "ports.h" #include "l2_packet.h" #include "states.h" #include "messages.h" #include "lldpad.h" #include "lldp_tlv.h" #include "lldp_mod.h" #include "lldp_mand.h" bool mibConstrInfoLLDPDU(struct port *port, struct lldp_agent *agent) { struct l2_ethhdr eth; u8 own_addr[ETH_ALEN]; u32 fb_offset = 0; u32 datasize = 0; struct packed_tlv *ptlv = NULL; struct lldp_module *np; char macstring[30]; if (agent->tx.frameout) { free(agent->tx.frameout); agent->tx.frameout = NULL; } mac2str(agent->mac_addr, macstring, 30); LLDPAD_DBG("%s: port %s mac %s type %i.\n", __func__, port->ifname, macstring, agent->type); memcpy(eth.h_dest, agent->mac_addr, ETH_ALEN); l2_packet_get_own_src_addr(port->l2,(u8 *)&own_addr); memcpy(eth.h_source, &own_addr, ETH_ALEN); eth.h_proto = htons(ETH_P_LLDP); agent->tx.frameout = (u8 *)malloc(ETH_FRAME_LEN); if (agent->tx.frameout == NULL) { LLDPAD_DBG("InfoLLDPDU: Failed to malloc frame buffer \n"); return false; } memset(agent->tx.frameout, 0, ETH_FRAME_LEN); memcpy(agent->tx.frameout, (void *)ð, sizeof(struct l2_ethhdr)); fb_offset += sizeof(struct l2_ethhdr); /* Generic TLV Pack */ LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops || !np->ops->lldp_mod_gettlv) continue; ptlv = np->ops->lldp_mod_gettlv(port, agent); if (ptlv) { if ((ptlv->size+fb_offset) > ETH_DATA_LEN) goto error; memcpy(agent->tx.frameout+fb_offset, ptlv->tlv, ptlv->size); datasize += ptlv->size; fb_offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } } /* The End TLV marks the end of the LLDP PDU */ ptlv = pack_end_tlv(); if (!ptlv || ((ptlv->size + fb_offset) > ETH_DATA_LEN)) goto error; memcpy(agent->tx.frameout + fb_offset, ptlv->tlv, ptlv->size); datasize += ptlv->size; fb_offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); if (datasize < ETH_MIN_DATA_LEN) agent->tx.sizeout = ETH_ZLEN; else agent->tx.sizeout = fb_offset; return true; error: ptlv = free_pkd_tlv(ptlv); if (agent->tx.frameout) free(agent->tx.frameout); agent->tx.frameout = NULL; LLDPAD_DBG("InfoLLDPDU: packed TLV too large for tx frame\n"); return false; } void txInitializeLLDP(struct lldp_agent *agent) { if (agent->tx.frameout) { free(agent->tx.frameout); agent->tx.frameout = NULL; } agent->tx.state = TX_LLDP_INITIALIZE; agent->rx.state = LLDP_WAIT_PORT_OPERATIONAL; agent->tx.localChange = false; agent->stats.statsFramesOutTotal = 0; agent->timers.reinitDelay = REINIT_DELAY; agent->timers.msgTxHold = DEFAULT_TX_HOLD; agent->timers.msgTxInterval = DEFAULT_TX_INTERVAL; agent->timers.msgFastTx = FAST_TX_INTERVAL; agent->tx.txTTL = 0; agent->msap.length1 = 0; agent->msap.msap1 = NULL; agent->msap.length2 = 0; agent->msap.msap2 = NULL; agent->lldpdu = false; return; } void txInitializeTimers(struct lldp_agent *agent) { agent->timers.txTick = false; agent->tx.txNow = false; agent->tx.localChange = false; agent->timers.txTTR = 0; agent->tx.txFast = 0; agent->timers.txShutdownWhile = 0; agent->rx.newNeighbor = false; agent->timers.txMaxCredit = TX_CREDIT_MAX; agent->timers.txCredit = TX_CREDIT_MAX; agent->timers.txFastInit = TX_FAST_INIT; agent->timers.state = TX_TIMER_BEGIN; return; } bool mibConstrShutdownLLDPDU(struct port *port, struct lldp_agent *agent) { struct l2_ethhdr eth; u8 own_addr[ETH_ALEN]; u32 fb_offset = 0; u32 datasize = 0; struct packed_tlv *ptlv = NULL; struct lldp_module *np; char macstring[30]; if (agent->tx.frameout) { free(agent->tx.frameout); agent->tx.frameout = NULL; } mac2str(agent->mac_addr, macstring, 30); LLDPAD_DBG("%s: mac %s.\n", __func__, macstring); memcpy(eth.h_dest, agent->mac_addr, ETH_ALEN); l2_packet_get_own_src_addr(port->l2,(u8 *)&own_addr); memcpy(eth.h_source, &own_addr, ETH_ALEN); eth.h_proto = htons(ETH_P_LLDP); agent->tx.frameout = (u8 *)malloc(ETH_FRAME_LEN); if (agent->tx.frameout == NULL) { LLDPAD_DBG("ShutdownLLDPDU: Failed to malloc frame buffer \n"); return false; } memset(agent->tx.frameout,0,ETH_FRAME_LEN); memcpy(agent->tx.frameout, (void *)ð, sizeof(struct l2_ethhdr)); fb_offset += sizeof(struct l2_ethhdr); np = find_module_by_id(&lldp_head, LLDP_MOD_MAND); if (!np) goto error; if (!np->ops || !np->ops->lldp_mod_gettlv) goto error; ptlv = np->ops->lldp_mod_gettlv(port, agent); if (ptlv) { if ((ptlv->size + fb_offset) > ETH_DATA_LEN) goto error; /* set the TTL to be 0 TTL TLV */ memset(&ptlv->tlv[ptlv->size - 2], 0, 2); memcpy(agent->tx.frameout + fb_offset, ptlv->tlv, ptlv->size); datasize += ptlv->size; fb_offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } /* The End TLV marks the end of the LLDP PDU */ ptlv = pack_end_tlv(); if (!ptlv || ((ptlv->size + fb_offset) > ETH_DATA_LEN)) goto error; memcpy(agent->tx.frameout + fb_offset, ptlv->tlv, ptlv->size); datasize += ptlv->size; fb_offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); if (datasize < ETH_MIN_DATA_LEN) agent->tx.sizeout = ETH_ZLEN; else agent->tx.sizeout = fb_offset; return true; error: ptlv = free_pkd_tlv(ptlv); if (agent->tx.frameout) free(agent->tx.frameout); agent->tx.frameout = NULL; LLDPAD_DBG("InfoLLDPDU: packed TLV too large for tx frame\n"); return false; } u8 txFrame(struct port *port, struct lldp_agent *agent) { l2_packet_send(port->l2, agent->mac_addr, htons(ETH_P_LLDP), agent->tx.frameout, agent->tx.sizeout); agent->stats.statsFramesOutTotal++; return 0; } void run_tx_sm(struct port *port, struct lldp_agent *agent) { set_tx_state(port, agent); do { switch(agent->tx.state) { case TX_LLDP_INITIALIZE: txInitializeLLDP(agent); break; case TX_IDLE: process_tx_idle(agent); break; case TX_SHUTDOWN_FRAME: process_tx_shutdown_frame(port, agent); break; case TX_INFO_FRAME: process_tx_info_frame(port, agent); break; default: LLDPAD_DBG("ERROR The TX State Machine is broken!\n"); } } while (set_tx_state(port, agent) == true); return; } bool set_tx_state(struct port *port, struct lldp_agent *agent) { if ((port->portEnabled == false) && (port->prevPortEnabled == true)) { LLDPAD_DBG("set_tx_state: port was disabled\n"); tx_change_state(port, agent, TX_LLDP_INITIALIZE); } port->prevPortEnabled = port->portEnabled; switch (agent->tx.state) { case TX_LLDP_INITIALIZE: if (port->portEnabled && ((agent->adminStatus == enabledRxTx) || (agent->adminStatus == enabledTxOnly))) { tx_change_state(port, agent, TX_IDLE); return true; } return false; case TX_IDLE: if ((agent->adminStatus == disabled) || (agent->adminStatus == enabledRxOnly)) { tx_change_state(port, agent, TX_SHUTDOWN_FRAME); return true; } if ((agent->tx.txNow) && ((agent->timers.txCredit > 0))) { tx_change_state(port, agent, TX_INFO_FRAME); return true; } return false; case TX_SHUTDOWN_FRAME: if (agent->timers.txShutdownWhile == 0) { tx_change_state(port, agent, TX_LLDP_INITIALIZE); return true; } return false; case TX_INFO_FRAME: tx_change_state(port, agent, TX_IDLE); return true; default: LLDPAD_DBG("ERROR: The TX State Machine is broken!\n"); return false; } } void process_tx_idle(UNUSED struct lldp_agent *agent) { return; } void process_tx_shutdown_frame(struct port *port, struct lldp_agent *agent) { if (agent->timers.txShutdownWhile == 0) { if (mibConstrShutdownLLDPDU(port, agent)) txFrame(port, agent); agent->timers.txShutdownWhile = agent->timers.reinitDelay; } return; } void process_tx_info_frame(struct port *port, struct lldp_agent *agent) { mibConstrInfoLLDPDU(port, agent); txFrame(port, agent); if (agent->timers.txCredit > 0) agent->timers.txCredit--; agent->tx.txNow = false; return; } void update_tx_timers(struct lldp_agent *agent) { if (agent->timers.txTTR) agent->timers.txTTR--; agent->timers.txTick = true; return; } void tx_timer_change_state(struct lldp_agent *agent, u8 newstate) { switch(newstate) { case TX_TIMER_INITIALIZE: break; case TX_TIMER_IDLE: break; case TX_TIMER_EXPIRES: break; case TX_TICK: break; case SIGNAL_TX: break; case TX_FAST_START: break; default: LLDPAD_DBG("ERROR: tx_timer_change_state: default\n"); } agent->timers.state = newstate; return; } bool set_tx_timers_state(struct port *port, struct lldp_agent *agent) { if ((agent->timers.state == TX_TIMER_BEGIN) || (port->portEnabled == false) || (agent->adminStatus == disabled) || (agent->adminStatus == enabledRxOnly)) { tx_timer_change_state(agent, TX_TIMER_INITIALIZE); } switch (agent->timers.state) { case TX_TIMER_INITIALIZE: if (port->portEnabled && ((agent->adminStatus == enabledRxTx) || (agent->adminStatus == enabledTxOnly))) { tx_timer_change_state(agent, TX_TIMER_IDLE); return true; } return false; case TX_TIMER_IDLE: if (agent->tx.localChange) { tx_timer_change_state(agent, SIGNAL_TX); return true; } if (agent->timers.txTTR == 0) { tx_timer_change_state(agent, TX_TIMER_EXPIRES); return true; } if (agent->rx.newNeighbor) { tx_timer_change_state(agent, TX_FAST_START); return true; } if (agent->timers.txTick) { tx_timer_change_state(agent, TX_TICK); return true; } return false; case TX_TIMER_EXPIRES: tx_timer_change_state(agent, SIGNAL_TX); return true; case SIGNAL_TX: case TX_TICK: tx_timer_change_state(agent, TX_TIMER_IDLE); return true; case TX_FAST_START: tx_timer_change_state(agent, TX_TIMER_EXPIRES); return true; default: LLDPAD_DBG("ERROR: The TX State Machine is broken!\n"); return false; } } void run_tx_timers_sm(struct port *port, struct lldp_agent *agent) { set_tx_timers_state(port, agent); do { switch(agent->timers.state) { case TX_TIMER_INITIALIZE: txInitializeTimers(agent); break; case TX_TIMER_IDLE: break; case TX_TIMER_EXPIRES: if (agent->tx.txFast) agent->tx.txFast--; break; case TX_TICK: agent->timers.txTick = false; if (agent->timers.txCredit < agent->timers.txMaxCredit) agent->timers.txCredit++; break; case SIGNAL_TX: agent->tx.txNow = true; agent->tx.localChange = false; if (agent->tx.txFast) agent->timers.txTTR = agent->timers.msgFastTx; else agent->timers.txTTR = agent->timers.msgTxInterval; break; case TX_FAST_START: agent->rx.newNeighbor = false; if (agent->tx.txFast == 0) agent->tx.txFast = agent->timers.txFastInit; break; default: LLDPAD_DBG("ERROR The TX Timer State Machine " "is broken!\n"); } } while (set_tx_timers_state(port, agent) == true); return; } void tx_change_state(struct port *port, struct lldp_agent *agent, u8 newstate) { switch(newstate) { case TX_LLDP_INITIALIZE: if ((agent->tx.state != TX_SHUTDOWN_FRAME) && port->portEnabled) { assert(port->portEnabled); } break; case TX_IDLE: if (!(agent->tx.state == TX_LLDP_INITIALIZE || agent->tx.state == TX_INFO_FRAME)) { assert(agent->tx.state == TX_LLDP_INITIALIZE); assert(agent->tx.state == TX_INFO_FRAME); } break; case TX_SHUTDOWN_FRAME: case TX_INFO_FRAME: assert(agent->tx.state == TX_IDLE); break; default: LLDPAD_DBG("ERROR: tx_change_state: default\n"); } agent->tx.state = newstate; return; } lldpad-0.9.46/lldp_8021qaz.c000066400000000000000000001462431215142747300153570ustar00rootroot00000000000000/****************************************************************************** LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List ******************************************************************************/ #include #include #include #include #include #include "lldp.h" #include "lldp_8021qaz.h" #include "messages.h" #include "lldp_util.h" #include "dcb_driver_interface.h" #include "config.h" #include "lldp_mand_clif.h" #include "lldp_dcbx_nl.h" #include "lldp/l2_packet.h" #include "lldp/ports.h" #include "lldpad_status.h" #include "lldp_8021qaz_cmds.h" #include "lldp_mand_clif.h" #include "include/linux/dcbnl.h" #include "include/linux/rtnetlink.h" #include "include/linux/netlink.h" #include "lldp_dcbx.h" struct lldp_head lldp_head; struct config_t lldpad_cfg; static int ieee8021qaz_check_pending(struct port *port, struct lldp_agent *); static void run_all_sm(struct port *port, struct lldp_agent *agent); static void ieee8021qaz_mibUpdateObjects(struct port *port); static void ieee8021qaz_app_reset(struct app_tlv_head *head); static int get_ieee_hw(const char *ifname, struct ieee_ets **ets, struct ieee_pfc **pfc, struct app_prio **app, int *cnt); static const struct lldp_mod_ops ieee8021qaz_ops = { .lldp_mod_register = ieee8021qaz_register, .lldp_mod_unregister = ieee8021qaz_unregister, .lldp_mod_gettlv = ieee8021qaz_gettlv, .lldp_mod_rchange = ieee8021qaz_rchange, .lldp_mod_ifup = ieee8021qaz_ifup, .lldp_mod_ifdown = ieee8021qaz_ifdown, .lldp_mod_mibdelete = ieee8021qaz_mibDeleteObject, .get_arg_handler = ieee8021qaz_get_arg_handlers, .timer = ieee8021qaz_check_pending, }; static int ieee8021qaz_check_pending(struct port *port, struct lldp_agent *agent) { struct ieee8021qaz_user_data *iud; struct ieee8021qaz_tlvs *tlv = NULL; if (agent->type != NEAREST_BRIDGE) return 0; if (!port->portEnabled) return 0; iud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8021QAZ); if (iud) { LIST_FOREACH(tlv, &iud->head, entry) { if (!strncmp(port->ifname, tlv->ifname, IFNAMSIZ)) { if (tlv->active && tlv->pending && port->dormantDelay == 1) { tlv->pending = false; ieee8021qaz_app_reset(&tlv->app_head); run_all_sm(port, agent); somethingChangedLocal(port->ifname, agent->type); } break; } } } return 0; } /* LLDP_8021QAZ_MOD_OPS - REGISTER */ struct lldp_module *ieee8021qaz_register(void) { struct lldp_module *mod; struct ieee8021qaz_user_data *iud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("Failed to malloc LLDP-8021QAZ module data"); goto out_err; } iud = malloc(sizeof(*iud)); if (!iud) { free(mod); LLDPAD_ERR("Failed to malloc LLDP-8021QAZ module user data"); goto out_err; } memset((void *) iud, 0, sizeof(struct ieee8021qaz_user_data)); LIST_INIT(&iud->head); mod->id = LLDP_MOD_8021QAZ; mod->ops = &ieee8021qaz_ops; mod->data = iud; LLDPAD_DBG("%s: ieee8021qaz_register SUCCESS\n", __func__); return mod; out_err: LLDPAD_DBG("%s: ieee8021qaz_register FAILED\n", __func__); return NULL; } struct ieee8021qaz_tlvs *ieee8021qaz_data(const char *ifname) { struct ieee8021qaz_user_data *iud; struct ieee8021qaz_tlvs *tlv = NULL; iud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8021QAZ); if (iud) { LIST_FOREACH(tlv, &iud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return tlv; } } return NULL; } static void set_ets_prio_map(const char *arg, u32 *prio_map) { char *argcpy = strdup(arg); char *tokens; int tc, prio; if (!argcpy) return; tokens = strtok(argcpy, ","); while (tokens) { prio = 0x7 & atoi(tokens); tc = 0x7 & atoi(&tokens[2]); *prio_map |= tc << (4 * (7-prio)); tokens = strtok(NULL, ","); } free(argcpy); } static void set_ets_tsa_map(const char *arg, u8 *tsa_map) { int i, type, tc; char *argcpy = strdup(arg); char *tokens; if (!argcpy) return; tokens = strtok(argcpy, ","); for (i = 0; tokens; i++) { tc = atoi(tokens); if ((strcmp(&tokens[2], "strict")) == 0) type = IEEE8021Q_TSA_STRICT; else if ((strcmp(&tokens[2], "cb_shaper")) == 0) type = IEEE8021Q_TSA_CBSHAPER; else if ((strcmp(&tokens[2], "ets")) == 0) type = IEEE8021Q_TSA_ETS; else if ((strcmp(&tokens[2], "vendor")) == 0) type = IEEE8021Q_TSA_VENDOR; else type = IEEE8021Q_TSA_STRICT; tsa_map[tc] = type; tokens = strtok(NULL, ","); } free(argcpy); } static int read_cfg_file(char *ifname, struct lldp_agent *agent, struct ieee8021qaz_tlvs *tlvs) { const char *arg = NULL; char arg_path[256]; int res = 0, i; int willing, pfc_mask, delay; if (agent->type != NEAREST_BRIDGE) return 0; /* Read ETS-CFG willing bit -- default willing enabled */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSCFG), ARG_WILLING); res = get_config_setting(ifname, agent->type, arg_path, &willing, CONFIG_TYPE_INT); if (!res) tlvs->ets->cfgl->willing = !!willing; else tlvs->ets->cfgl->willing = 1; /* Read PFC willing bit -- default willing enabled */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_PFC), ARG_WILLING); res = get_config_setting(ifname, agent->type, arg_path, &willing, CONFIG_TYPE_INT); if (!res) tlvs->pfc->local.willing = !!willing; else tlvs->pfc->local.willing = 1; /* Read and parse ETS-CFG priority map -- * default all priorities TC0 */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSCFG), ARG_ETS_UP2TC); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) set_ets_prio_map(arg, &tlvs->ets->cfgl->prio_map); else tlvs->ets->cfgl->prio_map = 0x00000000; /* Default ETS-CFG num_tc to MAX */ tlvs->ets->cfgl->max_tcs = MAX_TCS; /* Read and parse ETS-REC priority map -- * default all priorities TC0 */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSREC), ARG_ETS_UP2TC); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) set_ets_prio_map(arg, &tlvs->ets->recl->prio_map); else tlvs->ets->recl->prio_map = 0x00000000; /* Read and parse ETS-CFG tc bandwidth map -- * default percentage mapping 0,0,0,0,0,0,0,0 (strict priority) */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSCFG), ARG_ETS_TCBW); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) { char *argcpy = strdup(arg); char *tokens; if (argcpy) { tokens = strtok(argcpy, ","); for (i = 0; tokens; i++) { tlvs->ets->cfgl->tc_bw[i] = atoi(tokens); tokens = strtok(NULL, ","); } free(argcpy); } } /* Read and parse ETS-REC tc bandwidth map -- * default percentage mapping 0,0,0,0,0,0,0,0 (strict priority) */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSREC), ARG_ETS_TCBW); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) { char *argcpy = strdup(arg); char *tokens; if (argcpy) { tokens = strtok(argcpy, ","); for (i = 0; tokens; i++) { tlvs->ets->recl->tc_bw[i] = atoi(tokens); tokens = strtok(NULL, ","); } free(argcpy); } } /* Read and parse ETS-CFG tc transmission selction algorithm map * This defaults to all traffic classes using strict priority */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSCFG), ARG_ETS_TSA); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) { set_ets_tsa_map(arg, tlvs->ets->cfgl->tsa_map); } else { for (i = 0; i < MAX_TCS; i++) tlvs->ets->cfgl->tsa_map[i] = IEEE8021Q_TSA_STRICT; } /* Read and parse ETS-REC tc transmission selction algorithm map * This defaults to all traffic classes using strict priority */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_ETSREC), ARG_ETS_TSA); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (!res) { set_ets_tsa_map(arg, tlvs->ets->recl->tsa_map); } else { for (i = 0; i < MAX_TCS; i++) tlvs->ets->recl->tsa_map[i] = IEEE8021Q_TSA_STRICT; } /* Read and parse PFC enable bitmask -- default 0x00 */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_PFC), ARG_PFC_ENABLED); res = get_config_setting(ifname, agent->type, arg_path, &pfc_mask, CONFIG_TYPE_INT); if (!res) tlvs->pfc->local.pfc_enable = pfc_mask; /* Read and parse PFC delay -- default 0x00 */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_PFC), ARG_PFC_DELAY); res = get_config_setting(ifname, agent->type, arg_path, &delay, CONFIG_TYPE_INT); if (!res) tlvs->pfc->local.delay = delay; /* Default PFC capabilities to MAX */ tlvs->pfc->local.pfc_cap = MAX_TCS; /* Read and add APP data to internal lldpad APP ring */ for (i = 0; i < MAX_APP_ENTRIES; i++) { char *parse; char *app_tuple; u8 prio, sel; long pid; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s%i", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_APP), ARG_APP, i); res = get_config_setting(ifname, agent->type, arg_path, &arg, CONFIG_TYPE_STRING); if (res) continue; /* Parse cfg file input, bounds checking done on set app cmd */ parse = strdup(arg); if (!parse) break; app_tuple = strtok(parse, ","); if (!app_tuple) break; prio = atoi(app_tuple); app_tuple = strtok(NULL, ","); if (!app_tuple) break; sel = atoi(app_tuple); app_tuple = strtok(NULL, ","); if (!app_tuple) break; /* APP Data can be in hex or integer form */ errno = 0; pid = strtol(app_tuple, NULL, 0); if (!errno) ieee8021qaz_mod_app(&tlvs->app_head, 0, prio, sel, (u16) pid, 0); free(parse); } return 0; } inline int get_prio_map(u32 prio_map, int prio) { if (prio > 7) return 0; return (prio_map >> (4 * (7-prio))) & 0xF; } inline void set_prio_map(u32 *prio_map, u8 prio, int tc) { u32 mask = ~(0xffffffff & (0xF << (4 * (7-prio)))); *prio_map &= mask; *prio_map |= tc << (4 * (7-prio)); } /* * get_dcbx_hw - Get bitmask of hardware DCBX version and firmware status * * @ifname: interface name to query * @dcbx: bitmask to store DCBX capabilities * * Returns 0 on success, error value otherwise. */ static int get_dcbx_hw(const char *ifname, __u8 *dcbx) { int err = 0; struct nlattr *attr; struct sockaddr_nl dest_addr; static struct nl_handle *nlhandle; struct nl_msg *nlm = NULL; unsigned char *msg = NULL; struct nlmsghdr *hdr; struct dcbmsg d = { .dcb_family = AF_UNSPEC, .cmd = DCB_CMD_GDCBX, .dcb_pad = 0 }; if (!nlhandle) { nlhandle = nl_handle_alloc(); if (!nlhandle) { LLDPAD_WARN("%s: %s: nl_handle_alloc failed, %s\n", __func__, ifname, nl_geterror()); err = -ENOMEM; goto out; } nl_socket_set_local_port(nlhandle, 0); } err = nl_connect(nlhandle, NETLINK_ROUTE); if (err < 0) { LLDPAD_WARN("%s: %s nlconnect failed abort get ieee, %s\n", __func__, ifname, nl_geterror()); goto out; } nlm = nlmsg_alloc_simple(RTM_GETDCB, NLM_F_REQUEST); if (!nlm) { LLDPAD_WARN("%s: %s nlmsg_alloc failed abort get ieee, %s\n", __func__, ifname, nl_geterror()); err = -ENOMEM; goto out; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; nlmsg_set_dst(nlm, &dest_addr); err = nlmsg_append(nlm, &d, sizeof(d), NLMSG_ALIGNTO); if (err < 0) goto out; err = nla_put(nlm, DCB_ATTR_IFNAME, strlen(ifname)+1, ifname); if (err < 0) goto out; err = nl_send_auto_complete(nlhandle, nlm); if (err <= 0) { LLDPAD_WARN("%s: %s 802.1Qaz get app attributes failed\n", __func__, ifname); goto out; } err = nl_recv(nlhandle, &dest_addr, &msg, NULL); if (err <= 0) { LLDPAD_WARN("%s: %s: nl_recv returned %d\n", __func__, ifname, err); goto out; } hdr = (struct nlmsghdr *) msg; attr = nlmsg_find_attr(hdr, sizeof(d), DCB_ATTR_DCBX); if (!attr) { LLDPAD_DBG("%s: %s: nlmsg_find_attr failed, no GDCBX support\n", __func__, ifname); goto out; } *dcbx = nla_get_u8(attr); out: nlmsg_free(nlm); free(msg); if (nlhandle) nl_close(nlhandle); return err; } /* * LLDP_8021QAZ_MOD_OPS - IFUP * * Load TLV values (either from config file, command prompt or defaults), * set up adapter, initialize FSMs and build tlvs * * Check if a 'config' file exists (to load tlv values). If YES, load save them * as new defaults. If NO, load defaults. Also, check for TLV values via cmd * prompt. Then initialize FSMs for each tlv and finally build the tlvs */ void ieee8021qaz_ifup(char *ifname, struct lldp_agent *agent) { struct port *port = NULL; struct ieee8021qaz_tlvs *tlvs; struct ieee8021qaz_user_data *iud; int adminstatus, cnt, len; __u8 dcbx = 0; struct ieee_ets *ets = NULL; struct ieee_pfc *pfc = NULL; struct app_prio *data = NULL; int err; if (agent->type != NEAREST_BRIDGE) return; /* 802.1Qaz does not support bonded or vlan devices */ if (is_bond(ifname) || is_vlan(ifname)) return; err = get_dcbx_hw(ifname, &dcbx); if (err < 0) return; /* If hardware is not DCBX IEEE compliant or it is managed * by an LLD agent most likely a firmware agent abort */ if (!(dcbx & DCB_CAP_DCBX_VER_IEEE) || (dcbx & DCB_CAP_DCBX_LLD_MANAGED)) return; /* If 802.1Qaz is already configured no need to continue */ tlvs = ieee8021qaz_data(ifname); if (tlvs) goto initialized; /* if there is no persistent adminStatus setting then set to enabledRx * but do not persist that as a setting. */ if (get_config_setting(ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT)) set_lldp_agent_admin(ifname, agent->type, enabledRxOnly); /* lookup port data */ port = porthead; while (port != NULL) { if (!strncmp(ifname, port->ifname, MAX_DEVICE_NAME_LEN)) break; port = port->next; } /* * Check if link down and/or tlvs exist for current port. * If true, then return without any further work */ if (!port) return; /* Initializing TLV structs */ tlvs = malloc(sizeof(*tlvs)); if (!tlvs) { LLDPAD_DBG("%s: ifname %s malloc failed.\n", __func__, ifname); return; } memset(tlvs, 0, sizeof(*tlvs)); tlvs->rx = malloc(sizeof(*tlvs->rx)); if (!tlvs->rx) { free(tlvs); LLDPAD_DBG("%s: %s malloc failed.\n", __func__, ifname); return; } memset(tlvs->rx, 0, sizeof(*tlvs->rx)); /* Initializing the ieee8021qaz_tlvs struct */ strncpy(tlvs->ifname, ifname, IFNAMSIZ); tlvs->port = port; tlvs->ieee8021qazdu = 0; l2_packet_get_own_src_addr(port->l2, tlvs->local_mac); memset(tlvs->remote_mac, 0, ETH_ALEN); /* Initialize all TLVs */ tlvs->ets = malloc(sizeof(*tlvs->ets)); if (!tlvs->ets) goto err; memset(tlvs->ets, 0, sizeof(*tlvs->ets)); tlvs->ets->cfgl = malloc(sizeof(*tlvs->ets->cfgl)); if (!tlvs->ets->cfgl) goto err; tlvs->ets->recl = malloc(sizeof(*tlvs->ets->recl)); if (!tlvs->ets->recl) goto err_recl; tlvs->pfc = malloc(sizeof(*tlvs->pfc)); if (!tlvs->pfc) goto err_pfc; memset(tlvs->ets->cfgl, 0, sizeof(*tlvs->ets->cfgl)); memset(tlvs->ets->recl, 0, sizeof(*tlvs->ets->recl)); memset(tlvs->pfc, 0, sizeof(*tlvs->pfc)); tlvs->ets->pending = 1; tlvs->ets->current_state = 0; tlvs->pfc->pending = 1; tlvs->pfc->current_state = 0; tlvs->pfc->remote_param = 0; LIST_INIT(&tlvs->app_head); read_cfg_file(port->ifname, agent, tlvs); iud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8021QAZ); LIST_INSERT_HEAD(&iud->head, tlvs, entry); initialized: /* Query hardware and set maximum number of TCs with hardware values */ len = get_ieee_hw(ifname, &ets, &pfc, &data, &cnt); if (len > 0) { tlvs->ets->cfgl->max_tcs = ets->ets_cap; tlvs->pfc->local.pfc_cap = pfc->pfc_cap; free(ets); free(pfc); free(data); } /* if the dcbx field is filled in by the dcbx query then the * kernel is supports IEEE mode, so make IEEE DCBX active by default. */ if (!dcbx || (dcbx_get_legacy_version(ifname) & ~MASK_DCBX_FORCE)) { tlvs->active = false; } else { tlvs->active = true; tlvs->pending = true; } return; err_pfc: free(tlvs->ets->recl); err_recl: free(tlvs->ets->cfgl); err: free(tlvs->ets); free(tlvs->rx); free(tlvs); LLDPAD_WARN("%s: %s malloc failed.\n", __func__, ifname); return; } /* * ets_sm - Asymmetric State Machine for the ETS tlv */ static void ets_sm(struct etscfg_obj *localAdminParam, struct etsrec_obj *remoteParam, bool *state) { int willing = localAdminParam->willing; if (willing && remoteParam) *state = RX_RECOMMEND; else if (!willing || !remoteParam) *state = INIT; } /* * cmp_mac_addrs - Compares 2 MAC addresses. * returns 1, if first_mac > second_mac; else 0 */ static bool cmp_mac_addrs(u8 first_mac[], u8 second_mac[]) { int i; for (i = 0; i < ETH_ALEN; i++) { if (first_mac[i] == second_mac[i]) continue; if (first_mac[i] < second_mac[i]) return 0; return 1; } return 0; } /* * pfc_sm - Symmetric State Machine for the PFC tlv */ static void pfc_sm(struct ieee8021qaz_tlvs *tlvs) { bool local_willing, remote_willing; bool remote_param, cmp_mac; local_willing = tlvs->pfc->local.willing; remote_willing = tlvs->pfc->remote.willing; remote_param = tlvs->pfc->remote_param; cmp_mac = cmp_mac_addrs(tlvs->local_mac, tlvs->remote_mac); if ((local_willing && !remote_willing && remote_param) || (local_willing && remote_willing && !cmp_mac)) tlvs->pfc->current_state = RX_RECOMMEND; else if (!local_willing || !remote_param || (local_willing && remote_willing && cmp_mac)) tlvs->pfc->current_state = INIT; } #ifdef LLDPAD_8021QAZ_DEBUG void print_ets(struct ieee_ets *ets) { int i; printf("ETS:\n"); printf("\tcap %2x cbs %2x\n", ets->ets_cap, ets->cbs); printf("\tets tc_tx_bw: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_tx_bw[i]); printf("\n"); printf("\tets tc_rx_bw: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_rx_bw[i]); printf("\n"); printf("\tets tc_tsa: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_tsa[i]); printf("\n"); printf("\tets prio_tc: "); for (i = 0; i < 8; i++) printf("%i ", ets->prio_tc[i]); printf("\n"); } void print_pfc(struct ieee_pfc *pfc) { int i; printf("PFC:\n"); printf("\t cap %2x en %2x\n", pfc->pfc_cap, pfc->pfc_en); printf("\t mbc %2x delay %i\n", pfc->mbc, pfc->delay); printf("\t requests: "); for (i = 0; i < 8; i++) printf("%llu ", pfc->requests[i]); printf("\n"); printf("\t indications: "); for (i = 0; i < 8; i++) printf("%llu ", pfc->indications[i]); printf("\n"); } #endif /* * get_ieee_hw - Populates IEEE data structures with hardware DCB attributes. * * @ifname: interface name to query * @ets: pointer to copy returned ETS struct * @pfc: pointer to copy returned PFC struct * @app: pointer to copy concatenated APP entries * @cnt: number of app entries returned * * Returns nlmsg bytes size on success otherwise negative error code. */ static int get_ieee_hw(const char *ifname, struct ieee_ets **ets, struct ieee_pfc **pfc, struct app_prio **app, int *cnt) { int err = 0; int rem; int itr = 0; struct sockaddr_nl dest_addr; static struct nl_handle *nlhandle; struct nl_msg *nlm; unsigned char *msg = NULL; struct nlmsghdr *hdr; struct nlattr *app_attr, *attr, *nattr; struct dcbmsg d = { .dcb_family = AF_UNSPEC, .cmd = DCB_CMD_IEEE_GET, .dcb_pad = 0 }; if (!nlhandle) { nlhandle = nl_handle_alloc(); if (!nlhandle) { LLDPAD_WARN("%s: %s: nl_handle_alloc failed, %s\n", __func__, ifname, nl_geterror()); *cnt = 0; return -ENOMEM; } nl_socket_set_local_port(nlhandle, 0); } if (nl_connect(nlhandle, NETLINK_ROUTE) < 0) { LLDPAD_WARN("%s: %s nlconnect failed abort get ieee, %s\n", __func__, ifname, nl_geterror()); goto out1; } nlm = nlmsg_alloc_simple(RTM_GETDCB, NLM_F_REQUEST); if (!nlm) { err = -ENOMEM; goto out1; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; nlmsg_set_dst(nlm, &dest_addr); err = nlmsg_append(nlm, &d, sizeof(d), NLMSG_ALIGNTO); if (err < 0) goto out; err = nla_put(nlm, DCB_ATTR_IFNAME, strlen(ifname)+1, ifname); if (err < 0) goto out; err = nl_send_auto_complete(nlhandle, nlm); if (err <= 0) { LLDPAD_WARN("%s: %s 802.1Qaz get app attributes failed\n", __func__, ifname); goto out; } err = nl_recv(nlhandle, &dest_addr, &msg, NULL); if (err <= 0) { LLDPAD_WARN("%s: %s: nl_recv returned %d\n", __func__, ifname, err); goto out; } hdr = (struct nlmsghdr *) msg; attr = nlmsg_find_attr(hdr, sizeof(d), DCB_ATTR_IEEE); if (!attr) { LLDPAD_WARN("%s: %s: nlmsg_find_attr failed\n", __func__, ifname); goto out; } *app = malloc(sizeof(struct app_prio)); if (*app == NULL) { err = -ENOMEM; goto out; } *ets = malloc(sizeof(struct ieee_ets)); if (*ets == NULL) { err = -ENOMEM; free(*app); goto out; } *pfc = malloc(sizeof(struct ieee_pfc)); if (*pfc == NULL) { err = -ENOMEM; free(*app); free(*ets); goto out; } memset(*pfc, 0, sizeof(struct ieee_pfc)); memset(*ets, 0, sizeof(struct ieee_ets)); memset(*app, 0, sizeof(struct app_prio)); nla_for_each_nested(nattr, attr, rem) { if (nla_type(nattr) == DCB_ATTR_IEEE_APP_TABLE) { struct app_prio *this_app = *app; nla_for_each_nested(app_attr, nattr, rem) { struct dcb_app *data = nla_data(app_attr); LLDPAD_DBG("app %i %i %i\n", data->selector, data->protocol, data->priority); this_app = realloc(this_app, sizeof(struct app_prio) * itr + sizeof(struct app_prio)); if (!this_app) { free(this_app); free(*ets); free(*pfc); err = -ENOMEM; LLDPAD_WARN("%s: %s: realloc failed\n", __func__, ifname); goto out; } this_app[itr].prs = (data->priority << 5) | data->selector; this_app[itr].pid = htons(data->protocol); itr++; } /* realloc may have moved app so reset it */ *app = this_app; } if (nla_type(nattr) == DCB_ATTR_IEEE_ETS) { struct ieee_ets *nl_ets = nla_data(nattr); memcpy(*ets, nl_ets, sizeof(struct ieee_ets)); #ifdef LLDPAD_8021QAZ_DEBUG print_ets(nl_ets); #endif } if (nla_type(nattr) == DCB_ATTR_IEEE_PFC) { struct ieee_pfc *nl_pfc = nla_data(nattr); memcpy(*pfc, nl_pfc, sizeof(struct ieee_pfc)); #ifdef LLDPAD_8021QAZ_DEBUG print_pfc(nl_pfc); #endif } } out: nlmsg_free(nlm); free(msg); nl_close(nlhandle); out1: *cnt = itr; return err; } static int del_ieee_hw(const char *ifname, struct dcb_app *app_data) { int err = 0; struct nlattr *ieee, *app; struct sockaddr_nl dest_addr; static struct nl_handle *nlhandle; struct nl_msg *nlm; struct dcbmsg d = { .dcb_family = AF_UNSPEC, .cmd = DCB_CMD_IEEE_DEL, .dcb_pad = 0 }; if (!nlhandle) { nlhandle = nl_handle_alloc(); if (!nlhandle) { LLDPAD_WARN("%s: %s: nl_handle_alloc failed, %s\n", __func__, ifname, nl_geterror()); return -ENOMEM; } nl_socket_set_local_port(nlhandle, 0); } if (nl_connect(nlhandle, NETLINK_ROUTE) < 0) { LLDPAD_WARN("%s: %s nlconnect failed abort hardware set, %s\n", __func__, ifname, nl_geterror()); err = -EIO; goto out1; } nlm = nlmsg_alloc_simple(RTM_SETDCB, NLM_F_REQUEST); if (!nlm) { err = -ENOMEM; goto out2; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; nlmsg_set_dst(nlm, &dest_addr); err = nlmsg_append(nlm, &d, sizeof(d), NLMSG_ALIGNTO); if (err < 0) goto out; err = nla_put(nlm, DCB_ATTR_IFNAME, strlen(ifname)+1, ifname); if (err < 0) goto out; ieee = nla_nest_start(nlm, DCB_ATTR_IEEE); if (!ieee) { err = -ENOMEM; goto out; } if (app_data) { app = nla_nest_start(nlm, DCB_ATTR_IEEE_APP_TABLE); if (!app) { err = -ENOMEM; goto out; } err = nla_put(nlm, DCB_ATTR_IEEE_APP, sizeof(*app_data), app_data); if (err < 0) goto out; nla_nest_end(nlm, app); } nla_nest_end(nlm, ieee); err = nl_send_auto_complete(nlhandle, nlm); if (err <= 0) LLDPAD_WARN("%s: %s 802.1Qaz set attributes failed\n", __func__, ifname); out: nlmsg_free(nlm); out2: nl_close(nlhandle); out1: return err; } static int set_ieee_hw(const char *ifname, struct ieee_ets *ets_data, struct ieee_pfc *pfc_data, struct dcb_app *app_data) { int err = 0; struct nlattr *ieee, *app; struct sockaddr_nl dest_addr; static struct nl_handle *nlhandle; struct nl_msg *nlm; struct dcbmsg d = { .dcb_family = AF_UNSPEC, .cmd = DCB_CMD_IEEE_SET, .dcb_pad = 0 }; if (!nlhandle) { nlhandle = nl_handle_alloc(); if (!nlhandle) { LLDPAD_WARN("%s: %s: nl_handle_alloc failed, %s\n", __func__, ifname, nl_geterror()); return -ENOMEM; } nl_socket_set_local_port(nlhandle, 0); } if (!ets_data && !pfc_data && !app_data) { err = 0; goto out1; } #ifdef LLDPAD_8021QAZ_DEBUG if (ets_data) print_ets(ets_data); if (pfc_data) print_pfc(pfc_data); #endif if (nl_connect(nlhandle, NETLINK_ROUTE) < 0) { LLDPAD_WARN("%s: %s nlconnect failed abort hardware set, %s\n", __func__, ifname, nl_geterror()); err = -EIO; goto out1; } nlm = nlmsg_alloc_simple(RTM_SETDCB, NLM_F_REQUEST); if (!nlm) { err = -ENOMEM; goto out2; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; nlmsg_set_dst(nlm, &dest_addr); err = nlmsg_append(nlm, &d, sizeof(d), NLMSG_ALIGNTO); if (err < 0) goto out; err = nla_put(nlm, DCB_ATTR_IFNAME, strlen(ifname)+1, ifname); if (err < 0) goto out; ieee = nla_nest_start(nlm, DCB_ATTR_IEEE); if (!ieee) { err = -ENOMEM; goto out; } if (ets_data) { err = nla_put(nlm, DCB_ATTR_IEEE_ETS, sizeof(*ets_data), ets_data); if (err < 0) goto out; } if (pfc_data) { err = nla_put(nlm, DCB_ATTR_IEEE_PFC, sizeof(*pfc_data), pfc_data); if (err < 0) goto out; } if (app_data) { app = nla_nest_start(nlm, DCB_ATTR_IEEE_APP_TABLE); if (!app) { err = -ENOMEM; goto out; } err = nla_put(nlm, DCB_ATTR_IEEE_APP, sizeof(*app_data), app_data); if (err < 0) goto out; nla_nest_end(nlm, app); } nla_nest_end(nlm, ieee); err = nl_send_auto_complete(nlhandle, nlm); if (err <= 0) LLDPAD_WARN("%s: %s 802.1Qaz set attributes failed\n", __func__, ifname); out: nlmsg_free(nlm); out2: nl_close(nlhandle); out1: return err; } static void ets_cfg_to_ieee(struct ieee_ets *ieee, struct etscfg_obj *cfg) { int i; memcpy(ieee->tc_tx_bw, cfg->tc_bw, MAX_TCS); memcpy(ieee->tc_rx_bw, cfg->tc_bw, MAX_TCS); memcpy(ieee->tc_tsa, cfg->tsa_map, MAX_TCS); for (i = 0; i < MAX_USER_PRIORITIES; i++) ieee->prio_tc[i] = get_prio_map(cfg->prio_map, i); memcpy(ieee->tc_reco_bw, cfg->tc_bw, MAX_TCS); memcpy(ieee->tc_reco_tsa, cfg->tsa_map, MAX_TCS); for (i = 0; i < MAX_USER_PRIORITIES; i++) ieee->reco_prio_tc[i] = get_prio_map(cfg->prio_map, i); return; } static void ets_rec_to_ieee(struct ieee_ets *ieee, struct etsrec_obj *rec) { int i; memcpy(ieee->tc_tx_bw, rec->tc_bw, MAX_TCS); memcpy(ieee->tc_rx_bw, rec->tc_bw, MAX_TCS); memcpy(ieee->tc_tsa, rec->tsa_map, MAX_TCS); for (i = 0; i < MAX_TCS; i++) ieee->prio_tc[i] = get_prio_map(rec->prio_map, i); memcpy(ieee->tc_reco_bw, rec->tc_bw, MAX_TCS); memcpy(ieee->tc_reco_tsa, rec->tsa_map, MAX_TCS); for (i = 0; i < MAX_TCS; i++) ieee->reco_prio_tc[i] = get_prio_map(rec->prio_map, i); return; } void run_all_sm(struct port *port, struct lldp_agent *agent) { struct ieee8021qaz_tlvs *tlvs; struct ieee_ets *ets; struct ieee_pfc *pfc; struct pfc_obj *pfc_obj; if (agent->type != NEAREST_BRIDGE) return; tlvs = ieee8021qaz_data(port->ifname); if (!tlvs) return; ets_sm(tlvs->ets->cfgl, tlvs->ets->recr, &tlvs->ets->current_state); ets = malloc(sizeof(*ets)); if (!ets) { LLDPAD_WARN("%s: %s: ets malloc failed\n", __func__, port->ifname); return; } memset(ets, 0, sizeof(*ets)); if (tlvs->ets->current_state == RX_RECOMMEND) ets_rec_to_ieee(ets, tlvs->ets->recr); else ets_cfg_to_ieee(ets, tlvs->ets->cfgl); pfc_sm(tlvs); if (tlvs->pfc->current_state == RX_RECOMMEND) pfc_obj = &tlvs->pfc->remote; else pfc_obj = &tlvs->pfc->local; pfc = malloc(sizeof(*pfc)); if (!pfc) { LLDPAD_WARN("%s: %s: pfc malloc failed\n", __func__, port->ifname); goto out; } memset(pfc, 0, sizeof(*pfc)); pfc->pfc_en = pfc_obj->pfc_enable; pfc->mbc = pfc_obj->mbc; pfc->delay = pfc_obj->delay; if (ieee8021qaz_check_active(port->ifname)) { set_dcbx_mode(port->ifname, DCB_CAP_DCBX_VER_IEEE | DCB_CAP_DCBX_HOST); set_ieee_hw(port->ifname, ets, pfc, NULL); ieee8021qaz_app_sethw(port->ifname, &tlvs->app_head); } out: free(pfc); free(ets); } /* * bld_ieee8021qaz_etscfg_tlv - builds the ETS Configuration TLV * Returns 1 on success, NULL if the TLV fail to build correctly. */ static struct unpacked_tlv * bld_ieee8021qaz_etscfg_tlv(struct ieee8021qaz_tlvs *tlvs) { struct ieee8021qaz_tlv_etscfg *etscfg; struct unpacked_tlv *tlv = create_tlv(); int i = 0; if (!tlv) return NULL; etscfg = (struct ieee8021qaz_tlv_etscfg *)malloc(sizeof(*etscfg)); if (!etscfg) { LLDPAD_WARN("%s: Failed to malloc etscfg\n", __func__); free(tlv); return NULL; } memset(etscfg, 0, sizeof(*etscfg)); hton24(etscfg->oui, OUI_IEEE_8021); etscfg->subtype = LLDP_8021QAZ_ETSCFG; etscfg->wcrt = tlvs->ets->cfgl->willing << 7 | tlvs->ets->cfgl->cbs << 6 | (tlvs->ets->cfgl->max_tcs & 0x7); if (tlvs->ets->current_state == INIT) { etscfg->prio_map = htonl(tlvs->ets->cfgl->prio_map); for (i = 0; i < MAX_TCS; i++) { etscfg->tc_bw[i] = tlvs->ets->cfgl->tc_bw[i]; etscfg->tsa_map[i] = tlvs->ets->cfgl->tsa_map[i]; } } else { etscfg->prio_map = htonl(tlvs->ets->recr->prio_map); for (i = 0; i < MAX_TCS; i++) { etscfg->tc_bw[i] = tlvs->ets->recr->tc_bw[i]; etscfg->tsa_map[i] = tlvs->ets->recr->tsa_map[i]; } } tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(struct ieee8021qaz_tlv_etscfg); tlv->info = (u8 *)etscfg; if (OUI_SUB_SIZE > tlv->length) goto error; return tlv; error: if (tlv) { if (tlv->info) free(tlv->info); free(tlv); } LLDPAD_WARN("%s: Failed\n", __func__); return NULL; } /* * bld_ieee8021qaz_etsrec_tlv - builds the ETS Recommendation TLV * Returns 1 on success, NULL if the TLV fail to build correctly. */ struct unpacked_tlv * bld_ieee8021qaz_etsrec_tlv(struct ieee8021qaz_tlvs *tlvs) { struct ieee8021qaz_tlv_etsrec *etsrec; struct unpacked_tlv *tlv = create_tlv(); int i = 0; if (!tlv) return NULL; etsrec = (struct ieee8021qaz_tlv_etsrec *)malloc(sizeof(*etsrec)); if (!etsrec) { LLDPAD_WARN("%s: Failed to malloc etscfg\n", __func__); free(tlv); return NULL; } memset(etsrec, 0, sizeof(*etsrec)); hton24(etsrec->oui, OUI_IEEE_8021); etsrec->subtype = LLDP_8021QAZ_ETSREC; etsrec->prio_map = htonl(tlvs->ets->recl->prio_map); for (i = 0; i < MAX_TCS; i++) { etsrec->tc_bw[i] = tlvs->ets->recl->tc_bw[i]; etsrec->tsa_map[i] = tlvs->ets->recl->tsa_map[i]; } tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(struct ieee8021qaz_tlv_etsrec); tlv->info = (u8 *)etsrec; if (OUI_SUB_SIZE > tlv->length) goto error; return tlv; error: if (tlv) { if (tlv->info) free(tlv->info); free(tlv); } LLDPAD_WARN("%s: Failed\n", __func__); return NULL; } /* * bld_ieee8021qaz_pfc_tlv - builds the PFC Control Configuration TLV * Returns unpacket tlv or NULL if the TLV fails to build correctly. */ static struct unpacked_tlv * bld_ieee8021qaz_pfc_tlv(struct ieee8021qaz_tlvs *tlvs) { struct ieee8021qaz_tlv_pfc *pfc; struct unpacked_tlv *tlv = create_tlv(); if (!tlv) return NULL; pfc = (struct ieee8021qaz_tlv_pfc *)malloc(sizeof(*pfc)); if (!pfc) { LLDPAD_WARN("%s: Failed to malloc pfc\n", __func__); free(tlv); return NULL; } memset(pfc, 0, sizeof(*pfc)); hton24(pfc->oui, OUI_IEEE_8021); pfc->subtype = LLDP_8021QAZ_PFC; pfc->wmrc = tlvs->pfc->local.willing << 7 | tlvs->pfc->local.mbc << 6 | tlvs->pfc->local.pfc_cap; if (tlvs->pfc->current_state == INIT) pfc->pfc_enable = tlvs->pfc->local.pfc_enable; else pfc->pfc_enable = tlvs->pfc->remote.pfc_enable; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(struct ieee8021qaz_tlv_pfc); tlv->info = (u8 *)pfc; if (OUI_SUB_SIZE > tlv->length) goto error; return tlv; error: if (tlv) { if (tlv->info) free(tlv->info); free(tlv); } LLDPAD_WARN("%s: Failed\n", __func__); return NULL; } /* * bld_ieee8021qaz_app_tlv - builds the APP TLV * Returns unacked tlv or NULL if the TLV fails to build correctly. */ static struct unpacked_tlv * bld_ieee8021qaz_app_tlv(char *ifname) { struct ieee8021qaz_tlv_app *app = NULL; struct unpacked_tlv *tlv; struct ieee_ets *ets = NULL; struct ieee_pfc *pfc = NULL; struct app_prio *data = NULL; __u8 *ptr; int cnt, err; tlv = create_tlv(); if (!tlv) return NULL; err = get_ieee_hw(ifname, &ets, &pfc, &data, &cnt); if (!err) { LLDPAD_WARN("%s: %s: get_ieee_hw failed\n", __func__, ifname); goto error; } app = malloc(sizeof(*app) + (sizeof(*data) * cnt)); if (!app) { LLDPAD_WARN("%s: Failed to malloc app\n", __func__); goto error; } memset(app, 0, sizeof(*app)); hton24(app->oui, OUI_IEEE_8021); app->subtype = LLDP_8021QAZ_APP; ptr = (u8 *) app + sizeof(*app); memcpy(ptr, data, sizeof(*data) * cnt); tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(struct ieee8021qaz_tlv_app) + (cnt * 3); tlv->info = (u8 *)app; if (OUI_SUB_SIZE > tlv->length) { LLDPAD_WARN("%s: %s: tlv->length = %d, cnt=%d\n", __func__, ifname, tlv->length, cnt); goto error; } free(ets); free(pfc); free(data); return tlv; error: free(tlv); free(ets); free(pfc); free(app); free(data); LLDPAD_WARN("%s: Failed\n", __func__); return NULL; } /* * ieee8021qaz_bld_tlv - builds all IEEE8021QAZ TLVs * Returns 1 on success, NULL if any of the TLVs fail to build correctly. */ static struct packed_tlv *ieee8021qaz_bld_tlv(struct port *port, struct lldp_agent *agent) { struct ieee8021qaz_tlvs *data; struct packed_tlv *ptlv = NULL; struct unpacked_tlv *etscfg_tlv, *etsrec_tlv, *pfc_tlv, *app_tlv; size_t size; if (agent->type != NEAREST_BRIDGE) return NULL; data = ieee8021qaz_data(port->ifname); if (!data) return ptlv; etscfg_tlv = etsrec_tlv = pfc_tlv = app_tlv = NULL; if (!data->active) return ptlv; if (!is_tlv_txdisabled(port->ifname, agent->type, TLVID_8021(LLDP_8021QAZ_ETSCFG))) etscfg_tlv = bld_ieee8021qaz_etscfg_tlv(data); if (is_tlv_txenabled(port->ifname, agent->type, TLVID_8021(LLDP_8021QAZ_ETSREC))) etsrec_tlv = bld_ieee8021qaz_etsrec_tlv(data); if (!is_tlv_txdisabled(port->ifname, agent->type, TLVID_8021(LLDP_8021QAZ_PFC))) pfc_tlv = bld_ieee8021qaz_pfc_tlv(data); if (is_tlv_txenabled(port->ifname, agent->type, TLVID_8021(LLDP_8021QAZ_APP))) app_tlv = bld_ieee8021qaz_app_tlv(port->ifname); size = TLVSIZE(etscfg_tlv) + TLVSIZE(etsrec_tlv) + TLVSIZE(pfc_tlv) + TLVSIZE(app_tlv); ptlv = create_ptlv(); if (!ptlv) goto err; ptlv->tlv = malloc(size); if (!ptlv->tlv) goto err; ptlv->size = 0; PACK_TLV_AFTER(etscfg_tlv, ptlv, size, err); PACK_TLV_AFTER(etsrec_tlv, ptlv, size, err); PACK_TLV_AFTER(pfc_tlv, ptlv, size, err); PACK_TLV_AFTER(app_tlv, ptlv, size, err); err: free_unpkd_tlv(etscfg_tlv); free_unpkd_tlv(etsrec_tlv); free_unpkd_tlv(pfc_tlv); free_unpkd_tlv(app_tlv); return ptlv; } /* LLDP_8021QAZ_MOD_OPS - GETTLV */ struct packed_tlv *ieee8021qaz_gettlv(struct port *port, struct lldp_agent *agent) { struct packed_tlv *ptlv = NULL; if (agent->type != NEAREST_BRIDGE) return NULL; /* Update TLV State Machines */ run_all_sm(port, agent); /* Build TLVs */ ptlv = ieee8021qaz_bld_tlv(port, agent); return ptlv; } static bool unpack_ieee8021qaz_tlvs(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { /* Unpack tlvs and store in rx */ struct ieee8021qaz_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) { LLDPAD_INFO("%s: %s: ignoring tlv from remote bridge\n", __func__, port->ifname); return false; } tlvs = ieee8021qaz_data(port->ifname); /* Process */ switch (tlv->info[OUI_SIZE]) { case IEEE8021QAZ_ETSCFG_TLV: if (tlvs->rx->etscfg == NULL) { tlvs->ieee8021qazdu |= RCVD_IEEE8021QAZ_TLV_ETSCFG; tlvs->rx->etscfg = tlv; } else { LLDPAD_WARN("%s: %s: 802.1Qaz Duplicate ETSCFG TLV\n", __func__, port->ifname); agent->rx.dupTlvs |= DUP_IEEE8021QAZ_TLV_ETSCFG; return false; } break; case IEEE8021QAZ_ETSREC_TLV: if (tlvs->rx->etsrec == NULL) { tlvs->ieee8021qazdu |= RCVD_IEEE8021QAZ_TLV_ETSREC; tlvs->rx->etsrec = tlv; } else { LLDPAD_WARN("%s: %s: 802.1Qaz Duplicate ETSREC TLV\n", __func__, port->ifname); agent->rx.dupTlvs |= DUP_IEEE8021QAZ_TLV_ETSREC; return false; } break; case IEEE8021QAZ_PFC_TLV: if (tlvs->rx->pfc == NULL) { tlvs->ieee8021qazdu |= RCVD_IEEE8021QAZ_TLV_PFC; tlvs->rx->pfc = tlv; } else { LLDPAD_WARN("%s: %s: 802.1Qaz Duplicate PFC TLV\n", __func__, port->ifname); agent->rx.dupTlvs |= DUP_IEEE8021QAZ_TLV_PFC; return false; } break; case IEEE8021QAZ_APP_TLV: if (tlvs->rx->app == NULL) { tlvs->ieee8021qazdu |= RCVD_IEEE8021QAZ_TLV_APP; tlvs->rx->app = tlv; } else { LLDPAD_WARN("%s: %s: 802.1Qaz Duplicate APP TLV\n", __func__, port->ifname); agent->rx.dupTlvs |= DUP_IEEE8021QAZ_TLV_APP; return false; } break; default: LLDPAD_INFO("%s: %s: Unknown TLV 0x%04x\n", __func__, port->ifname, tlv->info[OUI_SIZE]); return false; } return true; } static void clear_ieee8021qaz_rx(struct ieee8021qaz_tlvs *tlvs) { if (!tlvs) return; if (!tlvs->rx) return; if (tlvs->rx->ieee8021qaz) tlvs->rx->ieee8021qaz = free_unpkd_tlv(tlvs->rx->ieee8021qaz); if (tlvs->rx->etscfg) tlvs->rx->etscfg = free_unpkd_tlv(tlvs->rx->etscfg); if (tlvs->rx->etsrec) tlvs->rx->etsrec = free_unpkd_tlv(tlvs->rx->etsrec); if (tlvs->rx->pfc) tlvs->rx->pfc = free_unpkd_tlv(tlvs->rx->pfc); if (tlvs->rx->app) tlvs->rx->app = free_unpkd_tlv(tlvs->rx->app); free(tlvs->rx); tlvs->rx = NULL; } static void process_ieee8021qaz_etscfg_tlv(struct port *port) { struct ieee8021qaz_tlvs *tlvs; u8 offset = 0; int i = 0; tlvs = ieee8021qaz_data(port->ifname); offset = OUI_SUB_SIZE; if (tlvs->ets->cfgr) free(tlvs->ets->cfgr); tlvs->ets->cfgr = malloc(sizeof(*tlvs->ets->cfgr)); if (!tlvs->ets->cfgr) { LLDPAD_WARN("%s: %s: cfgr malloc failed\n", __func__, port->ifname); return; } if (tlvs->rx->etscfg->info[offset] & BIT7) tlvs->ets->cfgr->willing = true; else tlvs->ets->cfgr->willing = false; if (tlvs->rx->etscfg->info[offset] & BIT6) tlvs->ets->cfgr->cbs = true; else tlvs->ets->cfgr->cbs = false; tlvs->ets->cfgr->max_tcs = tlvs->rx->etscfg->info[offset] & 0x07; /*Moving offset to PRIO_MAP */ offset += 1; tlvs->ets->cfgr->prio_map = 0; for (i = 0; i < 4; i++) { u8 temp1 = 0, temp2 = 0; temp1 = (tlvs->rx->etscfg->info[offset] >> 4) & 0x0F; temp2 = tlvs->rx->etscfg->info[offset] & 0x0F; set_prio_map(&tlvs->ets->cfgr->prio_map, (2*i), temp1); set_prio_map(&tlvs->ets->cfgr->prio_map, ((2*i)+1), temp2); offset += 1; } for (i = 0; i < MAX_TCS; i++) { tlvs->ets->cfgr->tc_bw[i] = tlvs->rx->etscfg->info[offset]; offset += 1; } for (i = 0; i < MAX_TCS; i++) { tlvs->ets->cfgr->tsa_map[i] = tlvs->rx->etscfg->info[offset]; offset += 1; } } static void process_ieee8021qaz_etsrec_tlv(struct port *port) { struct ieee8021qaz_tlvs *tlvs; u8 offset = 0; int i = 0; tlvs = ieee8021qaz_data(port->ifname); /* Bypassing OUI, SUBTYPE fields */ offset = OUI_SUB_SIZE + 1; if (tlvs->ets->recr) memset(tlvs->ets->recr, 0, sizeof(*tlvs->ets->recr)); else tlvs->ets->recr = malloc(sizeof(*tlvs->ets->recr)); if (!tlvs->ets->recr) return; tlvs->ets->recr->prio_map = 0; for (i = 0; i < 4; i++) { u8 temp1 = 0, temp2 = 0; temp1 = (tlvs->rx->etsrec->info[offset] >> 4) & 0x0F; temp2 = tlvs->rx->etsrec->info[offset] & 0x0F; set_prio_map(&tlvs->ets->recr->prio_map, (2*i), temp1); set_prio_map(&tlvs->ets->recr->prio_map, ((2*i)+1), temp2); offset += 1; } for (i = 0; i < MAX_TCS; i++) { tlvs->ets->recr->tc_bw[i] = tlvs->rx->etsrec->info[offset]; offset += 1; } for (i = 0; i < MAX_TCS; i++) { tlvs->ets->recr->tsa_map[i] = tlvs->rx->etsrec->info[offset]; offset += 1; } } static void process_ieee8021qaz_pfc_tlv(struct port *port) { struct ieee8021qaz_tlvs *tlvs; u8 offset = 0; tlvs = ieee8021qaz_data(port->ifname); /* Bypassing OUI, SUBTYPE fields */ offset = OUI_SUB_SIZE; if (tlvs->rx->pfc->info[offset] & BIT7) tlvs->pfc->remote.willing = true; else tlvs->pfc->remote.willing = false; if (tlvs->rx->pfc->info[offset] & BIT6) tlvs->pfc->remote.mbc = true; else tlvs->pfc->remote.mbc = false; tlvs->pfc->remote.pfc_cap = tlvs->rx->pfc->info[offset] & 0x0F; offset += 1; tlvs->pfc->remote.pfc_enable = tlvs->rx->pfc->info[offset]; tlvs->pfc->remote_param = true; } int ieee8021qaz_mod_app(struct app_tlv_head *head, int peer, u8 prio, u8 sel, u16 proto, u32 ops) { struct app_obj *np; /* Search list for existing match and abort * Mark entry for deletion if delete option supplied */ LIST_FOREACH(np, head, entry) { if (np->app.selector == sel && np->app.protocol == proto && np->app.priority == prio) { if (ops & op_delete) np->hw = IEEE_APP_DEL; return 1; } } if (ops & op_delete) return 1; /* Add new entry for APP data */ np = calloc(1, sizeof(*np)); if (!np) { LLDPAD_WARN("%s: memory alloc failure.\n", __func__); return -1; } np->peer = peer; np->hw = IEEE_APP_SET; np->app.priority = prio; np->app.selector = sel; np->app.protocol = proto; LIST_INSERT_HEAD(head, np, entry); return 0; } static void ieee8021qaz_app_reset(struct app_tlv_head *head) { struct app_obj *np; LIST_FOREACH(np, head, entry) { if (np->peer) np->hw = IEEE_APP_DEL; } } static int __ieee8021qaz_app_sethw(char *ifname, struct app_tlv_head *head) { struct app_obj *np, *np_tmp; int set = 0; LIST_FOREACH(np, head, entry) { if (np->hw != IEEE_APP_SET) continue; set = set_ieee_hw(ifname, NULL, NULL, &np->app); np->hw = IEEE_APP_DONE; } np = LIST_FIRST(head); while (np) { if (np->hw == IEEE_APP_DEL) { np_tmp = np; np = LIST_NEXT(np, entry); LIST_REMOVE(np_tmp, entry); set = del_ieee_hw(ifname, &np_tmp->app); free(np_tmp); } else { np = LIST_NEXT(np, entry); } } return set; } int ieee8021qaz_app_sethw(char *ifname, struct app_tlv_head *head) { if (ieee8021qaz_check_active(ifname)) { set_dcbx_mode(ifname, DCB_CAP_DCBX_VER_IEEE | DCB_CAP_DCBX_HOST); return __ieee8021qaz_app_sethw(ifname, head); } return 0; } static void process_ieee8021qaz_app_tlv(struct port *port) { struct ieee8021qaz_tlvs *tlvs; int offset = OUI_SUB_SIZE + 1; tlvs = ieee8021qaz_data(port->ifname); /* clear app priorities so old data is flushed */ ieee8021qaz_app_reset(&tlvs->app_head); while (offset < tlvs->rx->app->length) { struct app_obj *np; int set = 0; u8 prio = (tlvs->rx->app->info[offset] & 0xE0) >> 5; u8 sel = (tlvs->rx->app->info[offset] & 0x07); u16 proto = (tlvs->rx->app->info[offset + 1] << 8) | tlvs->rx->app->info[offset + 2]; /* Search list for existing match and mark set */ LIST_FOREACH(np, &tlvs->app_head, entry) { if (np->app.selector == sel && np->app.protocol == proto && np->app.priority == prio) { np->hw = IEEE_APP_SET; set = 1; break; } } /* If APP data not found in LIST add APP entry */ if (!set) ieee8021qaz_mod_app(&tlvs->app_head, 1, prio, sel, proto, 0); offset += 3; } } static void ieee8021qaz_mibUpdateObjects(struct port *port) { struct ieee8021qaz_tlvs *tlvs; tlvs = ieee8021qaz_data(port->ifname); if (tlvs->rx->etscfg) { process_ieee8021qaz_etscfg_tlv(port); } else if (tlvs->ets->cfgr) { free(tlvs->ets->cfgr); tlvs->ets->cfgr = NULL; } if (tlvs->rx->etsrec) { process_ieee8021qaz_etsrec_tlv(port); } else if (tlvs->ets->recr) { free(tlvs->ets->recr); tlvs->ets->recr = NULL; } if (tlvs->rx->pfc) process_ieee8021qaz_pfc_tlv(port); else if (tlvs->pfc) tlvs->pfc->remote_param = false; if (tlvs->rx->app) process_ieee8021qaz_app_tlv(port); else ieee8021qaz_app_reset(&tlvs->app_head); } /* * LLDP_8021_QAZ_MOD_OPS - RCHANGE * * TLVs not consumed on error otherwise it is either free'd or stored * internally in the module. */ int ieee8021qaz_rchange(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { u8 oui[OUI_SIZE] = INIT_IEEE8021QAZ_OUI; struct ieee8021qaz_tlvs *qaz_tlvs; struct ieee8021qaz_unpkd_tlvs *rx; if (agent->type != NEAREST_BRIDGE) return 0; qaz_tlvs = ieee8021qaz_data(port->ifname); if (!qaz_tlvs) return SUBTYPE_INVALID; /* * TYPE_1 mandatory and always before IEEE8021QAZ tlvs so * we can use it to make the beginning of a IEEE8021QAZ PDU. * Verifies that only a single IEEE8021QAZ tlv is present. */ if (tlv->type == TYPE_1) { clear_ieee8021qaz_rx(qaz_tlvs); rx = malloc(sizeof(*rx)); memset(rx, 0, sizeof(*rx)); qaz_tlvs->rx = rx; qaz_tlvs->ieee8021qazdu = 0; } /* * TYPE_127 is for the Organizationally Specific TLVS * More than 1 of this type is allowed. */ if (tlv->type == TYPE_127) { if (tlv->length < (OUI_SUB_SIZE)) return TLV_ERR; if ((memcmp(tlv->info, &oui, OUI_SIZE) != 0)) return SUBTYPE_INVALID; l2_packet_get_remote_addr(port->l2, qaz_tlvs->remote_mac); if (unpack_ieee8021qaz_tlvs(port, agent, tlv)) return TLV_OK; } if (tlv->type == TYPE_0) { if (qaz_tlvs->active && dcbx_tlvs_rxed(qaz_tlvs->ifname, agent) && !qaz_tlvs->ieee8021qazdu) { qaz_tlvs->active = false; LLDPAD_INFO("IEEE DCBX on %s going INACTIVE\n", qaz_tlvs->ifname); } if (qaz_tlvs->active) { /* If peer is DCBX, then go into RXTX mode * if current configuration is RXOnly and * not persistant (i.e. default) */ int adminstatus; if (qaz_tlvs->ieee8021qazdu && get_config_setting(qaz_tlvs->ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT) && get_lldp_agent_admin(qaz_tlvs->ifname, agent->type) == enabledRxOnly) { adminstatus = enabledRxTx; if (set_config_setting(qaz_tlvs->ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT) == cmd_success) set_lldp_agent_admin(qaz_tlvs->ifname, agent->type, adminstatus); } if (qaz_tlvs->ieee8021qazdu) qaz_tlvs->pending = false; /* Update TLV State Machines */ ieee8021qaz_mibUpdateObjects(port); run_all_sm(port, agent); clear_ieee8021qaz_rx(qaz_tlvs); somethingChangedLocal(port->ifname, agent->type); } } return TLV_OK; } static void ieee8021qaz_free_rx(struct ieee8021qaz_unpkd_tlvs *rx) { if (!rx) return; if (rx->etscfg) rx->etscfg = free_unpkd_tlv(rx->etscfg); if (rx->etsrec) rx->etsrec = free_unpkd_tlv(rx->etsrec); if (rx->pfc) rx->pfc = free_unpkd_tlv(rx->pfc); if (rx->app) rx->app = free_unpkd_tlv(rx->app); return; } /* * LLDP_8021QAZ_MOD_OPS - MIBDELETEOBJECT * * ieee8021qaz_mibDeleteObject - deletes MIBs * Check if peer has ETS enabled * - If yes, check if ETS TLV is present * - If yes, set it as absent (delete it?) * Same for PFC and APP. */ u8 ieee8021qaz_mibDeleteObject(struct port *port, struct lldp_agent *agent) { struct ieee8021qaz_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) return 0; tlvs = ieee8021qaz_data(port->ifname); if (!tlvs) return 0; ieee8021qaz_free_rx(tlvs->rx); /* Reseting ETS Remote params */ if (tlvs->ets) { if (tlvs->ets->recr) { free(tlvs->ets->recr); tlvs->ets->recr = NULL; } if (tlvs->ets->cfgr) { free(tlvs->ets->cfgr); tlvs->ets->cfgr = NULL; } } /* Reseting PFC Remote params */ tlvs->pfc->remote_param = 0; tlvs->pfc->remote.willing = NULL; tlvs->pfc->remote.mbc = NULL; tlvs->pfc->remote.pfc_cap = 0; tlvs->pfc->remote.pfc_enable = 0; /* Clear peer Application data */ ieee8021qaz_app_reset(&tlvs->app_head); /* Kick Tx State Machine */ somethingChangedLocal(port->ifname, agent->type); return 0; } static struct ets_attrib *free_ets_tlv(struct ets_attrib *ets) { if (ets) { free(ets->cfgl); free(ets->recl); free(ets->cfgr); free(ets->recr); free(ets); ets = NULL; } return NULL; } static struct pfc_attrib *free_pfc_tlv(struct pfc_attrib *pfc) { if (pfc) { free(pfc); pfc = NULL; } return NULL; } static void ieee8021qaz_free_tlv(struct ieee8021qaz_tlvs *tlvs) { struct app_obj *np; if (!tlvs) return; if (tlvs->ets) tlvs->ets = free_ets_tlv(tlvs->ets); if (tlvs->pfc) tlvs->pfc = free_pfc_tlv(tlvs->pfc); /* Remove _all_ existing application data */ LIST_FOREACH(np, &tlvs->app_head, entry) np->hw = IEEE_APP_DEL; __ieee8021qaz_app_sethw(tlvs->ifname, &tlvs->app_head); return; } static void ieee8021qaz_free_data(struct ieee8021qaz_user_data *iud) { struct ieee8021qaz_tlvs *id; if (iud) { while (!LIST_EMPTY(&iud->head)) { id = LIST_FIRST(&iud->head); LIST_REMOVE(id, entry); ieee8021qaz_free_tlv(id); ieee8021qaz_free_rx(id->rx); free(id->rx); free(id); } } } /* LLDP_8021QAZ_MOD_OPS - UNREGISTER */ void ieee8021qaz_unregister(struct lldp_module *mod) { if (mod->data) { ieee8021qaz_free_data(mod->data); free(mod->data); } free(mod); } /* * LLDP_8021QAZ_MOD_OPS - IFDOWN */ void ieee8021qaz_ifdown(char *device_name, struct lldp_agent *agent) { struct port *port = NULL; struct ieee8021qaz_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) return; port = porthead; while (port != NULL) { if (!strncmp(device_name, port->ifname, MAX_DEVICE_NAME_LEN)) break; port = port->next; } tlvs = ieee8021qaz_data(device_name); if (!tlvs) return; if (tlvs) { ieee8021qaz_free_rx(tlvs->rx); free(tlvs->rx); tlvs->rx = NULL; } } /* * LLDP_8021QAZ_MOD_OPS - TLVS_RXED */ int ieee8021qaz_tlvs_rxed(const char *ifname) { struct ieee8021qaz_user_data *iud; struct ieee8021qaz_tlvs *tlv = NULL; iud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8021QAZ); if (iud) { LIST_FOREACH(tlv, &iud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return !!tlv->ieee8021qazdu; } } return 0; } /* * LLDP_8021QAZ_MOD_OPS - CHECK_ACTIVE */ int ieee8021qaz_check_active(const char *ifname) { struct ieee8021qaz_user_data *iud; struct ieee8021qaz_tlvs *tlv = NULL; iud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8021QAZ); if (iud) { LIST_FOREACH(tlv, &iud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return tlv->active && !tlv->pending; } } return 0; } lldpad-0.9.46/lldp_8021qaz_clif.c000066400000000000000000000170101215142747300163410ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldp.h" #include "lldp_mod.h" #include "clif_msgs.h" #include "lldp_8021qaz.h" #include "lldp_8021qaz_clif.h" static void ieee8021qaz_print_etscfg_tlv(u16 len, char *info); static void ieee8021qaz_print_etsrec_tlv(u16 len, char *info); static void ieee8021qaz_print_pfc_tlv(u16 len, char *info); static void ieee8021qaz_print_app_tlv(u16 len, char *info); static u32 ieee8021qaz_lookup_tlv_name(char *tlvid_str); static int ieee8021qaz_print_help(void); static const struct lldp_mod_ops ieee8021qaz_ops_clif = { .lldp_mod_register = ieee8021qaz_cli_register, .lldp_mod_unregister = ieee8021qaz_cli_unregister, .print_tlv = ieee8021qaz_print_tlv, .lookup_tlv_name = ieee8021qaz_lookup_tlv_name, .print_help = ieee8021qaz_print_help, }; struct type_name_info ieee8021qaz_tlv_names[] = { { .type = (OUI_IEEE_8021 << 8), .name = "IEEE-DCBX Settings", .key = "IEEE-DCBX", }, { .type = (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG, .name = "IEEE 8021QAZ ETS Configuration TLV", .key = "ETS-CFG", .print_info = ieee8021qaz_print_etscfg_tlv, }, { .type = (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC, .name = "IEEE 8021QAZ ETS Recommendation TLV", .key = "ETS-REC", .print_info = ieee8021qaz_print_etsrec_tlv, }, { .type = (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC, .name = "IEEE 8021QAZ PFC TLV", .key = "PFC", .print_info = ieee8021qaz_print_pfc_tlv, }, { .type = (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_APP, .name = "IEEE 8021QAZ APP TLV", .key = "APP", .print_info = ieee8021qaz_print_app_tlv, }, { .type = INVALID_TLVID, } }; static int ieee8021qaz_print_help(void) { struct type_name_info *tn = &ieee8021qaz_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *ieee8021qaz_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) return NULL; mod->id = LLDP_MOD_8021QAZ; mod->ops = &ieee8021qaz_ops_clif; return mod; } void ieee8021qaz_cli_unregister(struct lldp_module *mod) { free(mod); } static void ieee8021qaz_print_etscfg_tlv(UNUSED u16 len, char *info) { u8 wc_maxtc; u8 tc_bw[8], tsa_map[8]; u32 prio_map; int offset = 0, i = 0; hexstr2bin(info, (u8 *)&wc_maxtc, 1); printf(" Willing: %s\n", (wc_maxtc & 0x80) ? "yes" : "no"); printf("\t CBS: %s\n", (wc_maxtc & 0x40) ? "supported" : "not supported"); if (!(wc_maxtc & 0x7)) printf("\t MAX_TCS: 8\n"); else printf("\t MAX_TCS: %i\n", wc_maxtc & 0x7); offset += 2; hexstr2bin(info + offset, (u8 *)&prio_map, 4); prio_map = ntohl(prio_map); printf("\t PRIO_MAP: "); for (i = 0; i < 8; i++) printf("%i:%i ", i, (prio_map >> ((7-i)*4)) & 0xf); printf("\n"); offset += 8; hexstr2bin(info + offset, tc_bw, 8); printf("\t TC Bandwidth: "); for (i = 0; i < MAX_TCS; i++) printf("%i%% ", tc_bw[i]); printf("\n"); offset += 16; hexstr2bin(info + offset, (u8 *)tsa_map, 8); printf("\t TSA_MAP: "); for (i = 0; i < MAX_TCS; i++) { printf("%i:", i); switch (tsa_map[i]) { case IEEE8021Q_TSA_STRICT: printf("strict "); break; case IEEE8021Q_TSA_CBSHAPER: printf("cbshaper "); break; case IEEE8021Q_TSA_ETS: printf("ets "); break; case IEEE8021Q_TSA_VENDOR: printf("vendor "); break; default: printf("unknown "); break; } } printf("\n"); } static void ieee8021qaz_print_etsrec_tlv(UNUSED u16 len, char *info) { u8 offset = 0; u32 prio_map; u8 tc_bw[8], tsa_map[8]; int i = 0; /* advanced past initial 8bit reserved field */ offset += 2; hexstr2bin(info + offset, (u8 *)&prio_map, 4); prio_map = ntohl(prio_map); printf(" PRIO_MAP: "); for (i = 0; i < 8; i++) printf("%i:%i ", i, (prio_map >> ((7-i)*4)) & 0xf); printf("\n"); offset += 8; hexstr2bin(info + offset, tc_bw, 8); printf("\t TC Bandwidth: "); for (i = 0; i < MAX_TCS; i++) printf("%i%% ", tc_bw[i]); printf("\n"); offset += 16; hexstr2bin(info + offset, (u8 *)tsa_map, 8); printf("\t TSA_MAP: "); for (i = 0; i < MAX_TCS; i++) { printf("%i:", i); switch (tsa_map[i]) { case IEEE8021Q_TSA_STRICT: printf("strict "); break; case IEEE8021Q_TSA_CBSHAPER: printf("cbshaper "); break; case IEEE8021Q_TSA_ETS: printf("ets "); break; case IEEE8021Q_TSA_VENDOR: printf("vendor "); break; default: printf("unknown "); break; } } printf("\n"); } static void ieee8021qaz_print_pfc_tlv(UNUSED u16 len, char *info) { int i, offset = 0; u8 w_mbc_cap, pfc_enable; u8 found = 0; hexstr2bin(info + offset, (u8 *)&w_mbc_cap, 1); printf(" Willing: %s\n", (w_mbc_cap & 0x80) ? "yes" : "no"); printf("\t MACsec Bypass Capable: %s\n", (w_mbc_cap & 0x40) ? "yes" : "no"); printf("\t PFC capable traffic classes: %i\n", w_mbc_cap & 0x0f); offset += 2; printf("\t PFC enabled: "); hexstr2bin(info + offset, (u8 *)&pfc_enable, 1); for (i = 0; i < 8; i++) { if ((pfc_enable >> i) & 1) { found = 1; printf("%i ", i); } } if (!found) printf("none"); printf("\n"); } static void ieee8021qaz_print_app_tlv(u16 len, char *info) { u8 offset = 2; u8 app; u16 proto; while (offset < len*2) { hexstr2bin(info + offset, &app, 1); hexstr2bin(info + offset + 2, (u8 *)&proto, 2); if (offset > 6) printf("\t"); printf("App#%i:\n", offset/6); printf("\t Priority: %i\n", (app & 0xE0) >> 5); printf("\t Sel: %i\n", app & 0x07); switch (app & 0x07) { case 1: printf("\t Ethertype: 0x%04x\n", ntohs(proto)); break; case 2: printf("\t {S}TCP Port: %i\n", ntohs(proto)); break; case 3: printf("\t UDP or DCCP Port: %i\n", ntohs(proto)); break; case 4: printf("\t TCP/STCP/UDP/DCCP Port: %i\n", ntohs(proto)); break; default: printf("\t Reserved Port: %i\n", ntohs(proto)); break; } printf("\n"); offset += 6; } } int ieee8021qaz_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &ieee8021qaz_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n\t", tn->name); if (tn->print_info) tn->print_info(len - 4, info); return 1; } tn++; } return 0; } static u32 ieee8021qaz_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &ieee8021qaz_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_8021qaz_cmds.c000066400000000000000000001100511215142747300163510ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include "lldp.h" #include "lldpad.h" #include "lldp_mand_clif.h" #include "lldp_8021qaz_cmds.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "config.h" #include "lldp/ports.h" #include "lldp/states.h" #include "lldp_8021qaz.h" #include "lldp_rtnl.h" #include "lldpad_shm.h" #include "messages.h" #include "lldp_util.h" static int get_arg_dcbx_mode(struct cmd *, char *, char *, char *, int); static int set_arg_dcbx_mode(struct cmd *, char *, char *, char *, int); static int test_arg_dcbx_mode(struct cmd *, char *, char *, char *, int); static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int test_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int get_arg_willing(struct cmd *, char *, char *, char *, int); static int set_arg_willing(struct cmd *, char *, char *, char *, int); static int test_arg_willing(struct cmd *, char *, char *, char *, int); static int get_arg_numtc(struct cmd *, char *, char *, char *, int); static int test_arg_numtc(struct cmd *, char *, char *, char *, int); static int get_arg_up2tc(struct cmd *, char *, char *, char *, int); static int set_arg_up2tc(struct cmd *, char *, char *, char *, int); static int test_arg_up2tc(struct cmd *, char *, char *, char *, int); static int get_arg_tcbw(struct cmd *, char *, char *, char *, int); static int set_arg_tcbw(struct cmd *, char *, char *, char *, int); static int test_arg_tcbw(struct cmd *, char *, char *, char *, int); static int get_arg_tsa(struct cmd *, char *, char *, char *, int); static int set_arg_tsa(struct cmd *, char *, char *, char *, int); static int test_arg_tsa(struct cmd *, char *, char *, char *, int); static int get_arg_enabled(struct cmd *, char *, char *, char *, int); static int set_arg_enabled(struct cmd *, char *, char *, char *, int); static int test_arg_enabled(struct cmd *, char *, char *, char *, int); static int get_arg_delay(struct cmd *, char *, char *, char *, int); static int set_arg_delay(struct cmd *, char *, char *, char *, int); static int test_arg_delay(struct cmd *, char *, char *, char *, int); static int get_arg_app(struct cmd *, char *, char *, char *, int); static int set_arg_app(struct cmd *, char *, char *, char *, int); static int test_arg_app(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_DCBX_MODE, .arg_class = TLV_ARG, .handle_get = get_arg_dcbx_mode, .handle_set = set_arg_dcbx_mode, .handle_test = test_arg_dcbx_mode, }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable, }, { .arg = ARG_WILLING, .arg_class = TLV_ARG, .handle_get = get_arg_willing, .handle_set = set_arg_willing, .handle_test = test_arg_willing, }, { .arg = ARG_ETS_NUMTCS, .arg_class = TLV_ARG, .handle_get = get_arg_numtc, /* no set */ .handle_test = test_arg_numtc, }, { .arg = ARG_ETS_UP2TC, .arg_class = TLV_ARG, .handle_get = get_arg_up2tc, .handle_set = set_arg_up2tc, .handle_test = test_arg_up2tc, }, { .arg = ARG_ETS_TCBW, .arg_class = TLV_ARG, .handle_get = get_arg_tcbw, .handle_set = set_arg_tcbw, .handle_test = test_arg_tcbw, }, { .arg = ARG_ETS_TSA, .arg_class = TLV_ARG, .handle_get = get_arg_tsa, .handle_set = set_arg_tsa, .handle_test = test_arg_tsa, }, { .arg = ARG_PFC_ENABLED, .arg_class = TLV_ARG, .handle_get = get_arg_enabled, .handle_set = set_arg_enabled, .handle_test = test_arg_enabled, }, { .arg = ARG_PFC_DELAY, .arg_class = TLV_ARG, .handle_get = get_arg_delay, .handle_set = set_arg_delay, .handle_test = test_arg_delay, }, { .arg = ARG_APP, .arg_class = TLV_ARG, .handle_get = get_arg_app, .handle_set = set_arg_app, .handle_test = test_arg_app, }, { .arg = 0 } }; static int get_arg_dcbx_mode(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, int obuf_len) { struct ieee8021qaz_tlvs *tlvs; char buf[250] = ""; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8): break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; switch (lldpad_shm_get_dcbx(cmd->ifname)) { case dcbx_subtype0: snprintf(buf, sizeof(buf), "auto"); break; case dcbx_subtype1: snprintf(buf, sizeof(buf), "CIN"); break; case dcbx_subtype2: snprintf(buf, sizeof(buf), "CEE"); break; default: snprintf(buf, sizeof(buf), "unknown"); break; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(buf), buf); return cmd_success; } static int set_arg_dcbx_mode(struct cmd *cmd, UNUSED char *args, char *arg_value, char *obuf, int obuf_len) { struct ieee8021qaz_tlvs *tlvs; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8): break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (strcmp(arg_value, "reset")) return cmd_invalid; tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; lldpad_shm_set_dcbx(cmd->ifname, dcbx_subtype0); snprintf(obuf, obuf_len, "mode = %s\n", arg_value); return cmd_success; } static int test_arg_dcbx_mode(UNUSED struct cmd *cmd, UNUSED char *args, UNUSED char *arg_value, UNUSED char *obuf, UNUSED int obuf_len) { return cmd_success; } static int get_arg_willing(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, int obuf_len) { int willing = 0; struct ieee8021qaz_tlvs *tlvs; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: willing = tlvs->ets->cfgl->willing; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: willing = tlvs->pfc->local.willing; break; } if (willing) snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(VAL_YES), VAL_YES); else snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(VAL_NO), VAL_NO); return cmd_success; } static int _set_arg_willing(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len, bool test) { int willing; struct ieee8021qaz_tlvs *tlvs; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; /* To remain backward compatible and make it easier * for everyone use to {0|1} notation we still support * this but also support english variants as well */ if (!strcasecmp(arg_value, VAL_YES)) willing = 1; else if (!strcasecmp(arg_value, VAL_NO)) willing = 0; else { char *end; errno = 0; willing = strtol(arg_value, &end, 10); if (end == arg_value || *end != '\0') return cmd_invalid; if (errno || willing < 0) return cmd_invalid; } switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (test) return cmd_success; tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs->ets->cfgl->willing = !!willing; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: tlvs->pfc->local.willing = !!willing; break; } snprintf(obuf, obuf_len, "willing = %s\n", !!willing ? VAL_YES : VAL_NO); snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &willing, CONFIG_TYPE_INT); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_willing(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_willing(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_willing(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_willing(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_numtc(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, int obuf_len) { struct ieee8021qaz_tlvs *tlvs; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: return cmd_not_applicable; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; snprintf(obuf, obuf_len, "%02x%s%04x%i", (unsigned int) strlen(args), args, 1, tlvs->ets->cfgl->max_tcs); return cmd_success; } static int test_arg_numtc(UNUSED struct cmd *cmd, UNUSED char *args, UNUSED char *arg_value, UNUSED char *obuf, UNUSED int obuf_len) { return cmd_invalid; } static int get_arg_up2tc(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, UNUSED int obuf_len) { struct ieee8021qaz_tlvs *tlvs; char buf[250] = ""; u32 *pmap = NULL; int i; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: pmap = &tlvs->ets->cfgl->prio_map; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: pmap = &tlvs->ets->recl->prio_map; break; } for (i = 0; i < 8; i++) { char cat[5]; if (i) snprintf(cat, sizeof(cat), ",%i:%i", i, get_prio_map(*pmap, i)); else snprintf(cat, sizeof(cat), "%i:%i", i, get_prio_map(*pmap, i)); strncat(buf, cat, sizeof(buf) - strlen(buf) - 1); } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(buf), buf); return cmd_success; } static int _set_arg_up2tc(struct cmd *cmd, char *args, const char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char arg_path[256]; char *toked_maps, *parse; u32 *pmap; u32 save_pmap; u8 max; int i, err = cmd_success; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; pmap = &tlvs->ets->cfgl->prio_map; max = tlvs->ets->cfgl->max_tcs; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; pmap = &tlvs->ets->recl->prio_map; max = MAX_TCS; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } save_pmap = *pmap; parse = strdup(arg_value); if (!parse) return cmd_failed; /* Parse comma seperated string ex: "1:ets,2:strict,0:vendor" */ if (strcmp(parse, "none")) { toked_maps = strtok(parse, ","); while (toked_maps) { char *end; int tc, prio; u32 mask; errno = 0; prio = strtol(toked_maps, &end, 10); if (*end != ':') { snprintf(obuf, obuf_len - 1, ": error: %s", toked_maps); err = cmd_invalid; goto invalid; } if (errno || prio < 0) { snprintf(obuf, obuf_len - 1, ": error: negative prio(%i)", prio); err = cmd_invalid; goto invalid; } if (prio > 7) { snprintf(obuf, obuf_len - 1, ": error: prio(%i) > 7", prio); err = cmd_invalid; goto invalid; } errno = 0; tc = strtol(&toked_maps[2], &end, 10); if (end == &toked_maps[2] || *end != '\0') { snprintf(obuf, obuf_len - 1, ": error: %s", toked_maps); err = cmd_invalid; goto invalid; } if (errno || tc < 0) { snprintf(obuf, obuf_len - 1, ": error: negative tc(%i)", tc); err = cmd_invalid; goto invalid; } if (tc > (max - 1)) { snprintf(obuf, obuf_len - 1, ": error: tc(%i) > max tc(%i)", tc, max - 1); err = cmd_invalid; goto invalid; } mask = ~(0xffffffff & (0xF << (4 * (7-prio)))); *pmap &= mask; *pmap |= tc << (4 * (7-prio)); toked_maps = strtok(NULL, ","); } } else { *pmap = 0; } if (test) { *pmap = save_pmap; free(parse); return cmd_success; } /* Build output buffer */ strncat(obuf, "up2tc = ", obuf_len - strlen(obuf) - 1); for (i = 0; i < 8; i++) { char cat[5]; snprintf(cat, sizeof(cat), "%i:%i ", i, get_prio_map(*pmap, i)); strncat(obuf, cat, obuf_len - strlen(obuf) - 1); } strncat(obuf, "\n", obuf_len - strlen(obuf) - 1); /* Update configuration file with new attribute */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &arg_value, CONFIG_TYPE_STRING); somethingChangedLocal(cmd->ifname, cmd->type); invalid: free(parse); return err; } static int set_arg_up2tc(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_up2tc(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_up2tc(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_up2tc(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_tcbw(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, UNUSED int obuf_len) { struct ieee8021qaz_tlvs *tlvs; char buf[250] = ""; int i; u8 *bmap; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; bmap = tlvs->ets->cfgl->tc_bw; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; bmap = tlvs->ets->recl->tc_bw; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } for (i = 0; i < 8; i++) { char cat[6]; if (i) snprintf(cat, sizeof(cat), ",%i", bmap[i]); else snprintf(cat, sizeof(cat), "%i", bmap[i]); strncat(buf, cat, sizeof(buf) - strlen(buf) - 1); } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(buf), buf); return cmd_success; } static int _set_arg_tcbw(struct cmd *cmd, char *args, const char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char arg_path[256]; char *toked_bw, *parse; int i, err = cmd_success; u8 *tcbw, percent[8] = {0}, total = 0; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tcbw = tlvs->ets->cfgl->tc_bw; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tcbw = tlvs->ets->recl->tc_bw; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } parse = strdup(arg_value); if (!parse) return cmd_failed; /* Parse comma seperated string ex: "1:ets,2:strict,0:vendor" */ toked_bw = strtok(parse, ","); for (i = 0; i < 8 && toked_bw; i++) { percent[i] = atoi(toked_bw); toked_bw = strtok(NULL, ","); } for (i = 0; i < 8; i++) total += percent[i]; if (total != 100) { err = cmd_invalid; goto invalid; } else if (test) { free(parse); return cmd_success; } else { memcpy(tcbw, percent, sizeof(*tcbw) * MAX_TCS); } strncat(obuf, "tcbw = ", obuf_len - strlen(obuf) - 1); for (i = 0; i < 8; i++) { char cat[5]; snprintf(cat, sizeof(cat), "%i%% ", percent[i]); printf("%i%% ", percent[i]); strncat(obuf, cat, obuf_len - strlen(obuf) - 1); } strncat(obuf, "\n", obuf_len - strlen(obuf) - 1); snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &arg_value, CONFIG_TYPE_STRING); somethingChangedLocal(cmd->ifname, cmd->type); invalid: free(parse); return err; } static int set_arg_tcbw(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_tcbw(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_tcbw(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_tcbw(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_tsa(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, UNUSED int obuf_len) { struct ieee8021qaz_tlvs *tlvs; char buf[250] = ""; int i; u8 *tsa; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tsa = tlvs->ets->cfgl->tsa_map; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tsa = tlvs->ets->recl->tsa_map; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } for (i = 0; i < 8; i++) { char cnt[4]; int space_left; if (i) snprintf(cnt, sizeof(cnt), ",%i:", i); else snprintf(cnt, sizeof(cnt), "%i:", i); strncat(buf, cnt, sizeof(buf) - strlen(buf) - 1); space_left = sizeof(buf) - strlen(buf) - 1; switch (tsa[i]) { case IEEE8021Q_TSA_STRICT: strncat(buf, "strict", space_left); break; case IEEE8021Q_TSA_CBSHAPER: strncat(buf, "cb_shaper", space_left); break; case IEEE8021Q_TSA_ETS: strncat(buf, "ets", space_left); break; case IEEE8021Q_TSA_VENDOR: strncat(buf, "vendor", space_left); break; default: strncat(buf, "unknown", space_left); break; } } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(buf), buf); return cmd_success; } static int _set_arg_tsa(struct cmd *cmd, char *args, const char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char arg_path[256]; char *toked_maps, *parse; int i, err = cmd_success; u8 *tsa; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tsa = tlvs->ets->cfgl->tsa_map; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; tsa = tlvs->ets->recl->tsa_map; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } parse = strdup(arg_value); if (!parse) return cmd_failed; /* Parse comma seperated string ex: "1:ets,2:strict,0:vendor" */ if (strcmp(parse, "none")) { toked_maps = strtok(parse, ","); while (toked_maps) { int tc, type; if (toked_maps[1] != ':') { err = cmd_invalid; goto invalid; } tc = atoi(toked_maps); if (tc > 7) { err = cmd_invalid; goto invalid; } if ((strcmp(&toked_maps[2], "strict")) == 0) type = IEEE8021Q_TSA_STRICT; else if ((strcmp(&toked_maps[2], "cb_shaper")) == 0) type = IEEE8021Q_TSA_CBSHAPER; else if ((strcmp(&toked_maps[2], "ets")) == 0) type = IEEE8021Q_TSA_ETS; else if ((strcmp(&toked_maps[2], "vendor")) == 0) type = IEEE8021Q_TSA_VENDOR; else { err = cmd_invalid; goto invalid; } if (!test) tsa[tc] = type; toked_maps = strtok(NULL, ","); } } else if (!test) { memset(tsa, 0, MAX_TCS); } if (test) { free(parse); return cmd_success; } strncat(obuf, "TSA = ", obuf_len - strlen(obuf) - 1); for (i = 0; i < 8; i++) { char cnt[3]; int space_left; snprintf(cnt, sizeof(cnt), "%i:", i); strncat(obuf, cnt, obuf_len - strlen(obuf) - 1); space_left = obuf_len - strlen(obuf) - 1; switch (tsa[i]) { case IEEE8021Q_TSA_STRICT: strncat(obuf, "strict ", space_left); break; case IEEE8021Q_TSA_CBSHAPER: strncat(obuf, "cb_shaper ", space_left); break; case IEEE8021Q_TSA_ETS: strncat(obuf, "ets ", space_left); break; case IEEE8021Q_TSA_VENDOR: strncat(obuf, "vendor ", space_left); break; default: strncat(obuf, "unknown ", space_left); break; } } strncat(obuf, "\n", obuf_len - strlen(obuf) - 1); snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &arg_value, CONFIG_TYPE_STRING); somethingChangedLocal(cmd->ifname, cmd->type); invalid: free(parse); return err; } static int set_arg_tsa(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_tsa(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_tsa(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_tsa(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_enabled(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, UNUSED int obuf_len) { struct ieee8021qaz_tlvs *tlvs; char buf[20] = ""; int i; bool first; u8 pfc; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; pfc = tlvs->pfc->local.pfc_enable; first = true; for (i = 0; i < 8; i++) { if (pfc & (1 << i)) { char val[3]; if (first) { snprintf(val, sizeof(val), "%i", i); first = false; } else { snprintf(val, sizeof(val), ",%i", i); } strncat(buf, val, sizeof(buf) - strlen(buf) - 1); } } if (first) strncpy(buf, "none", sizeof(buf)); snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(buf), buf); return cmd_success; } static int _set_arg_enabled(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char *priority, *parse; char arg_path[256]; int mask = 0; bool first; int i, err = cmd_success; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; parse = strdup(arg_value); if (!parse) return cmd_failed; /* Parse comma seperated string */ if (strcmp(arg_value, "none")) { priority = strtok(parse, ","); while (priority) { int prio; char *end; errno = 0; prio = strtol(priority, &end, 10); if (end == priority || *end != '\0') return cmd_invalid; if (errno || prio < 0) return cmd_invalid; if (prio > 7) { err = cmd_invalid; goto invalid; } mask |= 1 << prio; priority = strtok(NULL, ","); } } if (test) { free(parse); return cmd_success; } first = true; strncat(obuf, "prio = ", obuf_len - strlen(obuf) - 1); for (i = 0; i < 8; i++) { if (mask & (1 << i)) { char val[3]; if (first) { snprintf(val, sizeof(val), "%i", i); first = false; } else { snprintf(val, sizeof(val), ",%i", i); } strncat(obuf, val, obuf_len - strlen(obuf) - 1); } } strncat(obuf, "\n", obuf_len - strlen(obuf) - 1); /* Set configuration */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &mask, CONFIG_TYPE_INT); tlvs->pfc->local.pfc_enable = mask; somethingChangedLocal(cmd->ifname, cmd->type); invalid: free(parse); return err; } static int set_arg_enabled(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_enabled(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_enabled(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_enabled(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_delay(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, int obuf_len) { struct ieee8021qaz_tlvs *tlvs; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; snprintf(obuf, obuf_len, "%02x%s%04x%02x", (unsigned int) strlen(args), args, 2, tlvs->pfc->local.delay); return cmd_success; } static int _set_arg_delay(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char arg_path[256]; unsigned int delay = atoi(arg_value); if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; if (test) return cmd_success; tlvs->pfc->local.delay = delay; snprintf(obuf, obuf_len, "delay = %i\n", delay); /* Set configuration */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, args); set_config_setting(cmd->ifname, cmd->type, arg_path, &delay, CONFIG_TYPE_INT); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_delay(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_delay(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_delay(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_delay(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_app(struct cmd *cmd, char *args, UNUSED char *arg_value, char *obuf, int obuf_len) { struct ieee8021qaz_tlvs *tlvs; int i = 0; struct app_obj *np; char app_buf[2048] = "(prio,sel,proto)\n"; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_APP: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; LIST_FOREACH(np, &tlvs->app_head, entry) { char new_app[80]; char state[15]; struct dcb_app *dcb_app = &np->app; switch (np->hw) { case IEEE_APP_SET: strcpy(state, "pending set"); break; case IEEE_APP_DEL: strcpy(state, "pending delete"); break; case IEEE_APP_DONE: strcpy(state, "set"); break; default: strcpy(state, "unknown"); break; } if (dcb_app->selector == 1) { snprintf(new_app, sizeof(new_app), "%i:(%i,%i,0x%04x) %s (%s)\n", i, dcb_app->priority, dcb_app->selector, dcb_app->protocol, np->peer ? "peer" : "local", state); } else { snprintf(new_app, sizeof(new_app), "%i:(%i,%i,%i) %s hw (%s)\n", i, dcb_app->priority, dcb_app->selector, dcb_app->protocol, np->peer ? "peer" : "local", state); } strncat(app_buf, new_app, sizeof(app_buf) - strlen(app_buf) - 2); i++; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(args), args, (unsigned int) strlen(app_buf), app_buf); return cmd_success; } static int _set_arg_app(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len, bool test) { struct ieee8021qaz_tlvs *tlvs; char *app_tuple, *parse, *end; char arg_path[256]; char arg_parent[256]; char arg_name[256]; char new_argval[16]; const char *pp = &new_argval[0]; int prio, sel; long pid; struct app_obj *np; int i, res; int unused; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_APP: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } tlvs = ieee8021qaz_data(cmd->ifname); if (!tlvs) return cmd_device_not_found; parse = strdup(arg_value); if (!parse) return cmd_failed; app_tuple = strtok(parse, ","); if (!app_tuple) goto err; prio = atoi(app_tuple); app_tuple = strtok(NULL, ","); if (!app_tuple) goto err; sel = atoi(app_tuple); app_tuple = strtok(NULL, ","); if (!app_tuple) goto err; errno = 0; pid = strtol(app_tuple, &end, 0); /* Verify input is valid hex or integer */ if (errno) goto err; /* Verify input does not contain extra input */ if (end == app_tuple || *end != '\0') goto err; /* Verify priority and selector within valid IEEE range */ if (prio < 0 || prio > 7) { strncat(obuf, ": priority out of range", obuf_len - strlen(obuf) - 2); goto err; } if (sel < 1 || sel > 4) { strncat(obuf, ": selector out of range", obuf_len - strlen(obuf) - 2); goto err; } if (pid > 65535 || pid < 0) { strncat(obuf, ": pid out of range", obuf_len - strlen(obuf) - 2); goto err; } if (sel == 1 && (pid < 1536 && pid != 0)) { strncat(obuf, ": Ethertype < 1536", obuf_len - strlen(obuf) - 2); goto err; } free(parse); if (test) return cmd_success; snprintf(new_argval, sizeof(new_argval), "%1u,%1u,%5u", (u8) prio, (u8) sel, (u16)pid); /* Scan APP entries in config file */ unused = -1; for (i = 0; i < MAX_APP_ENTRIES; i++) { const char *dummy = NULL; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s%i", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_APP), ARG_APP, i); res = get_config_setting(cmd->ifname, cmd->type, arg_path, &dummy, CONFIG_TYPE_STRING); if (res) { if (unused < 0) unused = i; continue; } /* found an existing entry */ if (strcmp(dummy, new_argval) == 0) { if (cmd->ops & op_delete) { unused = 1; snprintf(arg_parent, sizeof(arg_parent), "%s%08x", TLVID_PREFIX, TLVID_8021(LLDP_8021QAZ_APP)); snprintf(arg_name, sizeof(arg_name), "%s%i", ARG_APP, i); res = remove_config_setting(cmd->ifname, cmd->type, arg_parent, arg_name); } } } if (unused < 0) return cmd_failed; /* Build app noting we verified prio, sel, and pid inputs */ ieee8021qaz_mod_app(&tlvs->app_head, 0, (u8) prio, (u8) sel, (u16) pid, (cmd->ops & op_delete) ? op_delete : 0); ieee8021qaz_app_sethw(cmd->ifname, &tlvs->app_head); i = 0; LIST_FOREACH(np, &tlvs->app_head, entry) { char new_app[80]; char state[15]; struct dcb_app *dcb_app = &np->app; switch (np->hw) { case IEEE_APP_SET: strcpy(state, "pending set"); break; case IEEE_APP_DEL: strcpy(state, "pending delete"); break; case IEEE_APP_DONE: strcpy(state, "set"); break; default: strcpy(state, "unknown"); break; } if (dcb_app->selector == 1) { snprintf(new_app, sizeof(new_app), "%i:(%i,%i,0x%04x) %s (%s)\n", i, dcb_app->priority, dcb_app->selector, dcb_app->protocol, np->peer ? "peer" : "local", state); } else { snprintf(new_app, sizeof(new_app), "%i:(%i,%i,%i) %s (%s)\n", i, dcb_app->priority, dcb_app->selector, dcb_app->protocol, np->peer ? "peer" : "local", state); } strncat(obuf, new_app, obuf_len - strlen(obuf) - 2); i++; } somethingChangedLocal(cmd->ifname, cmd->type); if (cmd->ops & op_delete) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s%i", TLVID_PREFIX, cmd->tlvid, args, unused); set_config_setting(cmd->ifname, cmd->type, arg_path, &pp, CONFIG_TYPE_STRING); return cmd_success; err: free(parse); return cmd_invalid; } static int set_arg_app(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_app(cmd, args, arg_value, obuf, obuf_len, false); } static int test_arg_app(struct cmd *cmd, char *args, char *arg_value, char *obuf, int obuf_len) { return _set_arg_app(cmd, args, arg_value, obuf, obuf_len, true); } static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (!is_tlv_txdisabled(cmd->ifname, cmd->type, cmd->tlvid)) value = true; else value = false; break; case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_APP: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (is_tlv_txenabled(cmd->ifname, cmd->type, cmd->tlvid)) value = true; else value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { int value, curr, err; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_IEEE_8021 << 8): case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSCFG: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_ETSREC: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_PFC: case (OUI_IEEE_8021 << 8) | LLDP_8021QAZ_APP: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, ARG_TLVTXENABLE); err = get_config_setting(cmd->ifname, cmd->type, arg_path, &curr, CONFIG_TYPE_BOOL); if (test) return cmd_success; snprintf(obuf, obuf_len, "enabled = %s\n", value ? "yes" : "no"); if (!err && curr == value) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) return cmd_failed; somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, true); } struct arg_handlers *ieee8021qaz_get_arg_handlers() { return &arg_handlers[0]; } lldpad-0.9.46/lldp_8023.c000066400000000000000000000271171215142747300146430ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include "lldp.h" #include "lldp_8023.h" #include "messages.h" #include "config.h" #include "lldp_8023_clif.h" #include "lldp_8023_cmds.h" extern struct lldp_head lldp_head; struct tlv_info_8023_maccfg { u8 oui[3]; u8 sub; u8 neg; u16 adv; u16 mau; } __attribute__ ((__packed__)); struct tlv_info_8023_maxfs { u8 oui[3]; u8 sub; u16 mfs; } __attribute__ ((__packed__)); struct tlv_info_8023_linkagg { u8 oui[3]; u8 sub; u8 status; u32 portid; } __attribute__ ((__packed__)); struct tlv_info_8023_powvmdi { u8 oui[3]; u8 sub; u8 caps; u8 pairs; u8 class; } __attribute__ ((__packed__)); static const struct lldp_mod_ops ieee8023_ops = { .lldp_mod_register = ieee8023_register, .lldp_mod_unregister = ieee8023_unregister, .lldp_mod_gettlv = ieee8023_gettlv, .lldp_mod_ifup = ieee8023_ifup, .lldp_mod_ifdown = ieee8023_ifdown, .get_arg_handler = ieee8023_get_arg_handlers, }; static struct ieee8023_data *ieee8023_data(const char *ifname, enum agent_type type) { struct ieee8023_user_data *ud; struct ieee8023_data *bd = NULL; ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8023); if (ud) { LIST_FOREACH(bd, &ud->head, entry) { if (!strncmp(ifname, bd->ifname, IFNAMSIZ) && (type == bd->agenttype)) return bd; } } return NULL; } /* * ieee8023_bld_maccfg_tlv - build the MAC/PHY Config Status TLV * @bd: the med data struct * * Returns 0 on success */ static int ieee8023_bld_maccfg_tlv(struct ieee8023_data *bd, struct lldp_agent *agent) { int rc = 0; struct unpacked_tlv *tlv = NULL; struct tlv_info_8023_maccfg maccfg; /* free old one if it exists */ FREE_UNPKD_TLV(bd, maccfg); /* mandatory for LLDP-MED */ if (!is_tlv_txenabled(bd->ifname, agent->type, TLVID_8023(LLDP_8023_MACPHY_CONFIG_STATUS))) { goto out_err; } /* load from config */ memset(&maccfg, 0, sizeof(maccfg)); if (get_config_tlvinfo_bin(bd->ifname, agent->type, TLVID_8023(LLDP_8023_MACPHY_CONFIG_STATUS), &maccfg, sizeof(maccfg))) { hton24(maccfg.oui, OUI_IEEE_8023); maccfg.sub = LLDP_8023_MACPHY_CONFIG_STATUS; if (is_autoneg_supported(bd->ifname)) maccfg.neg |= LLDP_8023_MACPHY_AUTONEG_SUPPORT; if (is_autoneg_enabled(bd->ifname)) maccfg.neg |= LLDP_8023_MACPHY_AUTONEG_ENABLED; maccfg.adv = htons(get_maucaps(bd->ifname)); maccfg.mau = htons(get_mautype(bd->ifname)); } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(maccfg); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &maccfg, tlv->length); bd->maccfg = tlv; rc = 0; out_err: return rc; } /* * ieee8023_bld_maxfs_tlv - build the Max Frame Size TLV * @bd: the med data struct * * Returns 0 on success */ static int ieee8023_bld_maxfs_tlv(struct ieee8023_data *bd, struct lldp_agent *agent) { int rc = 0; struct unpacked_tlv *tlv = NULL; struct tlv_info_8023_maxfs maxfs; /* free old one if it exists */ FREE_UNPKD_TLV(bd, maxfs); if (!is_tlv_txenabled(bd->ifname, agent->type, TLVID_8023(LLDP_8023_MAXIMUM_FRAME_SIZE))) { goto out_err; } /* load from config */ memset(&maxfs, 0, sizeof(maxfs)); if (get_config_tlvinfo_bin(bd->ifname, agent->type, TLVID_8023(LLDP_8023_MAXIMUM_FRAME_SIZE), &maxfs, sizeof(maxfs))) { hton24(maxfs.oui, OUI_IEEE_8023); maxfs.sub = LLDP_8023_MAXIMUM_FRAME_SIZE; maxfs.mfs = htons(get_mfs(bd->ifname)); } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(maxfs); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &maxfs, tlv->length); bd->maxfs = tlv; rc = 0; out_err: return rc; } /* * ieee8023_bld_linkagg_tlv - build the Link Aggregation TLV * @bd: the med data struct * * Returns 0 on success */ static int ieee8023_bld_linkagg_tlv(struct ieee8023_data *bd, struct lldp_agent *agent) { int rc = 0; struct unpacked_tlv *tlv = NULL; struct tlv_info_8023_linkagg linkagg; /* free old one if it exists */ FREE_UNPKD_TLV(bd, linkagg); if (!is_tlv_txenabled(bd->ifname, agent->type, TLVID_8023(LLDP_8023_LINK_AGGREGATION))) { goto out_err; } /* load from config */ memset(&linkagg, 0, sizeof(linkagg)); if (get_config_tlvinfo_bin(bd->ifname, agent->type, TLVID_8023(LLDP_8023_LINK_AGGREGATION), &linkagg, sizeof(linkagg))) { hton24(linkagg.oui, OUI_IEEE_8023); linkagg.sub = LLDP_8023_LINK_AGGREGATION; if (is_bond(bd->ifname)) { linkagg.status = (LLDP_8023_LINKAGG_CAPABLE | LLDP_8023_LINKAGG_ENABLED); linkagg.portid = htonl(get_master(bd->ifname)); } } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(linkagg); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &linkagg, tlv->length); bd->linkagg = tlv; rc = 0; out_err: return rc; } /* * ieee8023_bld_powvmdi_tlv - build the Power via MDI TLV * @bd: the med data struct * * Returns 0 on success */ static int ieee8023_bld_powvmdi_tlv(struct ieee8023_data *bd, struct lldp_agent *agent) { int rc = 0; struct unpacked_tlv *tlv = NULL; struct tlv_info_8023_powvmdi powvmdi; /* free old one if it exists */ FREE_UNPKD_TLV(bd, powvmdi); if (!is_tlv_txenabled(bd->ifname, agent->type, TLVID_8023(LLDP_8023_POWER_VIA_MDI))) { goto out_err; } /* not recommended for LLDP-MED */ if (is_tlv_txenabled(bd->ifname, agent->type, TLVID_MED(LLDP_MED_RESERVED))) { /* do not fail */ goto out_err; } /* TODO: currently only supports config */ memset(&powvmdi, 0, sizeof(powvmdi)); if (get_config_tlvinfo_bin(bd->ifname, agent->type, TLVID_8023(LLDP_8023_POWER_VIA_MDI), &powvmdi, sizeof(powvmdi))) { goto out_err; } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(powvmdi); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &powvmdi, tlv->length); bd->powvmdi = tlv; rc = 0; out_err: return rc; } static void ieee8023_free_tlv(struct ieee8023_data *bd) { if (bd) { FREE_UNPKD_TLV(bd, maccfg); FREE_UNPKD_TLV(bd, powvmdi); FREE_UNPKD_TLV(bd, linkagg); FREE_UNPKD_TLV(bd, maxfs); } } static int ieee8023_bld_tlv(struct ieee8023_data *bd, struct lldp_agent *agent) { int rc = 0; if (!port_find_by_name(bd->ifname)) { rc = EEXIST; goto out_err; } if (ieee8023_bld_maccfg_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:ieee8023_bld_macfg_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (ieee8023_bld_powvmdi_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:ieee8023_bld_powvmdi_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (ieee8023_bld_linkagg_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:ieee8023_bld_linkagg_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (ieee8023_bld_maxfs_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:ieee8023_bld_maxfs_tlv() failed\n", __func__, bd->ifname); goto out_err; } rc = 0; out_err: return rc; } static void ieee8023_free_data(struct ieee8023_user_data *ud) { struct ieee8023_data *bd; if (ud) { while (!LIST_EMPTY(&ud->head)) { bd = LIST_FIRST(&ud->head); LIST_REMOVE(bd, entry); ieee8023_free_tlv(bd); free(bd); } } } struct packed_tlv *ieee8023_gettlv(struct port *port, struct lldp_agent *agent) { int size; struct ieee8023_data *bd; struct packed_tlv *ptlv = NULL; bd = ieee8023_data(port->ifname, agent->type); if (!bd) goto out_err; ieee8023_free_tlv(bd); if (ieee8023_bld_tlv(bd, agent)) { LLDPAD_DBG("%s:%s ieee8023_bld_tlv failed\n", __func__, port->ifname); goto out_err; } size = TLVSIZE(bd->maccfg) + TLVSIZE(bd->powvmdi) + TLVSIZE(bd->linkagg) + TLVSIZE(bd->maxfs); if (!size) goto out_err; ptlv = create_ptlv(); if (!ptlv) goto out_err; ptlv->tlv = malloc(size); if (!ptlv->tlv) goto out_free; ptlv->size = 0; PACK_TLV_AFTER(bd->maccfg, ptlv, size, out_free); PACK_TLV_AFTER(bd->powvmdi, ptlv, size, out_free); PACK_TLV_AFTER(bd->linkagg, ptlv, size, out_free); PACK_TLV_AFTER(bd->maxfs, ptlv, size, out_free); return ptlv; out_free: ptlv = free_pkd_tlv(ptlv); out_err: LLDPAD_DBG("%s:%s: failed\n", __func__, port->ifname); return NULL; } void ieee8023_ifdown(char *ifname, struct lldp_agent *agent) { struct ieee8023_data *bd; bd = ieee8023_data(ifname, agent->type); if (!bd) goto out_err; LIST_REMOVE(bd, entry); ieee8023_free_tlv(bd); free(bd); LLDPAD_INFO("%s:port %s removed\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s:port %s adding failed\n", __func__, ifname); return; } void ieee8023_ifup(char *ifname, struct lldp_agent *agent) { struct ieee8023_data *bd; struct ieee8023_user_data *ud; bd = ieee8023_data(ifname, agent->type); if (bd) { LLDPAD_INFO("%s:%s exists\n", __func__, ifname); goto out_err; } /* not found, alloc/init per-port tlv data */ bd = (struct ieee8023_data *) malloc(sizeof(*bd)); if (!bd) { LLDPAD_INFO("%s:%s malloc %zu failed\n", __func__, ifname, sizeof(*bd)); goto out_err; } memset(bd, 0, sizeof(struct ieee8023_data)); strncpy(bd->ifname, ifname, IFNAMSIZ); bd->agenttype = agent->type; if (ieee8023_bld_tlv(bd, agent)) { LLDPAD_INFO("%s:%s mand_bld_tlv failed\n", __func__, ifname); free(bd); goto out_err; } ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_8023); LIST_INSERT_HEAD(&ud->head, bd, entry); LLDPAD_INFO("%s:port %s added\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s:port %s adding failed\n", __func__, ifname); return; } struct lldp_module *ieee8023_register(void) { struct lldp_module *mod; struct ieee8023_user_data *ud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("failed to malloc LLDP 802.3 module data"); goto out_err; } ud = malloc(sizeof(struct ieee8023_user_data)); if (!ud) { free(mod); LLDPAD_ERR("failed to malloc LLDP 802.3 module user data"); goto out_err; } LIST_INIT(&ud->head); mod->id = LLDP_MOD_8023; mod->ops = &ieee8023_ops; mod->data = ud; LLDPAD_INFO("%s:done\n", __func__); return mod; out_err: LLDPAD_INFO("%s:failed\n", __func__); return NULL; } void ieee8023_unregister(struct lldp_module *mod) { if (mod->data) { ieee8023_free_data((struct ieee8023_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_INFO("%s:done\n", __func__); } lldpad-0.9.46/lldp_8023_clif.c000066400000000000000000000264131215142747300156360ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldp_mod.h" #include "lldptool.h" #include "clif_msgs.h" #include "lldp.h" #include "lldp_8023.h" #include "lldp_8023_clif.h" #include "lldp_util.h" void print_mac_phy(u16, char *info); void print_power_mdi(u16, char *info); void print_link_agg(u16, char *info); void print_mtu(u16, char *info); int ieee8023_print_help(); u32 ieee8023_lookup_tlv_name(char *tlvid_str); static const struct lldp_mod_ops ieee8023_ops_clif = { .lldp_mod_register = ieee8023_cli_register, .lldp_mod_unregister = ieee8023_cli_unregister, .print_tlv = ieee8023_print_tlv, .lookup_tlv_name = ieee8023_lookup_tlv_name, .print_help = ieee8023_print_help, }; struct type_name_info ieee8023_tlv_names[] = { { .type = (LLDP_MOD_8023 << 8) | LLDP_8023_MACPHY_CONFIG_STATUS, .name = "MAC/PHY Configuration Status TLV", .key = "macPhyCfg", .print_info = print_mac_phy, }, { .type = (LLDP_MOD_8023 << 8) | LLDP_8023_POWER_VIA_MDI, .name = "Power via MDI TLV", .key = "powerMdi", .print_info = print_power_mdi, }, { .type = (LLDP_MOD_8023 << 8) | LLDP_8023_LINK_AGGREGATION, .name = "Link Aggregation TLV", .key = "linkAgg", .print_info = print_link_agg, }, { .type = (LLDP_MOD_8023 << 8) | LLDP_8023_MAXIMUM_FRAME_SIZE, .name = "Maximum Frame Size TLV", .key = "MTU", .print_info = print_mtu, }, { .type = INVALID_TLVID, } }; int ieee8023_print_help() { struct type_name_info *tn = &ieee8023_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *ieee8023_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); goto out_err; } mod->id = LLDP_MOD_8023; mod->ops = &ieee8023_ops_clif; return mod; out_err: return NULL; } void ieee8023_cli_unregister(struct lldp_module *mod) { free(mod); } void print_mac_phy(u16 len, char *info) { u8 autoneg_status; u16 pmd_autoneg_cap; u16 mau_type; if (len != 5) { printf("Bad MAC/PHY TLV: %*.*s\n", len*2, len*2, info); return; } hexstr2bin(info, (u8 *)&autoneg_status, sizeof(autoneg_status)); hexstr2bin(info+2, (u8 *)&pmd_autoneg_cap, sizeof(pmd_autoneg_cap)); pmd_autoneg_cap = ntohs(pmd_autoneg_cap); hexstr2bin(info+6, (u8 *)&mau_type, sizeof(mau_type)); mau_type = ntohs(mau_type); printf("Auto-negotiation %s and %s\n", (autoneg_status & 0x01) ? "supported" : "not supported", (autoneg_status & 0x02) ? "enabled" : "not enabled"); printf("\tPMD auto-negotiation capabilities: 0x%04x\n", pmd_autoneg_cap); /* See dot3MauType IETF RFC 4836 && */ /* IANA MAU MIB */ printf("\tMAU type:"); switch (mau_type) { case DOT3MAUTYPE_AUI: printf(" AUI"); break; case DOT3MAUTYPE_10Base5: printf(" 10 Base5"); break; case DOT3MAUTYPE_Foirl: printf(" FOIRL"); break; case DOT3MAUTYPE_10Base2: printf(" 10 Base2"); break; case DOT3MAUTYPE_10BaseT: printf(" 10 BaseT"); break; case DOT3MAUTYPE_10BaseFP: printf(" 10 BaseFP"); break; case DOT3MAUTYPE_10BaseFB: printf(" 10 BaseFB"); break; case DOT3MAUTYPE_10BaseFL: printf(" 10 BaseFL"); break; case DOT3MAUTYPE_10Broad36: printf(" 10 Broad 36"); break; case DOT3MAUTYPE_10BaseTHD: printf(" 100 BaseTHD"); break; case DOT3MAUTYPE_10BaseTFD: printf(" 100 BaseTFD"); break; case DOT3MAUTYPE_10BaseFLHD: printf(" 100 BaseFLHD"); break; case DOT3MAUTYPE_10BaseFLFD: printf(" 100 BaseFLFD"); break; case DOT3MAUTYPE_100BaseT4: printf(" 100 BaseT4"); break; case DOT3MAUTYPE_100BaseTXHD: printf(" 100 BaseTXHD"); break; case DOT3MAUTYPE_100BaseTXFD: printf(" 100 BaseTXFD"); break; case DOT3MAUTYPE_100BaseFXHD: printf(" 100 BaseFXHD"); break; case DOT3MAUTYPE_100BaseFXFD: printf(" 100 BaseFXFD"); break; case DOT3MAUTYPE_100BaseT2HD: printf(" 100 BaseT2HD"); break; case DOT3MAUTYPE_100BaseT2FD: printf(" 100 BaseT2FD"); break; case DOT3MAUTYPE_1000BaseXHD: printf(" 1000 BaseXHD"); break; case DOT3MAUTYPE_1000BaseXFD: printf(" 1000 BaseXFD"); break; case DOT3MAUTYPE_1000BaseLXHD: printf(" 1000 BaseLXHD"); break; case DOT3MAUTYPE_1000BaseLXFD: printf(" 1000 BaseLXFD"); break; case DOT3MAUTYPE_1000BaseSXHD: printf(" 1000 BaseSXHD"); break; case DOT3MAUTYPE_1000BaseSXFD: printf(" 1000 BaseSXFD"); break; case DOT3MAUTYPE_1000BaseCXHD: printf(" 1000 BaseCXHD"); break; case DOT3MAUTYPE_1000BaseCXFD: printf(" 1000 BaseCXFD"); break; case DOT3MAUTYPE_1000BaseTHD: printf(" 1000 BaseTHD"); break; case DOT3MAUTYPE_1000BaseTFD: printf(" 1000 BaseTFD"); break; case DOT3MAUTYPE_10GBaseX: printf(" 10G BaseX"); break; case DOT3MAUTYPE_10GBaseLX4: printf(" 10G BaseLX4"); break; case DOT3MAUTYPE_10GBaseR: printf(" 10G BaseR"); break; case DOT3MAUTYPE_10GBaseER: printf(" 10G BaseER"); break; case DOT3MAUTYPE_10GBaseLR: printf(" 10G BaseLR"); break; case DOT3MAUTYPE_10GBaseSR: printf(" 10G BaseSR"); break; case DOT3MAUTYPE_10GBaseW: printf(" 10G BaseW"); break; case DOT3MAUTYPE_10GBaseEW: printf(" 10G BaseEW"); break; case DOT3MAUTYPE_10GBaseLW: printf(" 10G BaseLW"); break; case DOT3MAUTYPE_10GBaseSW: printf(" 10G BaseSW"); break; case DOT3MAUTYPE_10GBaseCX4: printf(" 10G BaseCX4"); break; case DOT3MAUTYPE_2BaseTL: printf(" 2 BaseTL"); break; case DOT3MAUTYPE_10PassTS: printf(" 10 PassTS"); break; case DOT3MAUTYPE_100BaseBX10D: printf(" 100 BaseBX10D"); break; case DOT3MAUTYPE_100BaseBX10U: printf(" 100 BaseBX10U"); break; case DOT3MAUTYPE_100BaseLX10: printf(" 100 BaseLX10"); break; case DOT3MAUTYPE_1000BaseBX10D: printf(" 1000 BaseBX10D"); break; case DOT3MAUTYPE_1000BaseBX10U: printf(" 1000 BaseBX10U"); break; case DOT3MAUTYPE_1000BaseLX10: printf(" 1000 BaseLX10"); break; case DOT3MAUTYPE_1000BasePX10D: printf(" 1000 BasePX10D"); break; case DOT3MAUTYPE_1000BasePX10U: printf(" 1000 BasePX10U"); break; case DOT3MAUTYPE_1000BasePX20D: printf(" 1000 BasePX20D"); break; case DOT3MAUTYPE_1000BasePX20U: printf(" 1000 BasePX20U"); break; case DOT3MAUTYPE_10GBaseT: printf(" 10G BaseT"); break; case DOT3MAUTYPE_10GBaseLRM: printf(" 10G BaseLRM"); break; case DOT3MAUTYPE_1000BaseKX: printf(" 1000 BaseKX"); break; case DOT3MAUTYPE_10GBaseKX4: printf(" 10G BaseKX4"); break; case DOT3MAUTYPE_10GBaseKR: printf(" 10G BaseKR"); break; case DOT3MAUTYPE_10_1GBasePRXD1: printf(" 10/1G BasePRXD1"); break; case DOT3MAUTYPE_10_1GBasePRXD2: printf(" 10/1G BasePRXD2"); break; case DOT3MAUTYPE_10_1GBasePRXD3: printf(" 10/1G BasePRXD3"); break; case DOT3MAUTYPE_10_1GBasePRXU1: printf(" 10/1G BasePRXU1"); break; case DOT3MAUTYPE_10_1GBasePRXU2: printf(" 10/1G BasePRXU2"); break; case DOT3MAUTYPE_10_1GBasePRXU3: printf(" 10/1G BasePRXU3"); break; case DOT3MAUTYPE_10GBasePRD1: printf(" 10G BasePRD1"); break; case DOT3MAUTYPE_10GBasePRD2: printf(" 10G BasePRD2"); break; case DOT3MAUTYPE_10GBasePRD3: printf(" 10G BasePRD3"); break; case DOT3MAUTYPE_10GBasePRU1: printf(" 10G BasePRU1"); break; case DOT3MAUTYPE_10GBasePRU3: printf(" 10G BasePRU3"); break; case DOT3MAUTYPE_40GBaseKR4: printf(" 40G BaseKR4"); break; case DOT3MAUTYPE_40GBaseCR4: printf(" 40G BaseCR4"); break; case DOT3MAUTYPE_40GBaseSR4: printf(" 40G BaseSR4"); break; case DOT3MAUTYPE_40GBaseFR: printf(" 40G BaseFR"); break; case DOT3MAUTYPE_40GBaseLR4: printf(" 40G BaseLR4"); break; case DOT3MAUTYPE_100GBaseCR10: printf(" 100G BaseCR10"); break; case DOT3MAUTYPE_100GBaseSR10: printf(" 100G BaseSR10"); break; case DOT3MAUTYPE_100GBaseLR4: printf(" 100G BaseLR4"); break; case DOT3MAUTYPE_100GBaseER4: printf(" 100G BaseER4"); break; default: printf(" Unknown [0x%04x]", mau_type); break; } printf("\n"); } void print_power_mdi(u16 len, char *info) { u8 mdi_power; u8 pse_power; u8 power_class; if (len != 3) { printf("Bad Power Via MDI TLV: %*.*s\n", len*2, len*2, info); return; } hexstr2bin(info, (u8 *)&mdi_power, sizeof(mdi_power)); hexstr2bin(info+2, (u8 *)&pse_power, sizeof(pse_power)); hexstr2bin(info+4, (u8 *)&power_class, sizeof(power_class)); printf("Port class %s\n\t", (mdi_power & 0x01) ? "PSE" : "PD"); printf("PSE MDI power %ssupported\n\t", (mdi_power & 0x02) ? "" : "not "); if (mdi_power & 0x02) printf(" and %s", (mdi_power & 0x04) ? "enabled" : "disabled"); printf("PSE pairs %scontrollable\n\t", (mdi_power & 0x08) ? "" : "not "); /* pethPsePortPowerPair - IETF RFC 3621 */ printf("PSE Power pair: "); if (pse_power == 1) printf("signal"); else if (pse_power == 2) printf("spare"); else printf("unkwown [%d]", pse_power); printf("\n\t"); /* pethPsePortPowerClassifications - IETF RFC 3621 */ printf("Power class %d\n", power_class+1); } void print_link_agg(u16 len, char *info) { u8 agg_status; u32 agg_portid; if (len != 5) { printf("Bad Link Aggregation TLV: %*.*s\n", len*2, len*2, info); return; } hexstr2bin(info, (u8 *)&agg_status, sizeof(agg_status)); hexstr2bin(info+2, (u8 *)&agg_portid, sizeof(agg_portid)); agg_portid = ntohl(agg_portid); printf("Aggregation %scapable\n", (agg_status & 0x01) ? "":"not "); printf("\tCurrently %saggregated\n", (agg_status & 0x02) ? "":"not "); printf("\tAggregated Port ID: %d\n", agg_portid); } void print_mtu(UNUSED u16 len, char *info) { u16 mtu; hexstr2bin(info, (u8 *)&mtu, sizeof(mtu)); mtu = ntohs(mtu); printf("%d\n", mtu); } /* return 1: if it printed the TLV * 0: if it did not */ int ieee8023_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &ieee8023_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } u32 ieee8023_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &ieee8023_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_8023_cmds.c000066400000000000000000000104221215142747300156400ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldpad.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_8023.h" #include "lldp_mand_clif.h" #include "lldp_8023_clif.h" #include "lldp/ports.h" #include "libconfig.h" #include "config.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/states.h" static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int test_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable, }, { .arg = 0 } }; static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (LLDP_MOD_8023 << 8) | LLDP_8023_MACPHY_CONFIG_STATUS: case (LLDP_MOD_8023 << 8) | LLDP_8023_POWER_VIA_MDI: case (LLDP_MOD_8023 << 8) | LLDP_8023_LINK_AGGREGATION: case (LLDP_MOD_8023 << 8) | LLDP_8023_MAXIMUM_FRAME_SIZE: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { int value; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (LLDP_MOD_8023 << 8) | LLDP_8023_MACPHY_CONFIG_STATUS: case (LLDP_MOD_8023 << 8) | LLDP_8023_POWER_VIA_MDI: case (LLDP_MOD_8023 << 8) | LLDP_8023_LINK_AGGREGATION: case (LLDP_MOD_8023 << 8) | LLDP_8023_MAXIMUM_FRAME_SIZE: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) return cmd_failed; snprintf(obuf, obuf_len, "enableTx = %s\n", value ? "yes" : "no"); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, true); } struct arg_handlers *ieee8023_get_arg_handlers() { return &arg_handlers[0]; } lldpad-0.9.46/lldp_basman.c000066400000000000000000000424221215142747300155040ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include "lldp.h" #include "lldp_basman.h" #include "messages.h" #include "clif_msgs.h" #include "config.h" #include "libconfig.h" #include "lldp_mand_clif.h" #include "lldp_basman_cmds.h" #include "lldp_util.h" #define SYSNAME_DEFAULT "localhost" #define IFNUM_SUBTYPE_UNKNOWN 1 #define IFNUM_SUBTYPE_IFINDEX 2 #define IFNUM_SUBTYPE_PORTNUM 3 struct tlv_info_maddr { u8 len; u8 sub; union { struct in_addr in; struct in6_addr in6; u8 mac[6]; u8 addr[31]; } __attribute__ ((__packed__)) a; } __attribute__ ((__packed__)); struct tlv_info_maif { u8 sub; u32 num; } __attribute__ ((__packed__)); struct tlv_info_maoid { u8 len; u8 oid[128]; } __attribute__ ((__packed__)); struct tlv_info_manaddr { struct tlv_info_maddr m; struct tlv_info_maif i; struct tlv_info_maoid o; } __attribute__ ((__packed__)); extern struct lldp_head lldp_head; static const struct lldp_mod_ops basman_ops = { .lldp_mod_register = basman_register, .lldp_mod_unregister = basman_unregister, .lldp_mod_gettlv = basman_gettlv, .lldp_mod_ifup = basman_ifup, .lldp_mod_ifdown = basman_ifdown, .get_arg_handler = basman_get_arg_handlers, }; static struct basman_data *basman_data(const char *ifname, enum agent_type type) { struct basman_user_data *bud; struct basman_data *bd = NULL; bud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_BASIC); if (bud) { LIST_FOREACH(bd, &bud->head, entry) { if (!strncmp(ifname, bd->ifname, IFNAMSIZ) && (type == bd->agenttype)) return bd; } } return NULL; } /* * basman_bld_portdesc_tlv - build port description TLV * @bd: the basman data struct * * Returns 0 for success or error code for failure */ static int basman_bld_portdesc_tlv(struct basman_data *bd, struct lldp_agent *agent) { unsigned int length; int rc = 0; char desc[256]; struct unpacked_tlv *tlv = NULL; /* free old if it's there */ FREE_UNPKD_TLV(bd, portdesc); if (!is_tlv_txenabled(bd->ifname, agent->type, PORT_DESCRIPTION_TLV)) { LLDPAD_DBG("%s:%s:Port Description disabled\n", __func__, bd->ifname); goto out_err; } /* load from config */ if (!get_config_tlvinfo_str(bd->ifname, agent->type, TLVID_NOUI(PORT_DESCRIPTION_TLV), desc, sizeof(desc))) { /* use what's in the config */ length = strlen(desc); LLDPAD_DBG("%s:%s:configed as %s\n", __func__, bd->ifname, desc); } else { length = snprintf(desc, sizeof(desc), "Interface %3d as %s", if_nametoindex(bd->ifname), bd->ifname); LLDPAD_DBG("%s:%s:built as %s\n", __func__, bd->ifname, desc); } if (length >= sizeof(desc)) length = sizeof(desc) - 1; tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = PORT_DESCRIPTION_TLV; tlv->length = length; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, desc, tlv->length); bd->portdesc = tlv; rc = 0; out_err: return rc; } /* * basman_bld_sysname_tlv - build port description TLV * @bd: the basman data struct * * Returns 0 for success or error code for failure */ static int basman_bld_sysname_tlv(struct basman_data *bd, struct lldp_agent *agent) { unsigned int length; int rc = 0; char desc[256]; struct utsname uts; struct unpacked_tlv *tlv = NULL; /* free old if it's there */ FREE_UNPKD_TLV(bd, sysname); if (!is_tlv_txenabled(bd->ifname, agent->type, SYSTEM_NAME_TLV)) { LLDPAD_DBG("%s:%s:System Name disabled\n", __func__, bd->ifname); goto out_err; } /* load from config */ if (!get_config_tlvinfo_str(bd->ifname, agent->type, TLVID_NOUI(SYSTEM_NAME_TLV), desc, sizeof(desc))) { /* use what's in the config */ LLDPAD_DBG("%s:%s:configed as %s\n", __func__, bd->ifname, desc); } else { const char *node_name; if (uname(&uts)) node_name = SYSNAME_DEFAULT; else node_name = uts.nodename; strncpy(desc, node_name, sizeof(desc)); desc[sizeof(desc) - 1] = 0; LLDPAD_DBG("%s:%s:built as %s\n", __func__, bd->ifname, desc); } length = strlen(desc); if (length >= sizeof(desc)) length = sizeof(desc) - 1; tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = SYSTEM_NAME_TLV; tlv->length = length; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info){ free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, desc, tlv->length); bd->sysname = tlv; rc = 0; out_err: return rc; } /* * basman_bld_sysdesc_tlv - build port description TLV * @bd: the basman data struct * * Returns 0 for success or error code for failure * * net-snmp-utils: snmptest returns the following for sysDesr: `uname-a` */ static int basman_bld_sysdesc_tlv(struct basman_data *bd, struct lldp_agent *agent) { unsigned int length; int rc = 0; char desc[256]; struct utsname uts; struct unpacked_tlv *tlv = NULL; /* free old if it's there */ FREE_UNPKD_TLV(bd, sysdesc); if (!is_tlv_txenabled(bd->ifname, agent->type, SYSTEM_DESCRIPTION_TLV)) { LLDPAD_DBG("%s:%s:System Description disabled\n", __func__, bd->ifname); goto out_err; } /* load from config */ if (!get_config_tlvinfo_str(bd->ifname, agent->type, TLVID_NOUI(SYSTEM_DESCRIPTION_TLV), desc, sizeof(desc))) { /* use what's in the config */ length = strlen(desc); LLDPAD_DBG("%s:%s:configed as %s\n", __func__, bd->ifname, desc); } else { if (uname(&uts)) { length = snprintf(desc, sizeof(desc), "Unknown system"); } else { length = snprintf(desc, sizeof(desc), "%s %s %s %s %s", uts.sysname, uts.nodename, uts.release, uts.version, uts.machine); } LLDPAD_DBG("%s:%s:built as %s\n", __func__, bd->ifname, desc); } if (length >= sizeof(desc)) length = sizeof(desc) - 1; tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = SYSTEM_DESCRIPTION_TLV; tlv->length = length; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info){ free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, desc, tlv->length); bd->sysdesc = tlv; rc = 0; out_err: return rc; } /* * basman_bld_syscaps_tlv - build port description TLV * @bd: the basman data struct * * Returns 0 for success or error code for failure * * TODO: * - This is mandatory for LLDP-MED Class III * - TPID to determine C-VLAN vs. S-VLAN ? */ static int basman_bld_syscaps_tlv(struct basman_data *bd, struct lldp_agent *agent) { int rc = 0; u16 syscaps[2]; struct unpacked_tlv *tlv = NULL; /* free old if it's there */ FREE_UNPKD_TLV(bd, syscaps); if (!is_tlv_txenabled(bd->ifname, agent->type, SYSTEM_CAPABILITIES_TLV)) { LLDPAD_DBG("%s:%s:System Capabilities disabled\n", __func__, bd->ifname); goto out_err; } /* load from config */ if (get_config_tlvinfo_bin(bd->ifname, agent->type, TLVID_NOUI(SYSTEM_CAPABILITIES_TLV), &syscaps, sizeof(syscaps))) { LLDPAD_DBG("%s:%s:Build System Caps from scratch\n", __func__, bd->ifname); syscaps[0] = htons(get_caps(bd->ifname)); syscaps[1] = (is_active(bd->ifname)) ? syscaps[0] : 0; } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = SYSTEM_CAPABILITIES_TLV; tlv->length = sizeof(syscaps); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &syscaps, tlv->length); bd->syscaps = tlv; rc = 0; out_err: return rc; } /* * basman_get_manaddr_sub - build management address TLV by subtype * @bd: the basman data struct * @masub: IETF RFC 3232 ianaAddressFamilyNumbers * * Returns 0 for success or error code for failure * * Currently supports only IPv4, IPv6, and MAC address types. * */ static int basman_get_manaddr_sub(struct basman_data *bd, struct lldp_agent *agent, u8 masub) { int domain; int length = 0; int rc = EINVAL; u8 *data = NULL; char maddr[128]; char *field = NULL; struct tlv_info_maif *i = NULL; struct tlv_info_maddr *m = NULL; struct tlv_info_maoid *o = NULL; struct tlv_info_manaddr manaddr; struct unpacked_tlv *tlv = NULL; if (bd->macnt >= MANADDR_MAX) { LLDPAD_DBG("%s:%s:reached max %d Management Address\n", __func__, bd->ifname, bd->macnt); goto out_err; } memset(maddr, 0, sizeof(maddr)); memset(&manaddr, 0, sizeof(manaddr)); m = &manaddr.m; m->sub = masub; switch(m->sub) { case MANADDR_IPV4: field = "ipv4"; domain = AF_INET; m->len = sizeof(m->a.in); break; case MANADDR_IPV6: field = "ipv6"; domain = AF_INET6; m->len = sizeof(m->a.in6); break; case MANADDR_ALL802: field = "mac"; domain = AF_UNSPEC; m->len = sizeof(m->a.mac); break; default: LLDPAD_DBG("%s:%s:unsupported sub type %d\n", __func__, bd->ifname, masub); goto out_err; } m->len += sizeof(m->sub); /* read from the config first */ if (get_config_tlvfield_str(bd->ifname, agent->type, TLVID_NOUI(MANAGEMENT_ADDRESS_TLV), field, maddr, sizeof(maddr))) { LLDPAD_DBG("%s:%s:failed to get %s from config\n", __func__, bd->ifname, field); goto out_bld; } if (!str2addr(domain, maddr, &m->a, sizeof(m->a))) { goto out_set; } out_bld: /* failed to get from config, so build from scratch */ if (get_addr(bd->ifname, domain, &m->a)) { LLDPAD_DBG("%s:%s:get_addr() for domain %d failed\n", __func__, bd->ifname, domain); goto out_err; } if (addr2str(domain, &m->a, maddr, sizeof(maddr))) { LLDPAD_DBG("%s:%s:get_addr() for domain %d failed\n", __func__, bd->ifname, domain); goto out_err; } out_set: set_config_tlvfield_str(bd->ifname, agent->type, TLVID_NOUI(MANAGEMENT_ADDRESS_TLV), field, maddr); /* build ifnum and oid: * mlen + msub + maddr + ifsub + ifidx + oidlen + oid * 1 + 1 + [1-31] + 1 + 4 + 1 + [1-128] */ data = (u8 *)&manaddr; length = sizeof(manaddr.m.len) + manaddr.m.len; i = (struct tlv_info_maif *)&data[length]; i->sub = IFNUM_SUBTYPE_IFINDEX; i->num = htonl(if_nametoindex(bd->ifname)); length += sizeof(struct tlv_info_maif); o = (struct tlv_info_maoid *)&data[length]; o->len = 0; length += sizeof(o->len) + o->len; tlv = create_tlv(); if (!tlv) goto out_err; tlv->length = length; tlv->type = MANAGEMENT_ADDRESS_TLV; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; goto out_err; } memcpy(tlv->info, &manaddr, tlv->length); bd->manaddr[bd->macnt] = tlv; bd->macnt++; LLDPAD_DBG("%s:%s:maddr[%d]:tlv->len %d bytes \n", __func__, bd->ifname, bd->macnt, tlv->length); rc = 0; out_err: return rc; } /* * basman_bld_manddrr_tlv - build management address TLV * @bd: the basman data struct * * Returns 0 for success or error code for failure * * Use as many existing as possible * Preference is config > ipv6 > ipv4 > mac > default * * Max info length is = 1 + 1 + 31 + 1 + 4 + 1 + 128 = 167 * * TODO: * - No support for OID yet */ static int basman_bld_manaddr_tlv(struct basman_data *bd, struct lldp_agent *agent) { int i; int rc = 0; /* free all existing manaddr TLVs */ for (i = 0; i < bd->macnt; i++) FREE_UNPKD_TLV(bd, manaddr[i]); bd->macnt = 0; /* ignore manaddr if it's not enabled for tx */ if (!is_tlv_txenabled(bd->ifname, agent->type, MANAGEMENT_ADDRESS_TLV)) { LLDPAD_DBG("%s:%s:Management Address disabled\n", __func__, bd->ifname); goto out_err; } /* management addr preference: ipv4, ipv6, mac */ rc = basman_get_manaddr_sub(bd, agent, MANADDR_IPV4); if (rc) { rc = basman_get_manaddr_sub(bd, agent, MANADDR_IPV6); if (rc) basman_get_manaddr_sub(bd, agent, MANADDR_ALL802); } out_err: return rc; } static void basman_free_tlv(struct basman_data *bd) { int i = 0; if (bd) { FREE_UNPKD_TLV(bd, portdesc); FREE_UNPKD_TLV(bd, sysname); FREE_UNPKD_TLV(bd, sysdesc); FREE_UNPKD_TLV(bd, syscaps); for (i = 0; i < bd->macnt; i++) FREE_UNPKD_TLV(bd, manaddr[i]); bd->macnt = 0; } } /* build unpacked tlvs */ static int basman_bld_tlv(struct basman_data *bd, struct lldp_agent *agent) { int rc = EPERM; if (!port_find_by_name(bd->ifname)) { rc = EEXIST; goto out_err; } if (basman_bld_portdesc_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:basman_bld_portdesc_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (basman_bld_sysname_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:basman_bld_sysname_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (basman_bld_sysdesc_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:basman_bld_sysdesc_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (basman_bld_syscaps_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:basman_bld_syscaps_tlv() failed\n", __func__, bd->ifname); goto out_err; } if (basman_bld_manaddr_tlv(bd, agent)) { LLDPAD_DBG("%s:%s:basman_bld_manaddr_tlv() failed\n", __func__, bd->ifname); goto out_err; } rc = 0; out_err: return rc; } static void basman_free_data(struct basman_user_data *bud) { struct basman_data *bd; if (bud) { while (!LIST_EMPTY(&bud->head)) { bd = LIST_FIRST(&bud->head); LIST_REMOVE(bd, entry); basman_free_tlv(bd); free(bd); } } } struct packed_tlv *basman_gettlv(struct port *port, struct lldp_agent *agent) { int i; int size; struct basman_data *bd; struct packed_tlv *ptlv = NULL; bd = basman_data(port->ifname, agent->type); if (!bd) goto out_err; /* free and rebuild the TLVs */ basman_free_tlv(bd); if (basman_bld_tlv(bd, agent)) { LLDPAD_DBG("%s:%s basman_bld_tlv failed\n", __func__, port->ifname); goto out_err; } size = TLVSIZE(bd->portdesc) + TLVSIZE(bd->sysname) + TLVSIZE(bd->sysdesc) + TLVSIZE(bd->syscaps); for (i = 0; i < bd->macnt; i++) size += TLVSIZE(bd->manaddr[i]); if (!size) goto out_err; ptlv = create_ptlv(); if (!ptlv) { LLDPAD_DBG("%s:%s malloc(ptlv) failed\n", __func__, port->ifname); goto out_err; } ptlv->tlv = malloc(size); if (!ptlv->tlv) goto out_free; /* pack all pre-built tlvs */ ptlv->size = 0; PACK_TLV_AFTER(bd->portdesc, ptlv, size, out_free); PACK_TLV_AFTER(bd->sysname, ptlv, size, out_free); PACK_TLV_AFTER(bd->sysdesc, ptlv, size, out_free); PACK_TLV_AFTER(bd->syscaps, ptlv, size, out_free); for (i = 0; i < bd->macnt; i++) PACK_TLV_AFTER(bd->manaddr[i], ptlv, size, out_free); return ptlv; out_free: ptlv = free_pkd_tlv(ptlv); out_err: LLDPAD_DBG("%s:%s: failed\n", __func__, port->ifname); return NULL; } void basman_ifdown(char *ifname, struct lldp_agent *agent) { struct basman_data *bd; bd = basman_data(ifname, agent->type); if (!bd) goto out_err; LIST_REMOVE(bd, entry); basman_free_tlv(bd); free(bd); LLDPAD_DBG("%s:port %s removed\n", __func__, ifname); return; out_err: LLDPAD_DBG("%s:port %s adding failed\n", __func__, ifname); return; } void basman_ifup(char *ifname, struct lldp_agent *agent) { struct basman_data *bd; struct basman_user_data *bud; bd = basman_data(ifname, agent->type); if (bd) { LLDPAD_DBG("%s:%s exists\n", __func__, ifname); goto out_err; } /* not found, alloc/init per-port tlv data */ bd = (struct basman_data *) malloc(sizeof(*bd)); if (!bd) { LLDPAD_DBG("%s:%s malloc %zu failed\n", __func__, ifname, sizeof(*bd)); goto out_err; } memset(bd, 0, sizeof(struct basman_data)); strncpy(bd->ifname, ifname, IFNAMSIZ); bd->agenttype = agent->type; if (basman_bld_tlv(bd, agent)) { LLDPAD_DBG("%s:%s mand_bld_tlv failed\n", __func__, ifname); free(bd); goto out_err; } bud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_BASIC); LIST_INSERT_HEAD(&bud->head, bd, entry); LLDPAD_DBG("%s:port %s added\n", __func__, ifname); return; out_err: LLDPAD_DBG("%s:port %s adding failed\n", __func__, ifname); return; } struct lldp_module *basman_register(void) { struct lldp_module *mod; struct basman_user_data *bud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("failed to malloc LLDP Basic Management module data\n"); goto out_err; } bud = malloc(sizeof(struct basman_user_data)); if (!bud) { free(mod); LLDPAD_ERR("failed to malloc LLDP Basic Management module user data\n"); goto out_err; } LIST_INIT(&bud->head); mod->id = LLDP_MOD_BASIC; mod->ops = &basman_ops; mod->data = bud; LLDPAD_DBG("%s:done\n", __func__); return mod; out_err: LLDPAD_DBG("%s:failed\n", __func__); return NULL; } void basman_unregister(struct lldp_module *mod) { if (mod->data) { basman_free_data((struct basman_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s:done\n", __func__); } lldpad-0.9.46/lldp_basman_clif.c000066400000000000000000000165061215142747300165050ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include "lldp_mod.h" #include "lldptool.h" #include "clif_msgs.h" #include "lldp_basman.h" #include "lldp_basman_clif.h" void print_string_tlv(u16, char *info); void print_capabilities(u16, char *info); void print_mng_addr(u16, char *info); u32 basman_lookup_tlv_name(char *tlvid_str); int basman_print_help(); static const struct lldp_mod_ops basman_ops_clif = { .lldp_mod_register = basman_cli_register, .lldp_mod_unregister = basman_cli_unregister, .print_tlv = basman_print_tlv, .lookup_tlv_name = basman_lookup_tlv_name, .print_help = basman_print_help, }; struct type_name_info basman_tlv_names[] = { { .type = PORT_DESCRIPTION_TLV, .name = "Port Description TLV", .key = "portDesc", .print_info = print_string_tlv, }, { .type = SYSTEM_NAME_TLV, .name = "System Name TLV", .key = "sysName", .print_info = print_string_tlv, }, { .type = SYSTEM_DESCRIPTION_TLV, .name = "System Description TLV", .key = "sysDesc", .print_info = print_string_tlv, }, { .type = SYSTEM_CAPABILITIES_TLV, .name = "System Capabilities TLV", .key = "sysCap", .print_info = print_capabilities, }, { .type = MANAGEMENT_ADDRESS_TLV, .name = "Management Address TLV", .key = "mngAddr", .print_info = print_mng_addr, }, { .type = INVALID_TLVID, } }; int basman_print_help() { struct type_name_info *tn = &basman_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *basman_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); goto out_err; } mod->id = LLDP_MOD_BASIC; mod->ops = &basman_ops_clif; return mod; out_err: return NULL; } void basman_cli_unregister(struct lldp_module *mod) { free(mod); } void print_string_tlv(u16 len, char *info) { int i; for (i = 0; i < 2*len; i+=2) printf("%c", hex2int(info+i)); printf("\n"); } void print_capability_list(u16 capabilities) { int print_comma = 0; if (capabilities & 0x01) { printf("Other"); print_comma = 1; } if (capabilities & 0x02) { printf("%sRepeater", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x04) { printf("%sBridge", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x08) { printf("%sWLAN Access Point", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x10) { printf("%sRouter", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x20) { printf("%sTelephone", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x40) { printf("%sDOCSIS cable device", (print_comma)?", ":""); print_comma = 1; } if (capabilities & 0x80) { printf("%sStation Only", (print_comma)?", ":""); print_comma = 1; } } void print_capabilities(u16 len, char *info) { u16 system_cap; u16 enabled_cap; if (len != 4) { printf("Bad System Capabilities TLV: %*.*s\n", 2*len, 2*len, info); return; } hexstr2bin(info, (u8 *)&system_cap, sizeof(system_cap)); system_cap = ntohs(system_cap); hexstr2bin(info+4, (u8 *)&enabled_cap, sizeof(enabled_cap)); enabled_cap = ntohs(enabled_cap); printf("System capabilities: "); print_capability_list(system_cap); printf("\n"); printf("\tEnabled capabilities: "); print_capability_list(enabled_cap); printf("\n"); } void print_mng_addr(u16 len, char *info) { u8 addrlen; u8 addrnum; u8 iftype; u8 oidlen; u32 ifnum; u32 offset; int i; char buf[132]; if (len < 9 || len > 167) { printf("Bad Management Address TLV: %*.*s\n", 2*len, 2*len, info); return; } hexstr2bin(info, (u8 *)&addrlen, sizeof(addrlen)); hexstr2bin(info+2, (u8 *)&addrnum, sizeof(addrnum)); switch(addrnum) { case MANADDR_ALL802: if (addrlen != 1 + 6) return; printf("MAC: "); for (i = 0; i < 12; i+=2) { printf("%2.2s", info + 4 + i); if (i < 10) printf(":"); else printf("\n"); } break; case MANADDR_IPV4: if (addrlen == 5) { struct in_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); inet_ntop(AF_INET, (void *)&addr, buf, sizeof(buf)); printf("IPv4: %s\n", buf); } else { printf("Bad IPv4: %*.*s\n", 2*(addrlen-2), 2*(addrlen-2), info+4); } break; case MANADDR_IPV6: if (addrlen == 17) { struct in6_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); memset(buf, 0, sizeof(buf)); inet_ntop(AF_INET6, (void *)&addr, buf, sizeof(buf)); printf("IPv6: %s\n", buf); } else { printf("Bad IPv6: %*.*s\n", 2*(addrlen-2), 2*(addrlen-2), info+4); } break; default: printf("Network Address Type %d: %*.*s\n", addrnum, 2*(addrlen-1), 2*(addrlen-1), info+4); break; } offset = 2*(1+addrlen); hexstr2bin(info+offset, (u8 *)&iftype, sizeof(iftype)); offset += 2; hexstr2bin(info+offset, (u8 *)&ifnum, sizeof(ifnum)); offset += 2*sizeof(u32); ifnum = ntohl(ifnum); switch (iftype) { case IFNUM_UNKNOWN: printf("\tUnknown interface subtype: "); break; case IFNUM_IFINDEX: printf("\tIfindex: "); break; case IFNUM_SYS_PORT_NUM: printf("\tSystem port number: "); break; default: printf("\tBad interface numbering subtype: "); break; } printf("%d\n", ifnum); hexstr2bin(info+offset, (u8 *)&oidlen, sizeof(oidlen)); offset += 2; if (oidlen && oidlen <= 128) { memset(buf, 0, sizeof(buf)); if (hexstr2bin(info+offset, (u8 *)&buf, oidlen)) printf("\tOID: Error parsing OID\n"); else printf("\tOID: %s\n", buf); } else if (oidlen > 128) { printf("\tOID: Invalid length = %d\n", oidlen); } } /* return 1: if it printed the TLV * 0: if it did not */ int basman_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &basman_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n\t", tn->name); if (tn->print_info) tn->print_info(len, info); return 1; } tn++; } return 0; } u32 basman_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &basman_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_basman_cmds.c000066400000000000000000000200321215142747300165030ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldpad.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_basman.h" #include "lldp_mand_clif.h" #include "lldp_basman_clif.h" #include "lldp/ports.h" #include "libconfig.h" #include "config.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/states.h" static int get_arg_ipv4(struct cmd *, char *, char *, char *, int); static int set_arg_ipv4(struct cmd *, char *, char *, char *, int); static int test_arg_ipv4(struct cmd *, char *, char *, char *, int); static int get_arg_ipv6(struct cmd *, char *, char *, char *, int); static int set_arg_ipv6(struct cmd *, char *, char *, char *, int); static int test_arg_ipv6(struct cmd *, char *, char *, char *, int); static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int test_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_IPV4_ADDR, .arg_class = TLV_ARG, .handle_get = get_arg_ipv4, .handle_set = set_arg_ipv4, .handle_test = test_arg_ipv4, }, { .arg = ARG_IPV6_ADDR, .arg_class = TLV_ARG, .handle_get = get_arg_ipv6, .handle_set = set_arg_ipv6, .handle_test = test_arg_ipv6, }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable }, { .arg = 0 } }; static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case PORT_DESCRIPTION_TLV: case SYSTEM_NAME_TLV: case SYSTEM_DESCRIPTION_TLV: case SYSTEM_CAPABILITIES_TLV: case MANAGEMENT_ADDRESS_TLV: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { int value; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case PORT_DESCRIPTION_TLV: case SYSTEM_NAME_TLV: case SYSTEM_DESCRIPTION_TLV: case SYSTEM_CAPABILITIES_TLV: case MANAGEMENT_ADDRESS_TLV: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) return cmd_failed; snprintf(obuf, obuf_len, "enableTx = %s\n", value ? "yes" : "no"); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, true); } struct arg_handlers *basman_get_arg_handlers() { return &arg_handlers[0]; } int get_arg_ipv4(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { const char *p = NULL; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; if (cmd->tlvid != MANAGEMENT_ADDRESS_TLV) return cmd_not_applicable; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &p, CONFIG_TYPE_STRING)) snprintf(obuf, obuf_len, "%02zx%s%04d", strlen(arg), arg, 0); else snprintf(obuf, obuf_len, "%02zx%s%04zx%s", strlen(arg), arg, strlen(p), p); return cmd_success; } int get_arg_ipv6(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { const char *p = NULL; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; if (cmd->tlvid != MANAGEMENT_ADDRESS_TLV) return cmd_not_applicable; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &p, CONFIG_TYPE_STRING)) snprintf(obuf, obuf_len, "%02zx%s%04d", strlen(arg), arg, 0); else snprintf(obuf, obuf_len, "%02zx%s%04zx%s", strlen(arg), arg, strlen(p), p); return cmd_success; } int _set_arg_ipv4(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { struct in_addr ipv4addr; char arg_path[256]; const char *p; if (cmd->cmd != cmd_settlv || cmd->tlvid != MANAGEMENT_ADDRESS_TLV) return cmd_bad_params; if (!inet_pton(AF_INET, argvalue, &ipv4addr)) return cmd_bad_params; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); p = &argvalue[0]; if (set_config_setting(cmd->ifname, cmd->type, arg_path, &p, CONFIG_TYPE_STRING)) return cmd_failed; snprintf(obuf, obuf_len, "ipv4 = %s\n", argvalue); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } int set_arg_ipv4(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_ipv4(cmd, arg, argvalue, obuf, obuf_len, false); } int test_arg_ipv4(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_ipv4(cmd, arg, argvalue, obuf, obuf_len, true); } int _set_arg_ipv6(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { struct in6_addr ipv6addr; char arg_path[256]; const char *p; if (cmd->cmd != cmd_settlv || cmd->tlvid != MANAGEMENT_ADDRESS_TLV) return cmd_bad_params; if (!inet_pton(AF_INET6, argvalue, &ipv6addr)) return cmd_bad_params; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); p = &argvalue[0]; if (set_config_setting(cmd->ifname, cmd->type, arg_path, &p, CONFIG_TYPE_STRING)) return cmd_failed; snprintf(obuf, obuf_len, "ipv6 = %s\n", argvalue); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } int set_arg_ipv6(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_ipv6(cmd, arg, argvalue, obuf, obuf_len, false); } int test_arg_ipv6(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_ipv6(cmd, arg, argvalue, obuf, obuf_len, false); } lldpad-0.9.46/lldp_cisco_clif.c000066400000000000000000000071341215142747300163410ustar00rootroot00000000000000/******************************************************************************* Implementation of Cisco Specific TLVs for LLDP (c) Copyright SuSE Linux Products GmbH, 2011 Author(s): Hannes Reinecke This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #include #include #include #include #include #include "lldp_mod.h" #include "clif_msgs.h" #include "lldp.h" #include "lldp_cisco_clif.h" static void cisco_print_upoe_tlv(u16 len, char *info); static int cisco_print_help(); static u32 cisco_lookup_tlv_name(char *tlvid_str); static const struct lldp_mod_ops cisco_ops_clif = { .lldp_mod_register = cisco_cli_register, .lldp_mod_unregister = cisco_cli_unregister, .print_tlv = cisco_print_tlv, .lookup_tlv_name = cisco_lookup_tlv_name, .print_help = cisco_print_help, }; struct type_name_info cisco_tlv_names[] = { { .type = (OUI_CISCO << 8) | 1, .name = "Cisco 4-wire Power-via-MDI TLV", .key = "uPoE", .print_info = cisco_print_upoe_tlv }, { .type = INVALID_TLVID, } }; static int cisco_print_help() { struct type_name_info *tn = &cisco_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *cisco_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); return NULL; } mod->id = OUI_IEEE_8021; mod->ops = &cisco_ops_clif; return mod; } void cisco_cli_unregister(struct lldp_module *mod) { free(mod); } static void cisco_print_upoe_tlv(u16 len, char *info) { u8 cap; if (len != 1) { printf("Bad uPoE TLV: %s\n", info); return; } if (!hexstr2bin(info, (u8 *)&cap, sizeof(cap))) { printf("4-Pair PoE %ssupported\n", cap & 0x01 ? "" : "not "); printf("\tSpare pair Detection/Classification %srequired\n", cap & 0x02 ? "" : "not "); printf("\tPD Spare pair Desired State: %s\n", cap & 0x04 ? "Enabled" : "Disabled"); printf("\tPSE Spare pair Operational State: %s\n", cap & 0x08 ? "Enabled" : "Disabled"); } } /* return 1: if it printed the TLV * 0: if it did not */ int cisco_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &cisco_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } static u32 cisco_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &cisco_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_dcbx.c000066400000000000000000000537741215142747300151770ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include "linux/if.h" #include "include/linux/dcbnl.h" #include "lldp.h" #include "lldp/ports.h" #include "lldp/states.h" #include "dcb_types.h" #include "lldp_dcbx.h" #include "dcb_protocol.h" #include "tlv_dcbx.h" #include "messages.h" #include "lldpad.h" #include "libconfig.h" #include "config.h" #include "lldpad_status.h" #include "lldp_mod.h" #include "lldp_mand_clif.h" #include "lldp_dcbx_nl.h" #include "lldp_dcbx_cfg.h" #include "lldp_dcbx_cmds.h" #include "lldp_8021qaz.h" #include "lldp_rtnl.h" #include "lldp_tlv.h" #include "lldp_rtnl.h" #include "lldpad_shm.h" #include "dcb_driver_interface.h" extern u8 gdcbx_subtype; void dcbx_free_tlv(struct dcbx_tlvs *tlvs); static int dcbx_check_operstate(struct port *port, struct lldp_agent *agent); const struct lldp_mod_ops dcbx_ops = { .lldp_mod_register = dcbx_register, .lldp_mod_unregister = dcbx_unregister, .lldp_mod_gettlv = dcbx_gettlv, .lldp_mod_rchange = dcbx_rchange, .lldp_mod_ifup = dcbx_ifup, .lldp_mod_ifdown = dcbx_ifdown, .lldp_mod_mibdelete = dcbx_mibDeleteObjects, .client_cmd = dcbx_clif_cmd, .get_arg_handler = dcbx_get_arg_handlers, .timer = dcbx_check_operstate, }; static int dcbx_check_operstate(struct port *port, struct lldp_agent *agent) { int err; u8 app_good = 0; u8 pfc_good = 0; app_attribs app_data; pfc_attribs pfc_data; struct dcbx_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) return 0; if (!port->portEnabled || !port->dormantDelay) return 0; tlvs = dcbx_data(port->ifname); if (!tlvs || tlvs->operup) return 0; err = get_app(port->ifname, 0, &app_data); if (err) goto err_out; err = get_pfc(port->ifname, &pfc_data); if (err) goto err_out; if ((pfc_data.protocol.Enable && pfc_data.protocol.OperMode) || !pfc_data.protocol.Enable) pfc_good = 1; if ((app_data.protocol.Enable && app_data.protocol.OperMode) || !app_data.protocol.Enable) app_good = 1; if ((pfc_good && app_good) || port->dormantDelay == 1) { LLDPAD_DBG("%s: %s: IF_OPER_UP delay, %u pfc oper %u" "app oper %u\n", __func__, port->ifname, port->dormantDelay, pfc_data.protocol.OperMode, app_data.protocol.OperMode); tlvs->operup = true; if (get_operstate(port->ifname) != IF_OPER_UP) set_operstate(port->ifname, IF_OPER_UP); else set_hw_all(port->ifname); } return 0; err_out: set_operstate(port->ifname, IF_OPER_UP); return -1; } struct dcbx_tlvs *dcbx_data(const char *ifname) { struct dcbd_user_data *dud; struct dcbx_tlvs *tlv = NULL; dud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_DCBX); if (dud) { LIST_FOREACH(tlv, &dud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return tlv; } } return NULL; } int dcbx_tlvs_rxed(const char *ifname, struct lldp_agent *agent) { struct dcbd_user_data *dud; struct dcbx_tlvs *tlv = NULL; if (agent->type != NEAREST_BRIDGE) return 0; dud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_DCBX); if (dud) { LIST_FOREACH(tlv, &dud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return tlv->rxed_tlvs; } } return 0; } int dcbx_get_legacy_version(const char *ifname) { if (lldpad_shm_get_dcbx(ifname)) return gdcbx_subtype | DCBX_FORCE_BIT; else return gdcbx_subtype; } int dcbx_check_active(const char *ifname) { struct dcbd_user_data *dud; struct dcbx_tlvs *tlv = NULL; dud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_DCBX); if (dud) { LIST_FOREACH(tlv, &dud->head, entry) { if (!strncmp(tlv->ifname, ifname, IFNAMSIZ)) return tlv->active; } } return 0; } int dcbx_bld_tlv(struct port *newport, struct lldp_agent *agent) { bool success; struct dcbx_tlvs *tlvs; int enabletx; int adminstatus = disabled; if (agent->type != NEAREST_BRIDGE) return 0; tlvs = dcbx_data(newport->ifname); get_config_setting(newport->ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT); enabletx = is_tlv_txenabled(newport->ifname, agent->type, (OUI_CEE_DCBX << 8) | tlvs->dcbx_st); if (!tlvs->active || !enabletx || adminstatus != enabledRxTx) return 0; tlvs->control = bld_dcbx_ctrl_tlv(tlvs); if (tlvs->control == NULL) { LLDPAD_INFO("add_port: bld_dcbx_ctrl_tlv failed\n"); goto fail_add; } if (tlvs->dcbx_st == dcbx_subtype2) { tlvs->pg2 = bld_dcbx2_pg_tlv(tlvs, &success); if (!success) { LLDPAD_INFO("bld_dcbx2_pg_tlv: failed\n"); goto fail_add; } } else { tlvs->pg1 = bld_dcbx1_pg_tlv(tlvs, &success); if (!success) { LLDPAD_INFO("bld_dcbx1_pg_tlv: failed\n"); goto fail_add; } } if (tlvs->dcbx_st == dcbx_subtype2) { tlvs->pfc2 = bld_dcbx2_pfc_tlv(tlvs, &success); if (!success) { LLDPAD_INFO("bld_dcbx2_pfc_tlv: failed\n"); goto fail_add; } } else { tlvs->pfc1 = bld_dcbx1_pfc_tlv(tlvs, &success); if (!success) { LLDPAD_INFO("bld_dcbx1_pfc_tlv: failed\n"); goto fail_add; } } if (tlvs->dcbx_st == dcbx_subtype2) { tlvs->app2 = bld_dcbx2_app_tlv(tlvs, &success); if (!success) { LLDPAD_INFO("bld_dcbx2_app_tlv: failed\n"); goto fail_add; } } else { tlvs->app1 = bld_dcbx1_app_tlv(tlvs, 0, &success); if (!success) { LLDPAD_INFO("bld_dcbx1_app_tlv: failed\n"); goto fail_add; } } tlvs->llink = bld_dcbx_llink_tlv(tlvs, LLINK_FCOE_STYPE, &success); if (!success) { LLDPAD_INFO("bld_dcbx_llink_tlv: failed\n"); goto fail_add; } if (tlvs->dcbx_st == dcbx_subtype2) { tlvs->dcbx2 = bld_dcbx2_tlv(tlvs); if (tlvs->dcbx2 == NULL) { LLDPAD_INFO("add_port: bld_dcbx2_tlv failed\n"); goto fail_add; } } else { tlvs->dcbx1 = bld_dcbx1_tlv(tlvs); if (tlvs->dcbx1 == NULL) { LLDPAD_INFO("add_port: bld_dcbx1_tlv failed\n"); goto fail_add; } } return 0; fail_add: return -1; } void dcbx_free_manifest(struct dcbx_manifest *manifest) { if (!manifest) return; if (manifest->dcbx1) manifest->dcbx1 = free_unpkd_tlv(manifest->dcbx1); if (manifest->dcbx2) manifest->dcbx2 = free_unpkd_tlv(manifest->dcbx2); if (manifest->dcbx_ctrl) manifest->dcbx_ctrl = free_unpkd_tlv(manifest->dcbx_ctrl); if (manifest->dcbx_pg) manifest->dcbx_pg = free_unpkd_tlv(manifest->dcbx_pg); if (manifest->dcbx_pfc) manifest->dcbx_pfc = free_unpkd_tlv(manifest->dcbx_pfc); if (manifest->dcbx_app) manifest->dcbx_app = free_unpkd_tlv(manifest->dcbx_app); if (manifest->dcbx_llink) manifest->dcbx_llink = free_unpkd_tlv(manifest->dcbx_llink); return; } void dcbx_free_tlv(struct dcbx_tlvs *tlvs) { if (!tlvs) return; if (tlvs->control != NULL) { tlvs->control = free_unpkd_tlv(tlvs->control); } if (tlvs->pg1 != NULL) { tlvs->pg1 = free_unpkd_tlv(tlvs->pg1); } if (tlvs->pg2 != NULL) { tlvs->pg2 = free_unpkd_tlv(tlvs->pg2); } if (tlvs->pfc1 != NULL) { tlvs->pfc1 = free_unpkd_tlv(tlvs->pfc1); } if (tlvs->pfc2 != NULL) { tlvs->pfc2 = free_unpkd_tlv(tlvs->pfc2); } if (tlvs->app1 != NULL) { tlvs->app1 = free_unpkd_tlv(tlvs->app1); } if (tlvs->app2 != NULL) tlvs->app2 = free_unpkd_tlv(tlvs->app2); if (tlvs->llink != NULL) { tlvs->llink = free_unpkd_tlv(tlvs->llink); } if (tlvs->dcbx1 != NULL) { tlvs->dcbx1 = free_unpkd_tlv(tlvs->dcbx1); } if (tlvs->dcbx2 != NULL) { tlvs->dcbx2 = free_unpkd_tlv(tlvs->dcbx2); } return; } struct packed_tlv* dcbx_gettlv(struct port *port, struct lldp_agent *agent) { struct packed_tlv *ptlv = NULL; struct dcbx_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) return NULL; if (!check_port_dcb_mode(port->ifname)) return NULL; tlvs = dcbx_data(port->ifname); if (!tlvs) return NULL; dcbx_free_tlv(tlvs); dcbx_bld_tlv(port, agent); if (tlvs->dcbx_st == dcbx_subtype2) { /* Load Type127 - dcbx subtype 2*/ if (tlv_ok(tlvs->dcbx2)) ptlv = pack_tlv(tlvs->dcbx2); } else { /* Load Type127 - dcbx subtype1 */ if (tlv_ok(tlvs->dcbx1)) ptlv = pack_tlv(tlvs->dcbx1); } return ptlv; } static void dcbx_free_data(struct dcbd_user_data *dud) { struct dcbx_tlvs *dd; if (dud) { while (!LIST_EMPTY(&dud->head)) { dd = LIST_FIRST(&dud->head); LIST_REMOVE(dd, entry); dcbx_free_tlv(dd); dcbx_free_manifest(dd->manifest); free(dd->manifest); free(dd); } } } struct lldp_module * dcbx_register(void) { struct lldp_module *mod; struct dcbd_user_data *dud; int dcbx_version; int i; if (dcbx_default_cfg_file()) { LLDPAD_INFO("failed to create default config file\n"); goto out_err; } /* Get the DCBX version */ if (get_dcbx_version(&dcbx_version)) { gdcbx_subtype = dcbx_version; } else { LLDPAD_ERR("failed to get DCBX version"); goto out_err; } mod = malloc(sizeof(struct lldp_module)); if (!mod) { LLDPAD_ERR("failed to malloc LLDP DCBX module data\n"); goto out_err; } dud = malloc(sizeof(*dud)); if (!dud) { free(mod); LLDPAD_ERR("failed to malloc LLDP DCBX module user data\n"); goto out_err; } LIST_INIT(&dud->head); mod->id = LLDP_MOD_DCBX; mod->ops = &dcbx_ops; mod->data = dud; /* store pg defaults */ if (!add_pg_defaults()) { LLDPAD_INFO("failed to add default PG data"); goto out_err; } /* store pg defaults */ if (!add_pfc_defaults()) { LLDPAD_INFO("failed to add default PFC data"); goto out_err; } /* store app defaults */ for (i = 0; i < DCB_MAX_APPTLV; i++) { if (!add_app_defaults(i)) { LLDPAD_INFO("failed to add default APP data %d", i); goto out_err; } } for (i = 0; i < DCB_MAX_LLKTLV; i++) { if (!add_llink_defaults(i)) { LLDPAD_INFO("failed to add default LLKTLV data %i", i); goto out_err; } } if (!init_drv_if()) { LLDPAD_WARN("Error creataing netlink socket for driver i/f.\n"); goto out_err; } LLDPAD_DBG("%s: dcbx register done\n", __func__); return mod; out_err: LLDPAD_DBG("%s: dcbx register failed\n", __func__); return NULL; } /* BUG: need to check if tlvs are freed */ void dcbx_unregister(struct lldp_module *mod) { dcbx_remove_all(); deinit_drv_if(); if (mod->data) { dcbx_free_data((struct dcbd_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s: unregister dcbx complete.\n", __func__); } void dcbx_ifup(char *ifname, struct lldp_agent *agent) { struct port *port = NULL; struct dcbx_tlvs *tlvs; struct dcbd_user_data *dud; struct dcbx_manifest *manifest; feature_support dcb_support; int dcb_enable, exists; int adminstatus; int enabletx; char arg_path[256]; /* dcb does not support bonded devices */ if (is_bond(ifname) || is_vlan(ifname)) return; if (agent->type != NEAREST_BRIDGE) return; port = port_find_by_name(ifname); dud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_DCBX); tlvs = dcbx_data(ifname); if (!port) return; else if (tlvs) goto initialized; /* Abort initialization on hardware that does not support * querying the DCB state. We assume this means the driver * does not support DCB. * * dcb_enable falls through and makes CEE active if * it is already enabled AND configuration file does not * have an entry for the dcb_enable field. */ if (get_hw_state(ifname, &dcb_enable) < 0) return; /* Abort initialization on hardware where DCBX negotiation * is not performed by the host LLDP agent. */ get_dcb_capabilities(ifname, &dcb_support); if (dcb_support.dcbx && !(dcb_support.dcbx & DCB_CAP_DCBX_HOST)) return; /* if no adminStatus setting or wrong setting for adminStatus, * then set adminStatus to enabledRxTx. */ if (get_config_setting(ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT) || adminstatus == enabledTxOnly || adminstatus == enabledRxOnly) { /* set enableTx to true if it is not already set */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, (OUI_CEE_DCBX << 8) | 1, ARG_TLVTXENABLE); if (get_config_setting(ifname, agent->type, arg_path, &enabletx, CONFIG_TYPE_BOOL)) { enabletx = true; set_config_setting(ifname, agent->type, arg_path, &enabletx, CONFIG_TYPE_BOOL); } snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, (OUI_CEE_DCBX << 8) | 2, ARG_TLVTXENABLE); if (get_config_setting(ifname, agent->type, arg_path, &enabletx, CONFIG_TYPE_BOOL)) { enabletx = true; set_config_setting(ifname, agent->type, arg_path, &enabletx, CONFIG_TYPE_BOOL); } adminstatus = enabledRxTx; if (set_config_setting(ifname, agent->type, ARG_ADMINSTATUS, &adminstatus, CONFIG_TYPE_INT) == cmd_success) set_lldp_agent_admin(ifname, agent->type, adminstatus); /* ensure advertise bits are set consistently with enabletx */ dont_advertise_dcbx_all(ifname, 1); } tlvs = malloc(sizeof(*tlvs)); if (!tlvs) { LLDPAD_DBG("%s: ifname %s malloc failed.\n", __func__, ifname); return; } memset(tlvs, 0, sizeof(*tlvs)); manifest = malloc(sizeof(*manifest)); if (!manifest) { free(tlvs); LLDPAD_DBG("%s: %s malloc failure\n", __func__, ifname); return; } memset(manifest, 0, sizeof(*manifest)); tlvs->manifest = manifest; strncpy(tlvs->ifname, ifname, IFNAMSIZ); tlvs->port = port; tlvs->dcbdu = 0; tlvs->dcbx_st = gdcbx_subtype & MASK_DCBX_FORCE; LIST_INSERT_HEAD(&dud->head, tlvs, entry); initialized: if (!port->portEnabled || port->dormantDelay) tlvs->operup = false; dcbx_add_adapter(ifname); /* ensure advertise bits are set consistently with enabletx */ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, (OUI_CEE_DCBX << 8) | tlvs->dcbx_st, ARG_TLVTXENABLE); exists = get_config_setting(ifname, agent->type, arg_path, &enabletx, CONFIG_TYPE_BOOL); if (exists != cmd_success) dont_advertise_dcbx_all(ifname, 1); dcbx_bld_tlv(port, agent); /* if the dcbx field is not filled in by the capabilities * query, then the kernel is older and does not support * IEEE mode, lacking any specified behavior in the cfg * file DCBX is put in a default mode and will be enabled * if a peer DCBX TLV is received. */ get_dcb_capabilities(ifname, &dcb_support); get_dcb_enable_state(ifname, &dcb_enable); if ((dcb_enable != LLDP_DCBX_DISABLED) && (!dcb_support.dcbx || (gdcbx_subtype & ~MASK_DCBX_FORCE) || (lldpad_shm_get_dcbx(ifname)))) { set_dcbx_mode(tlvs->ifname, DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE); set_hw_state(ifname, 1); lldpad_shm_set_dcbx(ifname, gdcbx_subtype); tlvs->active = true; } else { tlvs->active = false; } if (tlvs->active && (get_operstate(ifname) == IF_OPER_UP)) set_hw_all(ifname); return; } void dcbx_ifdown(char *device_name, struct lldp_agent *agent) { struct port *port = NULL; struct dcbx_tlvs *tlvs; if (agent->type != NEAREST_BRIDGE) return; /* dcb does not support bonded devices */ if (is_bond(device_name)) return; port = porthead; while (port != NULL) { if (!strncmp(device_name, port->ifname, MAX_DEVICE_NAME_LEN)) break; port = port->next; } tlvs = dcbx_data(device_name); if (!tlvs) return; /* remove dcb port */ if (check_port_dcb_mode(device_name)) { dcbx_remove_adapter(device_name); } if (tlvs) { LIST_REMOVE(tlvs, entry); dcbx_free_tlv(tlvs); dcbx_free_manifest(tlvs->manifest); free(tlvs->manifest); free(tlvs); } } void clear_dcbx_manifest(struct dcbx_tlvs *dcbx) { if (!dcbx) return; if (dcbx->manifest->dcbx_llink) dcbx->manifest->dcbx_llink = free_unpkd_tlv(dcbx->manifest->dcbx_llink); if (dcbx->manifest->dcbx_app) dcbx->manifest->dcbx_app = free_unpkd_tlv(dcbx->manifest->dcbx_app); if (dcbx->manifest->dcbx_pfc) dcbx->manifest->dcbx_pfc = free_unpkd_tlv(dcbx->manifest->dcbx_pfc); if (dcbx->manifest->dcbx_pg) dcbx->manifest->dcbx_pg = free_unpkd_tlv(dcbx->manifest->dcbx_pg); if (dcbx->manifest->dcbx_ctrl) dcbx->manifest->dcbx_ctrl = free_unpkd_tlv(dcbx->manifest->dcbx_ctrl); if (dcbx->manifest->dcbx1) dcbx->manifest->dcbx1 = free_unpkd_tlv(dcbx->manifest->dcbx1); if (dcbx->manifest->dcbx2) dcbx->manifest->dcbx2 = free_unpkd_tlv(dcbx->manifest->dcbx2); free(dcbx->manifest); dcbx->manifest = NULL; } /* * dcbx_rchange: process RX TLV LLDPDU * * TLV not consumed on error otherwise it is either free'd or stored * internally in the module. */ int dcbx_rchange(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { u8 oui[DCB_OUI_LEN] = INIT_DCB_OUI; struct dcbx_tlvs *dcbx; struct dcbx_manifest *manifest; int res; if (agent == NULL || agent->type != NEAREST_BRIDGE) return SUBTYPE_INVALID; dcbx = dcbx_data(port->ifname); if (!dcbx) return SUBTYPE_INVALID; /* * TYPE_1 is _mandatory_ and will always be before the * DCBX TLV so we can use it to mark the begining of a * pdu for dcbx to verify only a single DCBX TLV is * present */ if (tlv->type == TYPE_1) { manifest = malloc(sizeof(*manifest)); memset(manifest, 0, sizeof(*manifest)); dcbx->manifest = manifest; dcbx->dcbdu = 0; dcbx->rxed_tlvs = false; } if (tlv->type == TYPE_127) { if (tlv->length < (DCB_OUI_LEN + OUI_SUBTYPE_LEN)) { return TLV_ERR; } /* Match DCB OUI */ if ((memcmp(tlv->info, &oui, DCB_OUI_LEN) != 0)) return SUBTYPE_INVALID; if ((tlv->info[DCB_OUI_LEN] == dcbx_subtype2) && (agent->lldpdu & RCVD_LLDP_DCBX2_TLV)) { LLDPAD_INFO("Received duplicate DCBX2 TLVs\n"); return TLV_ERR; } if ((tlv->info[DCB_OUI_LEN] == dcbx_subtype1) && (agent->lldpdu & RCVD_LLDP_DCBX1_TLV)) { LLDPAD_INFO("Received duplicate DCBX1 TLVs\n"); return TLV_ERR; } /* Only store a legacy (CIN or CEE) DCBX TLV which matches * the currently configured legacy dcbx mode. * However, capture if any legacy DCBX TLVs are recieved. */ if (tlv->info[DCB_OUI_LEN] == dcbx_subtype2) { if (dcbx->dcbx_st == dcbx_subtype2) dcbx->manifest->dcbx2 = tlv; agent->lldpdu |= RCVD_LLDP_DCBX2_TLV; dcbx->rxed_tlvs = true; return TLV_OK; } else if (tlv->info[DCB_OUI_LEN] == dcbx_subtype1) { if (dcbx->dcbx_st == dcbx_subtype1) dcbx->manifest->dcbx1 = tlv; agent->lldpdu |= RCVD_LLDP_DCBX1_TLV; dcbx->rxed_tlvs = true; return TLV_OK; } else { return SUBTYPE_INVALID; } } if (tlv->type == TYPE_0) { int enabled; int not_present = get_dcb_enable_state(dcbx->ifname, &enabled); if (!dcbx->active && !ieee8021qaz_tlvs_rxed(dcbx->ifname) && dcbx->rxed_tlvs && (not_present || enabled)) { if (dcbx->dcbx_st == dcbx_subtype2) LLDPAD_DBG("CEE DCBX %s going ACTIVE\n", dcbx->ifname); else if (dcbx->dcbx_st == dcbx_subtype1) LLDPAD_DBG("CIN DCBX %s going ACTIVE\n", dcbx->ifname); set_dcbx_mode(port->ifname, DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE); set_hw_state(port->ifname, 1); lldpad_shm_set_dcbx(dcbx->ifname, gdcbx_subtype); dcbx->active = true; somethingChangedLocal(port->ifname, agent->type); } if (dcbx->manifest->dcbx2) { res = unpack_dcbx2_tlvs(port, agent, dcbx->manifest->dcbx2); if (!res) { LLDPAD_DBG("Error unpacking the DCBX2" "TLVs - Discarding LLDPDU\n"); return TLV_ERR; } mibUpdateObjects(port, agent); } else if (dcbx->manifest->dcbx1) { res = unpack_dcbx1_tlvs(port, agent, dcbx->manifest->dcbx1); if (!res) { LLDPAD_DBG("Error unpacking the DCBX1" "TLVs - Discarding LLDPDU\n"); return TLV_ERR; } mibUpdateObjects(port, agent); } clear_dcbx_manifest(dcbx); } /* TLV is not handled by DCBx module return invalid to * indicate lldpad core should continue to look for * a module to handle this TLV */ return SUBTYPE_INVALID; } u8 dcbx_mibDeleteObjects(struct port *port, struct lldp_agent *agent) { control_protocol_attribs peer_control; pg_attribs peer_pg; pfc_attribs peer_pfc; app_attribs peer_app; llink_attribs peer_llink; u32 subtype = 0; u32 EventFlag = 0; int i; if (agent->type != NEAREST_BRIDGE) return 0; /* Set any stored values for this TLV to !Present */ if (get_peer_pg(port->ifname, &peer_pg) == cmd_success) { if (peer_pg.protocol.TLVPresent == true) { peer_pg.protocol.TLVPresent = false; put_peer_pg(port->ifname, &peer_pg); DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG); } } else { return (u8)-1; } if (get_peer_pfc(port->ifname, &peer_pfc) == cmd_success) { if (peer_pfc.protocol.TLVPresent == true) { peer_pfc.protocol.TLVPresent = false; put_peer_pfc(port->ifname, &peer_pfc); DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC); } } else { return (u8)-1; } for (i = 0; i < DCB_MAX_APPTLV; i++) { if (get_peer_app(port->ifname, i, &peer_app) == cmd_success) { if (peer_app.protocol.TLVPresent == true) { peer_app.protocol.TLVPresent = false; peer_app.Length = 0; put_peer_app(port->ifname, i, &peer_app); DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(i)); } } } if (get_peer_llink(port->ifname, subtype, &peer_llink) == cmd_success) { if (peer_llink.protocol.TLVPresent == true) { peer_llink.protocol.TLVPresent = false; put_peer_llink(port->ifname, subtype, &peer_llink); DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK); } } else { return (u8)-1; } if (get_peer_control(port->ifname, &peer_control) == cmd_success) { peer_control.RxDCBTLVState = DCB_PEER_EXPIRED; put_peer_control(port->ifname, &peer_control); } else { return (u8)-1; } if (EventFlag != 0) { /* process for all subtypes */ run_dcb_protocol(port->ifname, EventFlag, DCB_MAX_APPTLV+1); EventFlag = 0; } return 0; } lldpad-0.9.46/lldp_dcbx_cfg.c000066400000000000000000000735271215142747300160140ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include "lldp_dcbx_cfg.h" #include "dcb_protocol.h" #include "messages.h" #include "lldpad.h" #include "config.h" #include "libconfig.h" #include "lldp_util.h" #include "dcb_driver_interface.h" #define CFG_VERSION "1.0" extern config_t lldpad_cfg; static void config_setting_add_app_default(config_setting_t *eth_setting, int subtype) { config_setting_t *tmp_setting = NULL; config_setting_t *tmp2_setting = NULL; char abuf[32]; sprintf(abuf, "app_%d", subtype); tmp_setting = config_setting_add(eth_setting, abuf, CONFIG_TYPE_GROUP); if (!tmp_setting) goto error; tmp2_setting = config_setting_add(tmp_setting, "app_enable", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 1)) goto error; tmp2_setting = config_setting_add(tmp_setting, "app_advertise", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 1)) goto error; tmp2_setting = config_setting_add(tmp_setting, "app_willing", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 1)) goto error; tmp2_setting = config_setting_add(tmp_setting, "app_cfg", CONFIG_TYPE_STRING); if (!tmp2_setting) goto error; if (subtype == APP_FCOE_STYPE) snprintf(abuf, sizeof(abuf), "%02x", APP_FCOE_DEFAULT_DATA); else if (subtype == APP_ISCSI_STYPE) snprintf(abuf, sizeof(abuf), "%02x", APP_ISCSI_DEFAULT_DATA); else if (subtype == APP_FIP_STYPE) snprintf(abuf, sizeof(abuf), "%02x", APP_FIP_DEFAULT_DATA); else snprintf(abuf, sizeof(abuf), "%02x", 0x03); if (!config_setting_set_string(tmp2_setting, abuf)) goto error; /* Success */ return; error: LLDPAD_DBG("Failed setting defaults for app subtype %d", subtype); } static config_setting_t *construct_new_setting(char *device_name) { config_setting_t *dcbx_setting = NULL; config_setting_t *eth_setting = NULL; config_setting_t *tmp_setting = NULL; config_setting_t *tmp2_setting = NULL; char abuf[32]; int i; dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); if (!dcbx_setting) return NULL; eth_setting = config_setting_add(dcbx_setting, device_name, CONFIG_TYPE_GROUP); if (!eth_setting) goto set_error; tmp_setting = config_setting_add(eth_setting, "dcb_enable", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pfc_enable", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pfc_advertise", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pfc_willing", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pfc_admin_mode", CONFIG_TYPE_ARRAY); if (!tmp_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp_setting, -1, 0)) goto set_error; } tmp_setting = config_setting_add(eth_setting, "pg_enable", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pg_advertise", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pg_willing", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, 1)) goto set_error; tmp_setting = config_setting_add(eth_setting, "pg_tx_bwg_alloc", CONFIG_TYPE_ARRAY); if (!tmp_setting) goto set_error; for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { if (!config_setting_set_int_elem(tmp_setting, -1, 0)) goto set_error; } tmp_setting = config_setting_add(eth_setting, "pg_tx_traffic_attribs_type", CONFIG_TYPE_GROUP); if (!tmp_setting) goto set_error; tmp2_setting = config_setting_add(tmp_setting, "traffic_class_mapping", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, i)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "bandwidth_group_mapping", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, 0)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "percent_of_bandwidth_group", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, 0)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "strict_priority", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, 0)) goto set_error; } tmp_setting = config_setting_add(eth_setting, "pg_rx_bwg_alloc", CONFIG_TYPE_ARRAY); if (!tmp_setting) goto set_error; for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { if (!config_setting_set_int_elem(tmp_setting, -1, 0)) goto set_error; } tmp_setting = config_setting_add(eth_setting, "pg_rx_traffic_attribs_type", CONFIG_TYPE_GROUP); if (!tmp_setting) goto set_error; tmp2_setting = config_setting_add(tmp_setting, "traffic_class_mapping", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, i)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "bandwidth_group_mapping", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, i)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "percent_of_bandwidth_group", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, i)) goto set_error; } tmp2_setting = config_setting_add(tmp_setting, "strict_priority", CONFIG_TYPE_ARRAY); if (!tmp2_setting) goto set_error; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (!config_setting_set_int_elem(tmp2_setting, -1, i)) goto set_error; } tmp_setting = config_setting_add(eth_setting, "bwg_description", CONFIG_TYPE_ARRAY); if (!tmp_setting) goto set_error; for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { if (!config_setting_set_string_elem(tmp_setting, -1, "")) goto set_error; } for (i = 0; i < DCB_MAX_APPTLV; i++) config_setting_add_app_default(eth_setting, i); for (i = 0; i < DCB_MAX_LLKTLV; i++) { sprintf(abuf, "llink_%d", i); tmp_setting = config_setting_add(eth_setting, abuf, CONFIG_TYPE_GROUP); if (!tmp_setting) goto set_error; tmp2_setting = config_setting_add(tmp_setting, "llink_enable", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 0)) goto set_error; tmp2_setting = config_setting_add(tmp_setting, "llink_advertise", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 0)) goto set_error; tmp2_setting = config_setting_add(tmp_setting, "llink_willing", CONFIG_TYPE_INT); if (!tmp2_setting || !config_setting_set_int(tmp2_setting, 0)) goto set_error; } return eth_setting; set_error: LLDPAD_DBG("Failed constructing new settings for config file %s", cfg_file_name); return NULL; } int dcbx_default_cfg_file(void) { config_setting_t *root_setting = NULL; config_setting_t *dcbx_setting = NULL; config_setting_t *tmp_setting = NULL; /* add the legacy default dcbx configuration settings */ root_setting = config_root_setting(&lldpad_cfg); dcbx_setting = config_setting_add(root_setting, DCBX_SETTING, CONFIG_TYPE_GROUP); /* dcbx configuration exists abort creating cfg */ if (!dcbx_setting) return 0; tmp_setting = config_setting_add(dcbx_setting, "version", CONFIG_TYPE_STRING); if (!tmp_setting || !config_setting_set_string(tmp_setting, CFG_VERSION)) goto error; tmp_setting = config_setting_add(dcbx_setting, "dcbx_version", CONFIG_TYPE_INT); if (!tmp_setting || !config_setting_set_int(tmp_setting, dcbx_subtype2)) goto error; config_write_file(&lldpad_cfg, cfg_file_name); return 0; error: LLDPAD_DBG("Failed to generate default config file %s", cfg_file_name); return 1; } void cfg_fixup(config_setting_t *eth_settings) { config_setting_t *setting = NULL; char abuf[2*DCB_MAX_TLV_LENGTH + 1]; int i; setting = config_setting_get_member(eth_settings, "app_0"); /* * If there's no APP 0 entry then there's no need to create any others */ if (!setting) return; for (i = 1; i < DCB_MAX_APPTLV; i++) { sprintf(abuf, "app_%d", i); setting = config_setting_get_member(eth_settings, abuf); /* * Entry exists, go to the next one */ if (setting) continue; config_setting_add_app_default(eth_settings, i); } return; } static int _set_persistent(char *device_name, int dcb_enable, pg_attribs *pg, pfc_attribs *pfc, pg_info *bwg, app_attribs *app, u8 app_subtype, llink_attribs *llink, u8 link_subtype) { full_dcb_attribs attribs; config_setting_t *dcbx_setting = NULL; config_setting_t *eth_settings = NULL; config_setting_t *setting = NULL; config_setting_t *setting_traffic = NULL; config_setting_t *setting_value = NULL; char abuf[2*DCB_MAX_TLV_LENGTH + 1]; int result, i; dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); if (dcbx_setting) eth_settings = config_setting_get_member(dcbx_setting, device_name); /* init the internal data store for device_name */ if (NULL == eth_settings) { result = cmd_success; if (result == cmd_success && pg == NULL) { result = get_pg(device_name, &attribs.pg); pg = &attribs.pg; } if (result == cmd_success && pfc == NULL) { result = get_pfc(device_name, &attribs.pfc); pfc = &attribs.pfc; } if (result == cmd_success && app == NULL) { result = get_app(device_name,app_subtype,&attribs.app[app_subtype]); app = &attribs.app[app_subtype]; } if (result == cmd_success && llink == NULL) { result = get_llink(device_name,0,&attribs.llink[0]); llink = &attribs.llink[0]; } if (result != cmd_success) goto set_error; eth_settings = construct_new_setting(device_name); if (!eth_settings) goto set_error; } setting = config_setting_get_member(eth_settings, "dcb_enable"); if (!setting || !config_setting_set_int(setting, dcb_enable)) goto set_error; if (NULL != pfc) { setting = config_setting_get_member(eth_settings, "pfc_enable"); if (!setting || !config_setting_set_int(setting, pfc->protocol.Enable)) goto set_error; setting = config_setting_get_member(eth_settings, "pfc_advertise"); if (!setting || !config_setting_set_int(setting, pfc->protocol.Advertise)) goto set_error; setting = config_setting_get_member(eth_settings, "pfc_willing"); if (!setting || !config_setting_set_int(setting, pfc->protocol.Willing)) goto set_error; setting = config_setting_get_member(eth_settings, "pfc_admin_mode"); if (!setting) { goto set_error; } else { for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) { goto set_error; } if (!config_setting_set_int_elem(setting, i, pfc->admin[i])) { goto set_error; } } } } /* Read PG setting */ if (NULL != pg) { setting = config_setting_get_member(eth_settings, "pg_enable"); if (!setting || !config_setting_set_int(setting, pg->protocol.Enable)) goto set_error; setting = config_setting_get_member(eth_settings, "pg_advertise"); if (!setting || !config_setting_set_int(setting, pg->protocol.Advertise)) goto set_error; setting = config_setting_get_member(eth_settings, "pg_willing"); if (!setting || !config_setting_set_int(setting, pg->protocol.Willing)) goto set_error; setting = config_setting_get_member(eth_settings, "pg_tx_bwg_alloc"); if (!setting) { goto set_error; } else { for (i = 0; itx.pg_percent[i])) goto set_error; } } setting_traffic = config_setting_get_member(eth_settings, "pg_tx_traffic_attribs_type"); if (!setting_traffic) { goto set_error; } else { setting = config_setting_get_member(setting_traffic, "traffic_class_mapping"); if (!setting) goto set_error; for (i = 0; itx.up[i].pgid)) goto set_error; } setting = config_setting_get_member(setting_traffic, "bandwidth_group_mapping"); if (!setting) goto set_error; for (i = 0; itx.up[i].bwgid)) goto set_error; } setting = config_setting_get_member(setting_traffic, "percent_of_bandwidth_group"); for (i = 0; itx.up[i].percent_of_pg_cap)) goto set_error; } setting = config_setting_get_member(setting_traffic, "strict_priority"); if (!setting) goto set_error; for (i = 0; itx.up[i].strict_priority)) goto set_error; } } /* rx */ setting = config_setting_get_member(eth_settings, "pg_rx_bwg_alloc"); if (!setting) { goto set_error; } else { for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) goto set_error; if (!config_setting_set_int_elem(setting, i, pg->rx.pg_percent[i])) goto set_error; } } setting_traffic = config_setting_get_member(eth_settings, "pg_rx_traffic_attribs_type"); if (!setting_traffic) { goto set_error; } else { setting = config_setting_get_member(setting_traffic, "traffic_class_mapping"); if (!setting) goto set_error; for (i = 0; irx.up[i].pgid)) goto set_error; } setting = config_setting_get_member(setting_traffic, "bandwidth_group_mapping"); if (!setting) goto set_error; for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) goto set_error; if (!config_setting_set_int_elem(setting, i, pg->rx.up[i].bwgid)) goto set_error; } setting = config_setting_get_member(setting_traffic, "percent_of_bandwidth_group"); if (!setting) goto set_error; for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) goto set_error; if (!config_setting_set_int_elem(setting, i, pg->rx.up[i].percent_of_pg_cap)) goto set_error; } setting = config_setting_get_member(setting_traffic, "strict_priority"); if (!setting) goto set_error; for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) goto set_error; if (!config_setting_set_int_elem(setting, i, pg->rx.up[i].strict_priority)) goto set_error; } } } if (NULL != bwg) { setting = config_setting_get_member(eth_settings, "bwg_description"); if (!setting) { goto set_error; } else { for (i = 0; i < config_setting_length(setting); i++) { if (!config_setting_get_elem(setting, i)) goto set_error; if (!config_setting_set_string_elem(setting, i, bwg->pgid_desc[i])) goto set_error; } } } /* Update APP settings */ if (NULL != app) { sprintf(abuf, "app_%d", app_subtype); setting = config_setting_get_member(eth_settings, abuf); if (!setting) { /* * If we have an old version of the config file there * will only be a zero'th app entry, fixup the * config file to have all the other app entries. */ cfg_fixup(eth_settings); setting = config_setting_get_member(eth_settings, abuf); if (!setting) goto set_error; } setting_value = config_setting_get_member(setting, "app_enable"); if (!setting_value || !config_setting_set_int(setting_value, app->protocol.Enable)) goto set_error; setting_value = config_setting_get_member(setting, "app_advertise"); if (!setting_value || !config_setting_set_int(setting_value, app->protocol.Advertise)) goto set_error; setting_value = config_setting_get_member(setting, "app_willing"); if (!setting_value || !config_setting_set_int(setting_value, app->protocol.Willing)) goto set_error; memset(abuf, 0, sizeof(abuf)); setting_value = config_setting_get_member(setting, "app_cfg"); if (setting_value) { for (i = 0; i < (int)app->Length; i++) sprintf(abuf+2*i, "%02x", app->AppData[i]); if (!config_setting_set_string(setting_value, abuf)) goto set_error; } else { goto set_error; } } /* Update Logical link settings */ if (NULL != llink) { sprintf(abuf, "llink_%d", link_subtype); setting = config_setting_get_member(eth_settings, abuf); if (!setting) goto set_error; setting_value = config_setting_get_member(setting, "llink_enable"); if (!setting_value || !config_setting_set_int(setting_value, llink->protocol.Enable)) goto set_error; setting_value = config_setting_get_member(setting, "llink_advertise"); if (!setting_value || !config_setting_set_int(setting_value, llink->protocol.Advertise)) goto set_error; setting_value = config_setting_get_member(setting, "llink_willing"); if (!setting_value || !config_setting_set_int(setting_value, llink->protocol.Willing)) goto set_error; } config_write_file(&lldpad_cfg, cfg_file_name); return 0; set_error: LLDPAD_ERR("update of config file %s failed for %s", cfg_file_name, device_name); return cmd_failed; } static int get_default_persistent(const char *ifname, full_dcb_attribs *attribs) { int i; if (get_pg(DEF_CFG_STORE, &attribs->pg) != cmd_success) return 1; if (get_pfc(DEF_CFG_STORE, &attribs->pfc) != cmd_success) return 1; get_dcb_numtcs(ifname, &attribs->pg.num_tcs, &attribs->pfc.num_tcs); for (i = 0; i < DCB_MAX_APPTLV; i++) { if (get_app(DEF_CFG_STORE, i, &attribs->app[i]) != cmd_success) return 1; } for (i = 0; i < DCB_MAX_LLKTLV; i++) { if (get_llink(DEF_CFG_STORE, i, &attribs->llink[i]) != cmd_success) return 1; } return 0; } int save_dcb_enable_state(char *device_name, int dcb_enable) { return _set_persistent(device_name, dcb_enable, NULL, NULL, NULL, NULL, 0, NULL, 0); } int save_dcbx_version(int dcbx_version) { config_setting_t *dcbx_setting; config_setting_t *setting; dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); if (!dcbx_setting) return 1; setting = config_setting_get_member(dcbx_setting, "dcbx_version"); if (!setting || !config_setting_set_int(setting, dcbx_version) || !config_write_file(&lldpad_cfg, cfg_file_name)) return 1; return 0; } int set_persistent(char *device_name, full_dcb_attrib_ptrs *attribs) { int enabled = 0; int not_present = get_dcb_enable_state(device_name, &enabled); /* When the 'dcb_enable' config param does not exist put DCBX * into DEFAULT mode. This will cause DCBX to be enabled when * a DCBX TLV is received */ if (not_present) enabled = LLDP_DCBX_DEFAULT; return _set_persistent(device_name, enabled, attribs->pg, attribs->pfc, attribs->pgid, attribs->app, attribs->app_subtype, attribs->llink, LLINK_FCOE_STYPE); } int get_persistent(char *device_name, full_dcb_attribs *attribs) { config_setting_t *dcbx_setting = NULL; config_setting_t *eth_settings = NULL; config_setting_t *setting = NULL; config_setting_t *setting_array = NULL; config_setting_t *setting_traffic = NULL; config_setting_t *setting_value = NULL; full_dcb_attrib_ptrs attrib_ptrs = {0, 0, 0, 0, 0, 0, 0}; int result = cmd_failed, i; int results[MAX_USER_PRIORITIES]; int len; char abuf[32]; memset(attribs, 0, sizeof(*attribs)); dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); if (dcbx_setting) eth_settings = config_setting_get_member(dcbx_setting, device_name); /* init the internal data store for device_name */ result = get_default_persistent(device_name, attribs); if (NULL == eth_settings) { assert(memcmp(device_name, DEF_CFG_STORE, strlen(DEF_CFG_STORE))); return result; } /* Read pfc setting */ if (get_int_config(eth_settings, "pfc_enable", TYPE_BOOL, &result)) attribs->pfc.protocol.Enable = result; else goto set_default; if (get_int_config(eth_settings, "pfc_advertise", TYPE_BOOL, &result)) attribs->pfc.protocol.Advertise = result; else goto set_default; if (get_int_config(eth_settings, "pfc_willing", TYPE_BOOL, &result)) attribs->pfc.protocol.Willing = result; else goto set_default; if (!get_array_config(eth_settings, "pfc_admin_mode", TYPE_BOOL, (int *)&attribs->pfc.admin[0])) goto set_default; /* read PG protocol settings */ if (get_int_config(eth_settings, "pg_enable", TYPE_BOOL, &result)) attribs->pg.protocol.Enable = result; else goto set_default; if (get_int_config(eth_settings, "pg_advertise", TYPE_BOOL, &result)) attribs->pg.protocol.Advertise = result; else goto set_default; if (get_int_config(eth_settings, "pg_willing", TYPE_BOOL, &result)) attribs->pg.protocol.Willing = result; else goto set_default; /* read PG TX settings */ memset(results, 0, sizeof(results)); if (get_array_config(eth_settings, "pg_tx_bwg_alloc", TYPE_PERCENT, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.tx.pg_percent[i] = results[i]; else goto set_default; setting_traffic = config_setting_get_member(eth_settings, "pg_tx_traffic_attribs_type"); if (!setting_traffic) { goto set_default; } else { memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "traffic_class_mapping", TYPE_CHAR, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.tx.up[i].pgid = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "bandwidth_group_mapping", TYPE_CHAR, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.tx.up[i].bwgid = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "percent_of_bandwidth_group", TYPE_PERCENT, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.tx.up[i].percent_of_pg_cap = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "strict_priority", TYPE_PRIORITY, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.tx.up[i].strict_priority = results[i]; else goto set_default; } /* read PG RX settings */ memset(results, 0, sizeof(results)); if (get_array_config(eth_settings, "pg_rx_bwg_alloc", TYPE_PERCENT, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.rx.pg_percent[i] = results[i]; else goto set_default; setting_traffic = config_setting_get_member(eth_settings, "pg_rx_traffic_attribs_type"); if (!setting_traffic) { goto set_default; } else { memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "traffic_class_mapping", TYPE_CHAR, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.rx.up[i].pgid = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "bandwidth_group_mapping", TYPE_CHAR, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.rx.up[i].bwgid = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "percent_of_bandwidth_group", TYPE_PERCENT, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.rx.up[i].percent_of_pg_cap = results[i]; else goto set_default; memset(results, 0, sizeof(results)); if (get_array_config(setting_traffic, "strict_priority", TYPE_PRIORITY, &results[0])) for (i = 0; i < MAX_USER_PRIORITIES; i++) attribs->pg.rx.up[i].strict_priority = results[i]; else goto set_default; } setting = config_setting_get_member(eth_settings, "bwg_description"); if (setting) { for (i = 0; i < config_setting_length(setting); i++) { setting_array = config_setting_get_elem(setting, i); const char *bwg_descp = config_setting_get_string(setting_array); if (bwg_descp) strncpy(attribs->descript.pgid_desc[i], bwg_descp, MAX_DESCRIPTION_LEN-1); } /*The counter starts from 1, not 0 */ attribs->descript.max_pgid_desc = i; } for (i = 0; i < DCB_MAX_APPTLV; i++) { sprintf(abuf, "app_%d", i); setting = config_setting_get_member(eth_settings, abuf); if (!setting) { /* * If we have an old config file there will be the * zero'th entry so use it as the EWA template for * all other application types. Other data values * we be the default value. */ attribs->app[i].protocol.Enable = attribs->app[0].protocol.Enable; attribs->app[i].protocol.Willing = attribs->app[0].protocol.Willing; continue; } /* Read app setting */ if (get_int_config(setting, "app_enable", TYPE_BOOL, &result)) attribs->app[i].protocol.Enable = result; else goto set_default; if (get_int_config(setting, "app_advertise", TYPE_BOOL, &result)) attribs->app[i].protocol.Advertise = result; else goto set_default; if (get_int_config(setting, "app_willing", TYPE_BOOL, &result)) attribs->app[i].protocol.Willing = result; else goto set_default; setting_value = config_setting_get_member(setting, "app_cfg"); if (setting_value) { const char *app_cfg_hex = config_setting_get_string(setting_value); len = strlen(app_cfg_hex); if (len % 2 || len > DCB_MAX_TLV_LENGTH) { LLDPAD_DBG("invalid length for app_cfg"); goto set_default; } if (hexstr2bin(app_cfg_hex, attribs->app[i].AppData, len/2)) { LLDPAD_DBG("invalid value for app_cfg"); goto set_default; } attribs->app[i].Length = len/2; } } for (i = 0; i < DCB_MAX_LLKTLV; i++) { sprintf(abuf, "llink_%d", i); setting = config_setting_get_member(eth_settings, abuf); if (!setting) continue; /* Read app setting */ if (get_int_config(setting, "llink_enable", TYPE_BOOL, &result)) attribs->llink[i].protocol.Enable = result; else goto set_default; if (get_int_config(setting, "llink_advertise", TYPE_BOOL, &result)) attribs->llink[i].protocol.Advertise = result; else goto set_default; if (get_int_config(setting, "llink_willing", TYPE_BOOL, &result)) attribs->llink[i].protocol.Willing = result; else goto set_default; } return 0; set_default: LLDPAD_DBG("Error read config file.\n"); result = get_default_persistent(device_name, attribs); if (!result) { attrib_ptrs.pg = &attribs->pg; attrib_ptrs.pfc = &attribs->pfc; result = dcb_check_config(&attrib_ptrs); } return result; } /* * get_dcb_enable_state - check config for dcb_enable * @ifname: the port name * @result: output value buffer as int * * Supports old and new config format */ int get_dcb_enable_state(char *ifname, int *result) { int rc = EINVAL; config_setting_t *settings = NULL; char path[sizeof(DCBX_SETTING) + IFNAMSIZ + 16]; memset(path, 0, sizeof(path)); snprintf(path, sizeof(path), "%s.%s.dcb_enable", DCBX_SETTING, ifname); settings = config_lookup(&lldpad_cfg, path); if (!settings) { LLDPAD_INFO("### %s:%s:failed on %s\n", __func__, ifname, path); snprintf(path, sizeof(path), "%s.dcb_enable", ifname); settings = config_lookup(&lldpad_cfg, path); if (!settings) { LLDPAD_INFO("### %s:%s:failed again %s\n", __func__, ifname, path); rc = EIO; goto out_err; } } *result = (int)config_setting_get_int(settings); rc = 0; out_err: return rc; } int get_dcbx_version(int *result) { config_setting_t *dcbx_setting = NULL; int rval = 0; dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); if (!dcbx_setting) { create_default_cfg_file(); dcbx_setting = config_lookup(&lldpad_cfg, DCBX_SETTING); } if (get_int_config(dcbx_setting, "dcbx_version", TYPE_INT, result)) { switch (*result) { case dcbx_subtype1: case dcbx_subtype2: case dcbx_force_subtype1: case dcbx_force_subtype2: rval = 1; break; default: break; } } return rval; } lldpad-0.9.46/lldp_dcbx_clif.c000066400000000000000000000277541215142747300161730ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldp_mod.h" #include "lldptool.h" #include "clif_msgs.h" #include "lldp.h" #include "lldp_dcbx.h" #include "lldp_dcbx_clif.h" #include "tlv_dcbx.h" void print_dcbx_v1(u16, char *info); void print_dcbx_v2(u16, char *info); u32 dcbx_lookup_tlv_name(char *tlvid_str); int dcbx_print_help(); static const struct lldp_mod_ops dcbx_ops_clif = { .lldp_mod_register = dcbx_cli_register, .lldp_mod_unregister = dcbx_cli_unregister, .print_tlv = dcbx_print_tlv, .lookup_tlv_name = dcbx_lookup_tlv_name, .print_help = dcbx_print_help, }; struct type_name_info dcbx_tlv_names[] = { { .type = (OUI_CEE_DCBX << 8) | 1, .name = "CIN DCBX TLV", .key = "CIN-DCBX", .print_info = print_dcbx_v1, }, { .type = (OUI_CEE_DCBX << 8) | 2, .name = "CEE DCBX TLV", .key = "CEE-DCBX", .print_info = print_dcbx_v2, }, { .type = INVALID_TLVID, } }; int dcbx_print_help() { struct type_name_info *tn = &dcbx_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *dcbx_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); goto out_err; } mod->id = LLDP_MOD_DCBX; mod->ops = &dcbx_ops_clif; return mod; out_err: return NULL; } void dcbx_cli_unregister(struct lldp_module *mod) { free(mod); } static void print_dcbx_feature_header(char *info, int offset, u8 *subtype) { u8 flags; u16 op_version, max_version; hexstr2bin(info+offset, (u8 *)&op_version, sizeof(op_version)); op_version = ntohs(op_version); hexstr2bin(info+offset+2, (u8 *)&max_version, sizeof(max_version)); max_version = ntohs(max_version); hexstr2bin(info+offset+4, (u8 *)&flags, sizeof(flags)); printf("\t %sEnabled, %sWilling, %sError\n", (flags & 0x80) ? "" : "Not ", (flags & 0x40) ? "" : "Not ", (flags & 0x20) ? "" : "No "); hexstr2bin(info+offset+6, (u8 *)subtype, sizeof(*subtype)); } void print_dcbx_v1(u16 len, char *info) { int offset = 0; u16 tlvtype; u16 tlvlen; u8 bwgid; u8 bwgidmap[8]; u8 bwgpctmap[8]; u8 strictmap[8]; u8 bwgpct, bwpct; u8 pfcmap; u8 op_version, max_version; u32 seqno, ackno; u8 subtype; int i, j, cnt; int print_comma; int print_bwgid; u8 app_up_map; u8 llink; while (offset < 2*len) { hexstr2bin(info+offset, (u8 *)&tlvtype, sizeof(tlvtype)); tlvtype = ntohs(tlvtype); tlvlen = tlvtype & 0x1FF; tlvtype >>= 9; offset += 4; switch(tlvtype) { case DCB_CONTROL_TLV: hexstr2bin(info+offset, (u8 *)&op_version, sizeof(op_version)); op_version = ntohs(op_version); hexstr2bin(info+offset+2, (u8 *)&max_version, sizeof(max_version)); max_version = ntohs(max_version); printf("Control TLV:\n"); hexstr2bin(info+offset+4, (u8 *)&seqno, sizeof(seqno)); seqno = ntohl(seqno); hexstr2bin(info+offset+12, (u8 *)&ackno, sizeof(ackno)); ackno = ntohl(ackno); printf("\t SeqNo: %d, AckNo: %d\n", seqno, ackno); break; case DCB_PRIORITY_GROUPS_TLV: printf("\tPriority Groups TLV:\n"); print_dcbx_feature_header(info, offset, &subtype); printf("\t BWG Percentages: "); for (i = 0; i < 8; i++) { hexstr2bin(info+offset+8+i*2, (u8 *)&bwpct, sizeof(bwpct)); printf("%d:%d%%%s", i, bwpct, (i == 7) ? "\n" : " "); } printf("\t BWG Priorities: "); for (i = 0; i < 8; i++) { hexstr2bin(info+offset+8+16+4*i, (u8 *)&bwgid, sizeof(bwgid)); hexstr2bin(info+offset+8+16+4*i + 2, (u8 *)&bwgpct, sizeof(bwgpct)); bwgidmap[i] = (bwgid & 0xe0) >> 5; strictmap[i] = (bwgid & 0x18) >> 3; bwgpctmap[i] = bwgpct; } cnt = 0; for (i = 0; i < 8; i++) { print_bwgid = 1; print_comma = 0; for (j = 0; j < 8; j++) { if (bwgidmap[j] == i) { if (print_bwgid) printf(" %d:[", i); printf("%s%d%s-%d%%", (print_comma)?", ":"", j, (strictmap[j] == 1) ? "(ls)" : ( (strictmap[j] == 2) ? "(gs)" : ""), bwgpctmap[j]); print_bwgid = 0; print_comma = 1; if (++cnt == 4) { if (!print_bwgid) printf("]"); printf("\n\t\t\t "); print_bwgid = 1; print_comma = 0; } } } if (!print_bwgid) printf("]"); } printf("\n"); break; case DCB_PRIORITY_FLOW_CONTROL_TLV: printf("\tPriority Flow Control TLV:\n"); print_dcbx_feature_header(info, offset, &subtype); hexstr2bin(info+offset+8, (u8 *)&pfcmap, sizeof(pfcmap)); printf("\t PFC enabled priorities: "); print_comma = 0; for (i = 0; i < 8; i++) { if ((1<>= 9; offset += 4; switch(tlvtype) { case DCB_CONTROL_TLV2: hexstr2bin(info+offset, (u8 *)&op_version, sizeof(op_version)); op_version = ntohs(op_version); hexstr2bin(info+offset+2, (u8 *)&max_version, sizeof(max_version)); max_version = ntohs(max_version); printf("Control TLV:\n"); hexstr2bin(info+offset+4, (u8 *)&seqno, sizeof(seqno)); seqno = ntohl(seqno); hexstr2bin(info+offset+12, (u8 *)&ackno, sizeof(ackno)); ackno = ntohl(ackno); printf("\t SeqNo: %d, AckNo: %d\n", seqno, ackno); break; case DCB_PRIORITY_GROUPS_TLV2: printf("\tPriority Groups TLV:\n"); print_dcbx_feature_header(info, offset, &subtype); printf("\t PGID Priorities: "); for (i = 0; i < 4; i++) { hexstr2bin(info+offset + 8 + 2*i, (u8 *)&pgid, sizeof(pgid)); pgidmap[2*i] = (pgid & 0xf0) >> 4; pgidmap[2*i+1] = (pgid & 0x0f); } for (i = 0; i < 16; i++) { print_pgid = 1; print_comma = 0; for (j = 0; j < 8; j++) { if (pgidmap[j] == i) { if (print_pgid) printf(" %d:[", i); printf("%s%d", (print_comma)?",":"", j); print_pgid = 0; print_comma = 1; } } if (!print_pgid) printf("]"); } printf("\n\t PGID Percentages: "); for (i = 0; i < 8; i++) { hexstr2bin(info+offset+8+8+i*2, (u8 *)&pgpct, sizeof(pgpct)); printf("%d:%d%%%s", i, pgpct, (i == 7) ? "\n" : " "); } hexstr2bin(info+offset+8+8+16, (u8 *)&numtcs, sizeof(numtcs)); printf("\t Number of TC's supported: %d\n", numtcs); break; case DCB_PRIORITY_FLOW_CONTROL_TLV2: printf("\tPriority Flow Control TLV:\n"); print_dcbx_feature_header(info, offset, &subtype); hexstr2bin(info+offset+8, (u8 *)&pfcmap, sizeof(pfcmap)); hexstr2bin(info+offset+10, (u8 *)&numtcs, sizeof(numtcs)); printf("\t PFC enabled priorities: "); print_comma = 0; for (i = 0; i < 8; i++) { if ((1<>16) & 0x0fc)) && (oui_low16 == (OUI_INTEL_CORP & 0x0ffff))) { if ((oui_up8 & 0x03) == 0) { printf("\t Ethertype: 0x%04x", app_protoid); } else if ((oui_up8 & 0x03) == 1) { printf("\t TCP/UDP Port: 0x%04x", app_protoid); } else { printf("\t Reserved: 0x%04x", app_protoid); } } else { printf("\t OUI: 0x%02x%04x", oui_up8 & 0xfc, oui_low16); printf(", Selector: %d", oui_up8 & 0x03); printf(", Protocol ID: 0x%04x", app_protoid); } printf(", Priority Map: 0x%02x\n", app_up_map); suboff += 12; } break; case DCB_LLINK_TLV: hexstr2bin(info+offset+6, (u8 *)&subtype, sizeof(subtype)); if (subtype == 0) printf("\tFCoE Logical Link TLV:\n"); else if (subtype == 1) printf("\tLAN Logical Link TLV:\n"); print_dcbx_feature_header(info, offset, &subtype); hexstr2bin(info+offset+8, (u8 *)&llink, sizeof(llink)); printf("\t Link is %s\n", (llink & 0x80) ? "up" : "down"); break; default: printf("\tUnknown DCBX sub-TLV %d: %*.*s\n", tlvtype, 2*tlvlen, 2*tlvlen, info+offset); break; } offset += 2*tlvlen; } } /* return 1: if it printed the TLV * 0: if it did not */ int dcbx_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &dcbx_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } u32 dcbx_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &dcbx_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_dcbx_cmds.c000066400000000000000000000757531215142747300162060ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include "lldp.h" #include "lldpad.h" #include "dcb_events.h" #include "lldp_mand_clif.h" #include "lldp_dcbx_cmds.h" #include "lldp_dcbx_cfg.h" #include "lldpad_status.h" #include "dcb_protocol.h" #include "lldp/ports.h" #include "config.h" #include "lldp_dcbx_nl.h" #include "lldp/states.h" #include "lldp_dcbx.h" #include "lldp_rtnl.h" #include "messages.h" #include "lldp_util.h" extern u8 gdcbx_subtype; static char *hexlist = "0123456789abcdef"; static char *hexlistcaps = "0123456789ABCDEF"; static int get_dcb_state(char *port_id, char *rbuf); static cmd_status set_dcb_state(char *port_id, char *ibuf, int ilen); static int get_dcbx_config(u8 cmd, char *rbuf); static cmd_status set_dcbx_config(char *ibuf, int ilen); static cmd_status get_pg_data(pg_attribs *pg_data, int cmd, char *port_id, int dcbx_st, char *rbuf); static int set_pg_config(pg_attribs *pg_data, char *port_id, char *ibuf, int ilen); static int set_pfc_config(pfc_attribs *pfc_data, char *port_id, char *ibuf, int ilen); static int get_pfc_data(pfc_attribs *pfc_data, int cmd, char *port_id, int dcbx_st, char *rbuf); static int set_app_config(app_attribs *app_data, char *port_id, u32 subtype, char *ibuf, int ilen); static int get_app_data(app_attribs *app_data, int cmd, char *port_id, u32 subtype, char *rbuf); static int set_llink_config(llink_attribs *app_data, char *port_id, u32 subtype, char *ibuf, int ilen); static int get_llink_data(llink_attribs *llink_data, int cmd, char *port_id, u32 subtype, char *rbuf); static cmd_status get_bwg_desc(char *port_id, char *ibuf, int ilen, char *rbuf); static cmd_status set_bwg_desc(char *port_id, char *ibuf, int ilen); static void set_protocol_data(feature_protocol_attribs *protocol, char *ifname, char *ibuf, int plen, int agenttype); static cmd_status get_cmd_protocol_data(feature_protocol_attribs *protocol, u8 cmd, char *rbuf); static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int test_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable, }, { .arg = 0 } }; static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_CEE_DCBX << 8) | 1: case (OUI_CEE_DCBX << 8) | 2: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02zx%s%04zx%s", strlen(arg), arg, strlen(s), s); return cmd_success; } void dont_advertise_dcbx_all(char *ifname, bool ad) { int i, is_pfc; pfc_attribs pfc_data; pg_attribs pg_data; app_attribs app_data; llink_attribs llink_data; u32 event_flag = 0; is_pfc = get_pfc(ifname, &pfc_data); if (get_pg(ifname, &pg_data) == cmd_success) { pg_data.protocol.Advertise = ad; put_pg(ifname, &pg_data, &pfc_data); event_flag |= DCB_LOCAL_CHANGE_PG; } if (is_pfc == cmd_success) { pfc_data.protocol.Advertise = ad; put_pfc(ifname, &pfc_data); event_flag |= DCB_LOCAL_CHANGE_PFC; } for (i = 0; i < DCB_MAX_APPTLV ; i++) { if (get_app(ifname, (u32)i, &app_data) == cmd_success) { app_data.protocol.Advertise = ad; put_app(ifname, (u32)i, &app_data); event_flag |= DCB_LOCAL_CHANGE_APPTLV(i); } } for (i = 0; i < DCB_MAX_LLKTLV ; i++) { if (get_llink(ifname, (u32)i, &llink_data) == cmd_success) { llink_data.protocol.Advertise = ad; put_llink(ifname, (u32)i, &llink_data); event_flag |= DCB_LOCAL_CHANGE_LLINK; } } } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { int value; int current_value; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_CEE_DCBX << 8) | 1: case (OUI_CEE_DCBX << 8) | 2: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; if (test) return cmd_success; current_value = is_tlv_txenabled(cmd->ifname, cmd->type, cmd->tlvid); snprintf(obuf, obuf_len, "enabled = %s\n", value ? "yes" : "no"); if (current_value == value) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) return cmd_failed; dont_advertise_dcbx_all(cmd->ifname, value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, true); } struct arg_handlers *dcbx_get_arg_handlers() { return &arg_handlers[0]; } static int get_dcb_state(char *port_id, char *rbuf) { int state = 0; cmd_status rval = cmd_success; if (get_hw_state(port_id, &state)) rval = cmd_failed; else sprintf(rbuf, "%01x", state); return (rval); } static cmd_status set_dcb_state(char *port_id, char *ibuf, int ilen) { bool state; int off; int plen; cmd_status rval = cmd_success; plen=strlen(port_id); off = DCB_PORT_OFF + plen; if (ilen == (off + CFG_DCB_DLEN)) { state = (*(ibuf+off+DCB_STATE)) ^ '0'; set_hw_state(port_id, state); rval = save_dcb_enable_state(port_id, state); } else { printf("error - setcommand has invalid argument length\n"); rval = cmd_bad_params; } return rval; } static int get_dcbx_config(u8 cmd, char *rbuf) { cmd_status rval = cmd_success; int dcbx_version; if (cmd == CMD_GET_CONFIG) { if (!get_dcbx_version(&dcbx_version)) rval = cmd_failed; } else { dcbx_version = gdcbx_subtype; } if (rval == cmd_success) sprintf(rbuf, "%01x", dcbx_version); return (rval); } static cmd_status set_dcbx_config(char *ibuf, int ilen) { int version; int off; cmd_status rval = cmd_success; off = DCBX_CFG_OFF; if (ilen == DCBX_CFG_OFF + CFG_DCBX_DLEN) { version = (*(ibuf+off+DCBX_VERSION)) ^ '0'; switch (version) { case dcbx_subtype1: case dcbx_subtype2: case dcbx_force_subtype1: case dcbx_force_subtype2: rval = save_dcbx_version(version); break; default: rval = cmd_bad_params; break; } } else { printf("error - setcommand has invalid argument length\n"); rval = cmd_bad_params; } return rval; } static cmd_status get_bwg_desc(char *port_id, char *ibuf, int ilen, char *rbuf) { u8 pgid; int plen; char *desc_str = NULL; cmd_status rval = cmd_success; plen = strlen(port_id); if (ilen != DCB_PORT_OFF + plen + PG_DESC_GET_DLEN) { rval = cmd_bad_params; } else { pgid = *(ibuf + DCB_PORT_OFF + plen + PG_DESC_PGID) & 0x0f; if (pgid < 1 || pgid > 8) { rval = cmd_bad_params; } else { rval = get_bwg_descrpt(port_id, pgid-1, &desc_str); if (rval == cmd_success && desc_str) { sprintf(rbuf, "%01x%02x%s", pgid, (unsigned int) strlen(desc_str), desc_str); if (desc_str) free(desc_str); } } } return rval; } static cmd_status set_bwg_desc(char *port_id, char *ibuf, int ilen) { u8 pgid; u8 desc_len = 0; int plen; cmd_status rval = cmd_success; plen = strlen(port_id); if (ilen >= DCB_PORT_OFF + plen + PG_DESC_SET_DLEN) { hexstr2bin(ibuf+DCB_PORT_OFF + plen + PG_DESC_LEN, &desc_len, sizeof(desc_len)); pgid = *(ibuf + DCB_PORT_OFF + plen + PG_DESC_PGID) & 0x0f; } else { printf("ilen[%d] < %d\n", ilen, DCB_PORT_OFF + plen + PG_DESC_SET_DLEN); return cmd_bad_params; } if (ilen == DCB_PORT_OFF + plen + PG_DESC_SET_DLEN + desc_len) { if (pgid < 1 || pgid > 8 || desc_len > 100) { printf("pgid = %d, desc_len = %d\n", pgid, desc_len); rval = cmd_bad_params; } else { rval = put_bwg_descrpt(port_id, pgid-1, ibuf+DCB_PORT_OFF+plen+PG_DESC_DATA); } } else { printf("ilen[%d] != %d\n", ilen, DCB_PORT_OFF + plen + PG_DESC_SET_DLEN + desc_len); rval = cmd_bad_params; } return rval; } static void set_app_protocol_data(char *ifname, char *ibuf, int plen, int ilen, int subtype, int agenttype) { u8 aflag, eflag, wflag; int status, last, i; app_attribs app_data; feature_protocol_attribs *protocol; status = get_app(ifname, (u32)subtype, &app_data); if (status != cmd_success) { printf("%s %s: error[%d] getting APP data.\n", __func__, ifname, status); return; } protocol = &app_data.protocol; last = protocol->Advertise; aflag = *(ibuf+DCB_PORT_OFF+plen+CFG_ADVERTISE); if (aflag == '0' || aflag == '1') protocol->Advertise = aflag & 0x01; status = set_app_config(&app_data, ifname, (u32)subtype, ibuf, ilen); if (status != cmd_success) printf("%s %s: error[%d] set_app_config failed\n", __func__, ifname, status); if (last != protocol->Advertise && protocol->Advertise) { tlv_enabletx(ifname, agenttype, (OUI_CEE_DCBX << 8) | protocol->dcbx_st); } eflag = *(ibuf+DCB_PORT_OFF+plen+CFG_ENABLE); wflag = *(ibuf+DCB_PORT_OFF+plen+CFG_WILLING); for (i = 0; i < DCB_MAX_APPTLV; i++) { status = get_app(ifname, (u32)i, &app_data); if (status != cmd_success) continue; protocol = &app_data.protocol; if (eflag == '0' || eflag == '1') protocol->Enable = eflag & 0x01; if (wflag == '0' || wflag == '1') protocol->Willing = wflag & 0x01; status = put_app(ifname, i, &app_data); if (status != cmd_success) printf("%s %s: error[%d] set_app_config failed\n", __func__, ifname, status); } somethingChangedLocal(ifname, agenttype); } static void set_protocol_data(feature_protocol_attribs *protocol, char *ifname, char *ibuf, int plen, int agenttype) { u8 flag; int last; flag = *(ibuf+DCB_PORT_OFF+plen+CFG_ENABLE); if (flag == '0' || flag == '1') protocol->Enable = flag & 0x01; flag = *(ibuf+DCB_PORT_OFF+plen+CFG_ADVERTISE); if (flag == '0' || flag == '1') { last = protocol->Advertise; protocol->Advertise = flag & 0x01; if (last != protocol->Advertise && protocol->Advertise) { tlv_enabletx(ifname, agenttype, (OUI_CEE_DCBX << 8) | protocol->dcbx_st); somethingChangedLocal(ifname, agenttype); } } flag = *(ibuf+DCB_PORT_OFF+plen+CFG_WILLING); if (flag == '0' || flag == '1') protocol->Willing = flag & 0x01; } /* rbuf points to correct destination location */ static cmd_status get_cmd_protocol_data(feature_protocol_attribs *protocol, u8 cmd, char *rbuf) { cmd_status rval = cmd_success; switch(cmd) { case CMD_GET_CONFIG: sprintf(rbuf, "%1x%1x%1x", protocol->Enable, protocol->Advertise, protocol->Willing); break; case CMD_GET_OPER: sprintf(rbuf, "%02x%02x%02x%1x%1x", protocol->Oper_version, protocol->Max_version, protocol->Error_Flag, protocol->OperMode, protocol->Syncd); break; case CMD_GET_PEER: if (protocol->TLVPresent == true) sprintf(rbuf, "%1x%1x%02x%02x%1x%1x", protocol->Enable, protocol->Willing, protocol->Oper_version, protocol->Max_version, protocol->Error, protocol->dcbx_st); else rval = cmd_peer_not_present; break; } return rval; } static int handle_dcbx_cmd(u8 cmd, u8 feature, char *ibuf, int ilen, char *rbuf) { int status = cmd_success; if (ilen < DCBX_CFG_OFF) return cmd_invalid; /* append standard dcb command response content */ sprintf(rbuf , "%*.*s", DCBX_CFG_OFF, DCBX_CFG_OFF, ibuf); switch(feature) { case FEATURE_DCBX: if (cmd == CMD_SET_CONFIG) { status = set_dcbx_config(ibuf, ilen); } else if (cmd != CMD_GET_PEER) { status = get_dcbx_config(cmd, rbuf+strlen(rbuf)); } else { status = cmd_invalid; } break; default: break; } return status; } int dcbx_clif_cmd(void *data, UNUSED struct sockaddr_un *from, UNUSED socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen) { u8 status = cmd_success; u8 cmd; u8 feature; u8 subtype; u8 plen; char port_id[MAX_U8_BUF]; pg_attribs pg_data; pfc_attribs pfc_data; app_attribs app_data; llink_attribs llink_data; struct port *port; struct dcbx_tlvs *dcbx; data = (struct clif_data *) data; if (hexstr2bin(ibuf+DCB_CMD_OFF, &cmd, sizeof(cmd)) || hexstr2bin(ibuf+DCB_FEATURE_OFF, &feature, sizeof(feature))) return cmd_invalid; if (feature == FEATURE_DCBX) return handle_dcbx_cmd(cmd, feature, ibuf, ilen, rbuf); if (hexstr2bin(ibuf+DCB_SUBTYPE_OFF, &subtype, sizeof(subtype)) || hexstr2bin(ibuf+DCB_PORTLEN_OFF, &plen, sizeof(plen))) return cmd_invalid; if (ilen < DCB_PORT_OFF) return cmd_invalid; if (ibuf[DCB_VER_OFF] < (CLIF_DCBMSG_VERSION | 0x30)) { printf("unsupported client interface message version %x %x\n", ibuf[DCB_VER_OFF], CLIF_DCBMSG_VERSION | 0x30); return cmd_ctrl_vers_not_compatible; } if (ilen < DCB_PORT_OFF+plen) { printf("command too short\n"); return cmd_invalid; } /* append standard dcb command response content */ snprintf(rbuf , rlen, "%*.*s", DCB_PORT_OFF+plen, DCB_PORT_OFF+plen, ibuf); memcpy(port_id, ibuf+DCB_PORT_OFF, plen); port_id[plen] = '\0'; /* Confirm port is a lldpad managed port */ port = port_find_by_name(port_id); if (!port) return cmd_device_not_found; dcbx = dcbx_data(port->ifname); if (!dcbx) return cmd_device_not_found; /* OPER and PEER cmd not applicable while in IEEE-DCBX modes */ if (dcbx->active == 0 && (cmd == CMD_GET_PEER || cmd == CMD_GET_OPER)) return cmd_not_applicable; switch(feature) { case FEATURE_DCB: if (cmd == CMD_SET_CONFIG) status = set_dcb_state(port_id, ibuf, ilen); else if (cmd == CMD_GET_CONFIG) status = get_dcb_state(port_id, rbuf+strlen(rbuf)); else status = cmd_invalid; break; case FEATURE_PG: if (cmd == CMD_GET_PEER) { status = get_peer_pg(port_id, &pg_data); } else { status = get_pg(port_id, &pg_data); } if (status != cmd_success) { printf("error[%d] getting PG data for %s\n", status, port_id); return status; } if (cmd == CMD_SET_CONFIG) { if (ilen < (DCB_PORT_OFF + plen + CFG_LEN)) { printf("set command too short\n"); status = cmd_invalid; } else { set_protocol_data(&pg_data.protocol, port_id, ibuf, plen, NEAREST_BRIDGE); status = set_pg_config(&pg_data, port_id, ibuf, ilen); } } else { status = get_cmd_protocol_data(&pg_data.protocol, cmd, rbuf+strlen(rbuf)); if (status == cmd_success) status = get_pg_data(&pg_data, cmd, port_id, pg_data.protocol.dcbx_st, rbuf+strlen(rbuf)); } break; case FEATURE_PFC: if (cmd == CMD_GET_PEER) { status = get_peer_pfc(port_id, &pfc_data); } else { status = get_pfc(port_id, &pfc_data); } if (status != cmd_success) { printf("error[%d] getting PFC data for %s\n", status, port_id); return status; } if (cmd == CMD_SET_CONFIG) { if (ilen < (DCB_PORT_OFF + plen + CFG_LEN)) { printf("set command too short\n"); status = cmd_failed; } else { set_protocol_data(&pfc_data.protocol, port_id, ibuf, plen, NEAREST_BRIDGE); status = set_pfc_config(&pfc_data, port_id, ibuf, ilen); } } else { status = get_cmd_protocol_data(&pfc_data.protocol, cmd, rbuf+strlen(rbuf)); if (status == cmd_success) status = get_pfc_data(&pfc_data, cmd, port_id, pfc_data.protocol.dcbx_st, rbuf+strlen(rbuf)); } break; case FEATURE_APP: if (cmd == CMD_GET_PEER) { status = get_peer_app(port_id, (u32)subtype, &app_data); } else { status = get_app(port_id, (u32)subtype, &app_data); } if (status != cmd_success) { printf("error[%d] getting APP data for %s\n", status, port_id); return status; } if (cmd == CMD_SET_CONFIG) { if (ilen < (DCB_PORT_OFF + plen + CFG_LEN)) { printf("set command too short\n"); status = cmd_failed; } else { set_app_protocol_data(port_id, ibuf, plen, ilen, subtype, NEAREST_BRIDGE); } } else { status = get_cmd_protocol_data(&app_data.protocol, cmd, rbuf+strlen(rbuf)); if (status == cmd_success) status = get_app_data(&app_data, cmd, port_id, subtype, rbuf + strlen(rbuf)); } break; case FEATURE_LLINK: if (cmd == CMD_GET_PEER) { status = get_peer_llink(port_id, (u32)subtype, &llink_data); } else { status = get_llink(port_id, (u32)subtype, &llink_data); } if (status != cmd_success) { printf("error[%d] getting APP data for %s\n", status, port_id); return status; } if (cmd == CMD_SET_CONFIG) { if (ilen < (DCB_PORT_OFF + plen + CFG_LEN)) { printf("set command too short\n"); status = cmd_failed; } else { set_protocol_data(&llink_data.protocol, port_id, ibuf, plen, NEAREST_BRIDGE); status = set_llink_config(&llink_data, port_id, (u32)subtype, ibuf, ilen); } } else { status = get_cmd_protocol_data(&llink_data.protocol, cmd, rbuf+strlen(rbuf)); if (status == cmd_success) status = get_llink_data(&llink_data, cmd, port_id, subtype, rbuf + strlen(rbuf)); } break; case FEATURE_PG_DESC: if (cmd == CMD_GET_CONFIG) { status = get_bwg_desc(port_id, ibuf, ilen, rbuf+strlen(rbuf)); if (status != cmd_success) { printf("error[%d] getting BWG desc for %s\n", status, port_id); return status; } } else if (cmd == CMD_SET_CONFIG) { status = set_bwg_desc(port_id, ibuf, ilen); } break; default: break; } return status; } /* rbuf points to correct destination location */ static cmd_status get_pg_data(pg_attribs *pg_data, int cmd, char *port_id, int dcbx_st, char *rbuf) { int i; int status; int value; switch (cmd) { case CMD_GET_CONFIG: /* already have the data object */ case CMD_GET_PEER: break; case CMD_GET_OPER: status = get_oper_pg(port_id, pg_data); if (status != cmd_success) { printf("error[%d] getting oper PG data for %s\n", status, port_id); return status; } break; } for (i = 0; i < MAX_USER_PRIORITIES; i++) sprintf(rbuf+PG_UP2TC(i), "%1x", pg_data->tx.up[i].pgid); for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) sprintf(rbuf+PG_PG_PCNT(i), "%02x", pg_data->tx.pg_percent[i]); for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (pg_data->tx.up[i].strict_priority == dcb_link) value = LINK_STRICT_PGID; else value = pg_data->tx.up[i].pgid; sprintf(rbuf+PG_UP_PGID(i), "%1X", value); } for (i = 0; i < MAX_USER_PRIORITIES; i++) { if ((cmd != CMD_GET_PEER) || (cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype1)) sprintf(rbuf+PG_UP_PCNT(i), "%02x", pg_data->tx.up[i].percent_of_pg_cap); else if (cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype2) sprintf(rbuf+PG_UP_PCNT(i), "%c%c", CLIF_NOT_SUPPLIED, CLIF_NOT_SUPPLIED); } for (i = 0; i < MAX_USER_PRIORITIES; i++) { if ((cmd != CMD_GET_PEER) || (cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype1)) { if (pg_data->tx.up[i].strict_priority == dcb_link) value = dcb_none; else value = pg_data->tx.up[i].strict_priority; sprintf(rbuf+PG_UP_STRICT(i), "%1x", value); } else if (cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype2) { sprintf(rbuf+PG_UP_STRICT(i), "%c", CLIF_NOT_SUPPLIED); } } if ((cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype1) || (cmd == CMD_GET_OPER)) sprintf(rbuf+PG_UP_NUM_TC, "%c", CLIF_NOT_SUPPLIED); else sprintf(rbuf+PG_UP_NUM_TC, "%1x", pg_data->num_tcs); return cmd_success; } /* rbuf points to correct destination location */ static int get_pfc_data(pfc_attribs *pfc_data, int cmd, char *port_id, int dcbx_st, char *rbuf) { int i; int status; switch (cmd) { case CMD_GET_CONFIG: /* already have the data object */ break; case CMD_GET_OPER: status = get_oper_pfc(port_id, pfc_data); if (status != cmd_success) { printf("error[%d] getting oper PFC data for %s\n", status, port_id); return status; } break; case CMD_GET_PEER: status = get_peer_pfc(port_id, pfc_data); if (status != cmd_success) { printf("error[%d] getting peer PFC data for %s\n", status, port_id); return status; } break; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { sprintf(rbuf+PFC_UP(i), "%1x", pfc_data->admin[i]); } if ((cmd == CMD_GET_PEER && dcbx_st == dcbx_subtype1) || (cmd == CMD_GET_OPER)) sprintf(rbuf+PFC_NUM_TC, "%c", CLIF_NOT_SUPPLIED); else sprintf(rbuf+PFC_NUM_TC, "%1x", pfc_data->num_tcs); return cmd_success; } /* rbuf points to correct destination location */ static int get_app_data(app_attribs *app_data, int cmd, char *port_id, u32 subtype, char *rbuf) { int status; unsigned int i; switch (cmd) { case CMD_GET_CONFIG: /* already have the data object */ break; case CMD_GET_OPER: status = get_oper_app(port_id, subtype, app_data); if (status != cmd_success) { printf("error[%d] getting oper App data for %s\n", status, port_id); return status; } break; case CMD_GET_PEER: status = get_peer_app(port_id, subtype, app_data); if (status != cmd_success) { printf("error[%d] getting peer app data for %s\n", status, port_id); return status; } break; } sprintf(rbuf+APP_LEN, "%02x", 2*app_data->Length); for (i=0; iLength; i++) sprintf(rbuf+APP_DATA+2*i, "%02x", *(app_data->AppData+i)); return cmd_success; } /* rbuf points to correct destination location */ static int get_llink_data(llink_attribs *llink_data, int cmd, char *port_id, u32 subtype, char *rbuf) { int status; switch (cmd) { case CMD_GET_CONFIG: /* already have the data object */ break; case CMD_GET_OPER: status = get_oper_llink(port_id, subtype, llink_data); if (status != cmd_success) { printf("error[%d] getting oper logical link data " "for %s\n", status, port_id); return status; } break; case CMD_GET_PEER: status = get_peer_llink(port_id, subtype, llink_data); if (status != cmd_success) { printf("error[%d] getting peer logical link data " "for %s\n", status, port_id); return status; } break; } sprintf(rbuf+LLINK_STATUS, "%1x", llink_data->llink.llink_status); return cmd_success; } static int set_pg_config(pg_attribs *pg_data, char *port_id, char *ibuf, int ilen) { pfc_attribs pfc_data; full_dcb_attrib_ptrs dcb_data; u8 flag; cmd_status status = cmd_success; int i, is_pfc; int plen; int off; bool used[MAX_BANDWIDTH_GROUPS]; bool uppcts_changed = false; plen=strlen(port_id); off = DCB_PORT_OFF + plen + CFG_LEN; if (ilen == (off + CFG_PG_DLEN)) { for (i = 0; i < MAX_USER_PRIORITIES; i++) { flag = *(ibuf+off+PG_UP2TC(i)); if (flag == CLIF_NOT_SUPPLIED) continue; if ((flag & 0x07) >= pg_data->num_tcs) return cmd_bad_params; pg_data->tx.up[i].pgid = flag & 0x07; pg_data->rx.up[i].pgid = flag & 0x07; } memset(used, false, sizeof(used)); for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { if (*(ibuf+off+PG_PG_PCNT(i)) == CLIF_NOT_SUPPLIED) continue; if (hexstr2bin(ibuf+off+PG_PG_PCNT(i), &flag, sizeof(flag))) return cmd_bad_params; pg_data->tx.pg_percent[i] = flag; pg_data->rx.pg_percent[i] = flag; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { flag = *(ibuf+off+PG_UP_PGID(i)); if (flag == CLIF_NOT_SUPPLIED) { if (pg_data->tx.up[i].strict_priority == dcb_link) flag = LINK_STRICT_PGID; else flag = pg_data->tx.up[i].pgid; } else { if (flag == hexlist[LINK_STRICT_PGID] || flag == hexlistcaps[LINK_STRICT_PGID]) flag = LINK_STRICT_PGID; else flag = flag & 0x0f; pg_data->tx.up[i].pgid = flag; pg_data->rx.up[i].pgid = flag; } /* keep track of which PGID's in the range of * 0-7 are being used (not counting pre-existing * link strict PGID). */ if (flag < MAX_BANDWIDTH_GROUPS) used[flag] = true; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (*(ibuf+off+PG_UP_PCNT(i)) == CLIF_NOT_SUPPLIED) continue; uppcts_changed = true; if (hexstr2bin(ibuf+off+PG_UP_PCNT(i), &flag, sizeof(flag))) return cmd_bad_params; pg_data->tx.up[i].percent_of_pg_cap = flag; pg_data->rx.up[i].percent_of_pg_cap = flag; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { flag = *(ibuf+off+PG_UP_STRICT(i)); if (flag == CLIF_NOT_SUPPLIED) continue; /* only set or clear the group strict bit. * the link strict bit will be handled later. */ flag = flag & 0x01; if (flag) { pg_data->tx.up[i].strict_priority |= flag; pg_data->rx.up[i].strict_priority |= flag; } else { pg_data->tx.up[i].strict_priority &= ~dcb_group; pg_data->rx.up[i].strict_priority &= ~dcb_group; } } /* find the first unused PGID in range 0-7 */ for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) if (!used[i]) break; /* The end goal here is to have all of the user priorities * assigned to the link strict PGID to use the lowest * unused value in the range 0-7. The strict_priority field * is set to 'dcb_link' as well for the link strict PGID. */ flag = i; for (i = 0; i < MAX_USER_PRIORITIES; i++) { if (pg_data->tx.up[i].pgid == LINK_STRICT_PGID || (!used[pg_data->tx.up[i].pgid] && pg_data->tx.up[i].strict_priority & dcb_link)) { pg_data->tx.up[i].pgid = flag; pg_data->rx.up[i].pgid = flag; pg_data->tx.up[i].strict_priority = dcb_link; pg_data->rx.up[i].strict_priority = dcb_link; } else { pg_data->tx.up[i].strict_priority &= ~dcb_link; pg_data->rx.up[i].strict_priority &= ~dcb_link; } } } else if (ilen != off) { printf("error - setcommand has invalid argument length\n"); return cmd_bad_params; } memset((void *)&dcb_data, 0, sizeof(dcb_data)); dcb_data.pg = pg_data; status = dcb_check_config(&dcb_data); /* if the rule checking fails and client did not supply * user priority percentages, then compute new percentages * and try one more time. */ if (status == cmd_bad_params && !uppcts_changed) { rebalance_uppcts(pg_data); status = dcb_check_config(&dcb_data); } if (status != cmd_success) { printf("invalid DCB settings\n"); return status; } is_pfc = get_pfc(port_id, &pfc_data); if (is_pfc == cmd_success) status = put_pg(port_id, pg_data, &pfc_data); else status = put_pg(port_id, pg_data, NULL); if (status != cmd_success) printf("error[%d] setting PG data for %s\n", status, port_id); return status; } static int set_pfc_config(pfc_attribs *pfc_data, char *port_id, char *ibuf, int ilen) { u8 flag; cmd_status status = cmd_success; int i; int off; int plen; plen=strlen(port_id); off = DCB_PORT_OFF + plen + CFG_LEN; if (ilen == (off + CFG_PFC_DLEN)) { for (i = 0; i < MAX_USER_PRIORITIES; i++) { flag = (*(ibuf+off+PFC_UP(i))) & 0x07; if (flag == CLIF_NOT_SUPPLIED) continue; if (flag) pfc_data->admin[i] = pfc_enabled; else pfc_data->admin[i] = pfc_disabled; } } else if (ilen != off) { /* at least needs to include the protocol settings */ printf("error - setcommand has invalid argument length\n"); return cmd_failed; } status = put_pfc(port_id, pfc_data); if (status != cmd_success) printf("error[%d] setting PFC data for %s\n", status, port_id); return status; } static int set_app_config(app_attribs *app_data, char *port_id, u32 subtype, char *ibuf, int ilen) { cmd_status status = cmd_success; int off; int plen; u8 applen = 0; plen=strlen(port_id); off = DCB_PORT_OFF + plen + CFG_LEN; hexstr2bin(ibuf+off+APP_LEN, &applen, sizeof(applen)); if (ilen == (off + APP_DATA + applen)) { app_data->Length = applen/2; if (hexstr2bin(ibuf+off+APP_DATA, app_data->AppData, applen/2)) return cmd_bad_params; } else if (ilen != off) { /* at least needs to include the protocol settings */ printf("error - setcommand has invalid argument length\n"); return cmd_failed; } status = put_app(port_id, subtype, app_data); if (status != cmd_success) printf("error[%d] setting APP data for %s\n", status, port_id); return status; } static int set_llink_config(llink_attribs *llink_data, char *port_id, u32 subtype, char *ibuf, int ilen) { cmd_status status = cmd_success; int off; int value; off = DCB_PORT_OFF + strlen(port_id) + CFG_LEN; if (ilen != off + LLINK_DLEN) { /* at least needs to include the protocol settings */ printf("error - setcommand has invalid argument length\n"); return cmd_failed; } value = *(ibuf+off+LLINK_STATUS); if (value != CLIF_NOT_SUPPLIED) llink_data->llink.llink_status = value & 0x0f; status = put_llink(port_id, subtype, llink_data); if (status != cmd_success) printf("error[%d] setting logical link data for %s\n", status, port_id); return status; } void pfc_event(char *dn, u32 events) { char ebuf[512]; if (snprintf(ebuf, sizeof(ebuf), "%01x%02x%s%02x00%1x%1x", CLIF_EV_VERSION, (unsigned int)strlen(dn), dn, FEATURE_PFC, !!(events & EVENT_OPERMODE), !!(events & EVENT_OPERATTR)) < (int)sizeof(ebuf)) send_event(MSG_EVENT, 0, ebuf); } void pg_event(char *dn, u32 events) { char ebuf[512]; if (snprintf(ebuf, sizeof(ebuf), "%01x%02x%s%02x00%1x%1x", CLIF_EV_VERSION, (unsigned int)strlen(dn), dn, FEATURE_PG, !!(events & EVENT_OPERMODE), !!(events & EVENT_OPERATTR)) < (int)sizeof(ebuf)) send_event(MSG_EVENT, 0, ebuf); } void app_event(char *dn, u32 subtype, u32 events) { char ebuf[512]; if (snprintf(ebuf, sizeof(ebuf), "%01x%02x%s%02x%02x%1x%1x", CLIF_EV_VERSION, (unsigned int)strlen(dn), dn, FEATURE_APP, subtype, !!(events & EVENT_OPERMODE), !!(events & EVENT_OPERATTR)) < (int)sizeof(ebuf)) send_event(MSG_EVENT, 0, ebuf); } void llink_event(char *dn, u32 subtype, u32 events) { char ebuf[512]; if (snprintf(ebuf, sizeof(ebuf), "%01x%02x%s%02x%02x%1x%1x", CLIF_EV_VERSION, (unsigned int)strlen(dn), dn, FEATURE_LLINK, subtype, !!(events & EVENT_OPERMODE), !!(events & EVENT_OPERATTR)) < (int)sizeof(ebuf)) send_event(MSG_EVENT, 0, ebuf); } lldpad-0.9.46/lldp_dcbx_nl.c000066400000000000000000000456231215142747300156620ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "linux/netlink.h" #include "linux/rtnetlink.h" #include "linux/dcbnl.h" #include "lldp.h" #include "dcb_types.h" #include "dcb_protocol.h" #include "dcb_driver_interface.h" #include "lldp_dcbx_nl.h" #include "messages.h" #include "lldp_rtnl.h" #include "lldp/ports.h" static int nl_sd = 0; static int rtseq = 0; static int next_rtseq(void) { return ++rtseq; } static int init_socket(void) { int sd; int rcv_size = MAX_MSG_SIZE; struct sockaddr_nl snl; int reuse = 1; sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sd < 0) return sd; if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { close(sd); return -EIO; } if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) { close(sd); return -EIO; } memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = 0; if (connect(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(sd); return -EIO; } return sd; } static struct nlmsghdr *start_msg(__u16 msg_type, __u8 arg) { struct nlmsghdr *nlh; struct dcbmsg *d; struct ifinfomsg *ifi; /* nlh needs to be free'd by send_msg() */ nlh = (struct nlmsghdr *)malloc(MAX_MSG_SIZE); if (NULL == nlh) return NULL; memset((void *)nlh, 0, MAX_MSG_SIZE); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = next_rtseq(); nlh->nlmsg_pid = getpid(); switch (msg_type) { case RTM_GETDCB: case RTM_SETDCB: nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct dcbmsg)); d = NLMSG_DATA(nlh); d->cmd = arg; d->dcb_family = AF_UNSPEC; d->dcb_pad = 0; break; case RTM_GETLINK: nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); ifi = NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = arg; ifi->ifi_change = 0xffffffff; break; default: free(nlh); nlh = NULL; break; } return nlh; } static struct rtattr *add_rta(struct nlmsghdr *nlh, __u16 rta_type, void *attr, __u16 rta_len) { struct rtattr *rta; rta = (struct rtattr *)((char *)nlh + nlh->nlmsg_len); rta->rta_type = rta_type; rta->rta_len = rta_len + NLA_HDRLEN; if (attr) memcpy(NLA_DATA(rta), attr, rta_len); nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len); return rta; } /* free's nlh which was allocated by start_msg */ static int send_msg(struct nlmsghdr *nlh) { struct sockaddr_nl nladdr; void *buf = (void *)nlh; int r, len = nlh->nlmsg_len; if (nlh == NULL) return 1; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; do { r = sendto(nl_sd, buf, len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); LLDPAD_DBG("send_msg: sendto = %d\n", r); } while (r < 0 && errno == EINTR); free(nlh); if (r < 0) return 1; else return 0; } static struct nlmsghdr *get_msg(unsigned int seq) { struct nlmsghdr *nlh; unsigned len; int res; int found = 0; /* nlh needs to be free'd by caller */ nlh = (struct nlmsghdr *)malloc(MAX_MSG_SIZE); if (NULL == nlh) return NULL; memset(nlh, 0, MAX_MSG_SIZE); while (!found) { res = recv(nl_sd, (void *)nlh, MAX_MSG_SIZE, MSG_DONTWAIT); if (res < 0) { if (errno == EINTR) continue; perror("get_msg: recv error"); free(nlh); nlh = NULL; break; } len = res; if (!(NLMSG_OK(nlh, len))) { LLDPAD_DBG("get_msg: NLMSG_OK is false\n"); free(nlh); nlh = NULL; break; } if ((nlh->nlmsg_type == RTM_GETDCB || nlh->nlmsg_type == RTM_SETDCB) && nlh->nlmsg_seq == seq) { break; } else if (nlh->nlmsg_type == NLMSG_ERROR) { if (nlh->nlmsg_seq != seq) { continue; } else { free(nlh); nlh = NULL; break; } break; } } return nlh; } static int recv_msg(int cmd, int attr, unsigned int seq) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; int rval; nlh = get_msg(seq); if (NULL == nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if ((d->cmd != cmd) || (rta->rta_type != attr)) { LLDPAD_DBG("Bad netlink message attribute."); return -EIO; } rval = *(__u8 *)NLA_DATA(rta); free(nlh); return rval; } static int get_state(char *ifname, __u8 *state) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; int rval = 0; unsigned int seq; nlh = start_msg(RTM_GETDCB, DCB_CMD_GSTATE); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); if (send_msg(nlh)) return -EIO; nlh = get_msg(seq); if (NULL == nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GSTATE) { return -EIO; } if (rta->rta_type != DCB_ATTR_STATE) { rval = -EIO; } else { *state = *(__u8 *)NLA_DATA(rta); } free(nlh); return rval; } static int set_state(char *ifname, __u8 state) { struct nlmsghdr *nlh; int seq; nlh = start_msg(RTM_SETDCB, DCB_CMD_SSTATE); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_STATE, (void *)&state, sizeof(__u8)); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_SSTATE, DCB_ATTR_STATE, seq)); } /* returns: 0 on success * non-zero on failure */ static int set_pfc_cfg(char *ifname, __u8 *pfc) { struct nlmsghdr *nlh; struct rtattr *rta_parent, *rta_child; int i; int seq; LLDPAD_DBG("set_pfc_cfg: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_PFC_SCFG); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_PFC_CFG, NULL, 0); for (i = DCB_PFC_UP_ATTR_0; i < DCB_PFC_UP_ATTR_MAX; i++) { rta_child = add_rta(nlh, i, (void *)pfc, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); pfc++; } if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG, seq)); } /* returns: 0 on success * non-zero on failure */ static int set_pfc_state(char *ifname, __u8 state) { struct nlmsghdr *nlh; int seq; LLDPAD_DBG("set_pfc_state: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_PFC_SSTATE); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_PFC_STATE, (void *)&state, sizeof(__u8)); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE, seq)); return 0; } /* returns: 0 on success * 1 on failure */ static int set_pg_cfg(char *ifname, struct tc_config *tc, __u8 *bwg, int cmd) { struct nlmsghdr *nlh; struct rtattr *class_parent, *param_parent, *rta_child; __u8 *p = (__u8 *)tc; __u8 *b = (__u8 *)bwg; int i, j; int seq; nlh = start_msg(RTM_SETDCB, cmd); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); class_parent = add_rta(nlh, DCB_ATTR_PG_CFG, NULL, 0); for (i = DCB_PG_ATTR_TC_0; i < DCB_PG_ATTR_TC_MAX; i++) { param_parent = add_rta(nlh, i, NULL, 0); for (j = DCB_TC_ATTR_PARAM_UNDEFINED + 1; j < DCB_TC_ATTR_PARAM_MAX; j++) { rta_child = add_rta(nlh, j, (void *)p, sizeof(__u8)); param_parent->rta_len += NLA_ALIGN(rta_child->rta_len); p++; } class_parent->rta_len += NLA_ALIGN(param_parent->rta_len); } for (i = DCB_PG_ATTR_BW_ID_0; i < DCB_PG_ATTR_BW_ID_MAX; i++) { rta_child = add_rta(nlh, i, (void *)b, sizeof(__u8)); class_parent->rta_len += NLA_ALIGN(rta_child->rta_len); b++; } if (send_msg(nlh)) return -EIO; return(recv_msg(cmd, DCB_ATTR_PG_CFG, seq)); } /* returns 0: on error initializing interface * 1: if successful */ int init_drv_if(void) { int err = 0; if ((nl_sd = init_socket()) < 0) { LLDPAD_ERR("Error creating Netlink socket\n"); return err; } return 1; } int deinit_drv_if(void) { int rc; rc = fcntl(nl_sd, F_GETFD); if (rc != -1) { rc = close(nl_sd); if (rc) LLDPAD_ERR("Failed to close NETLINK socket (%d)\n", rc); nl_sd = 0; } return 0; } int set_dcbx_mode(char *ifname, __u8 mode) { struct nlmsghdr *nlh; int seq; nlh = start_msg(RTM_SETDCB, DCB_CMD_SDCBX); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_DCBX, (void *)&mode, sizeof(__u8)); if (send_msg(nlh)) return -EIO; return recv_msg(DCB_CMD_SDCBX, DCB_ATTR_DCBX, seq); } int get_dcb_capabilities(char *ifname, struct feature_support *dcb_capabilities) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent; struct rtattr *rta_child; int rval = 0; unsigned int seq; int i; u8 cap; memset((char *)dcb_capabilities, 0, sizeof(struct feature_support)); nlh = start_msg(RTM_GETDCB, DCB_CMD_GCAP); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_CAP, NULL, 0); rta_child = add_rta(nlh, DCB_CAP_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(seq); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GCAP) return -EIO; if (rta_parent->rta_type != DCB_ATTR_CAP) return -EIO; rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); for (i = 0; rta_parent > rta_child; i++) { cap = *(u8 *)NLA_DATA(rta_child); switch (rta_child->rta_type) { case DCB_CAP_ATTR_PG: dcb_capabilities->pg = cap; break; case DCB_CAP_ATTR_PFC: dcb_capabilities->pfc = cap; break; case DCB_CAP_ATTR_UP2TC: dcb_capabilities->up2tc_mappable = cap; break; case DCB_CAP_ATTR_PG_TCS: dcb_capabilities->traffic_classes = cap; break; case DCB_CAP_ATTR_PFC_TCS: dcb_capabilities->pfc_traffic_classes = cap; break; case DCB_CAP_ATTR_GSP: dcb_capabilities->gsp = cap; break; case DCB_CAP_ATTR_DCBX: dcb_capabilities->dcbx = cap; break; default: LLDPAD_DBG("unknown capability %d: %02x\n", rta_child->rta_type, cap); break; } rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } if (rta_parent != rta_child) LLDPAD_DBG("rta pointers are off\n"); free(nlh); return rval; } int get_dcb_numtcs(const char *ifname, u8 *pgtcs, u8 *pfctcs) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent; struct rtattr *rta_child; int rval = 0; unsigned int seq; int i; int found; char name[IFNAMSIZ]; nlh = start_msg(RTM_GETDCB, DCB_CMD_GNUMTCS); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; strncpy(name, ifname, sizeof(name)); add_rta(nlh, DCB_ATTR_IFNAME, (void *)name, strlen(name) + 1); rta_parent = add_rta(nlh, DCB_ATTR_NUMTCS, NULL, 0); rta_child = add_rta(nlh, DCB_NUMTCS_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(seq); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GNUMTCS) return -EIO; if (rta_parent->rta_type != DCB_ATTR_NUMTCS) return -EIO; rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); found = 0; for (i = 0; rta_parent > rta_child; i++) { switch (rta_child->rta_type) { case DCB_NUMTCS_ATTR_PG: if (! (found & 0x01) ) { *pgtcs = *(u8 *)NLA_DATA(rta_child); found += 1; } break; case DCB_NUMTCS_ATTR_PFC: if (!(found & 0x02)) { *pfctcs = *(u8 *)NLA_DATA(rta_child); found += 2; } break; default: LLDPAD_DBG("unknown capability %d: %02x\n", rta_child->rta_type, *(u8 *)NLA_DATA(rta_child)); break; } rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } if (rta_parent != rta_child) LLDPAD_DBG("rta pointers are off\n"); free(nlh); if (found != 3) rval = -EIO; return rval; } int get_hw_state(char *ifname, int *dcb_state) { int err = 0; __u8 state = 0; err = get_state(ifname, &state); if (err) LLDPAD_INFO("Adapter %s does not support DCB.\n", ifname); else LLDPAD_DBG("Adapter %s supports DCB.\n", ifname); *dcb_state = state; return err; } int set_hw_state(char *ifname, int dcb_state) { int err = 0; err = set_linkmode(ifname, dcb_state); if (err) LLDPAD_DBG("ERROR %s: set_linkmode dcbstate %i\n", __func__, dcb_state); return set_state(ifname, (__u8)dcb_state); } /* returns: 0 on success * 1 on failure */ int set_hw_pg(char *ifname, pgroup_attribs *pg_data, bool oper_mode) { int i, j; int rval = 0; struct tc_config tc[MAX_TRAFFIC_CLASSES]; __u8 bwg[MAX_BANDWIDTH_GROUPS]; pg_attribs pg_df_store; pgroup_attribs pg_df_data, *pg_temp; if (!oper_mode) { /* oper mode is false */ get_pg(DEF_CFG_STORE, &pg_df_store); memcpy(&pg_df_data.rx, &pg_df_store.rx, sizeof(pg_df_data.rx)); memcpy(&pg_df_data.tx, &pg_df_store.tx, sizeof(pg_df_data.tx)); pg_temp = &pg_df_data; } else { pg_temp = pg_data; } /* Configure TX PG per TC Settings */ for (i = 0; i < MAX_TRAFFIC_CLASSES; i++) tc[i].up_to_tc_bitmap = 0; for (i = 0; i < MAX_USER_PRIORITIES; i++) { for (j = 0; j < MAX_TRAFFIC_CLASSES; j++) { if (pg_temp->tx.up[i].pgid == j) { tc[j].up_to_tc_bitmap |= (1 << i); tc[j].prio_type = pg_temp->tx.up[i].strict_priority; tc[j].tc_percent = pg_temp->tx.up[i].percent_of_pg_cap; } } } for (i = 0; i < MAX_TRAFFIC_CLASSES; i++) { tc[i].bwgid = pg_temp->tx.up[i].bwgid; bwg[i] = pg_temp->tx.pg_percent[i]; LLDPAD_DBG("%s %s: (%i) TX bwgid %i up_to_tc %i " "prio %i percent %i\n", __func__, ifname, i, tc[i].bwgid, tc[i].up_to_tc_bitmap, tc[i].prio_type, tc[i].tc_percent); } rval = set_pg_cfg(ifname, &tc[0], &bwg[0], DCB_CMD_PGTX_SCFG); /* Configure RX PG per TC Settings */ for (i = 0; i < MAX_TRAFFIC_CLASSES; i++) tc[i].up_to_tc_bitmap = 0; for (i = 0; i < MAX_USER_PRIORITIES; i++) { for (j = 0; j < MAX_TRAFFIC_CLASSES; j++) { if (pg_temp->tx.up[i].pgid == j) { tc[j].up_to_tc_bitmap |= (1 << i); tc[j].prio_type = pg_temp->rx.up[i].strict_priority; tc[j].tc_percent = pg_temp->rx.up[i].percent_of_pg_cap; } } } for (i = 0; i < MAX_TRAFFIC_CLASSES; i++) { tc[i].bwgid = pg_temp->rx.up[i].bwgid; bwg[i] = pg_temp->rx.pg_percent[i]; LLDPAD_DBG("%s %s: (%i) RX bwgid %i up_to_tc %i " "prio %i percent %i\n", __func__, ifname, i, tc[i].bwgid, tc[i].up_to_tc_bitmap, tc[i].prio_type, tc[i].tc_percent); } rval |= set_pg_cfg(ifname, &tc[0], &bwg[0], DCB_CMD_PGRX_SCFG); return rval; } /* returns: 0 on success * non-zero on failure */ int set_hw_pfc(char *ifname, dcb_pfc_list_type pfc_data, bool oper_mode) { int i; __u8 pfc[MAX_TRAFFIC_CLASSES]; pfc_attribs pfc_df_store; pfc_type *pfc_temp; int rval; if (!oper_mode) /* oper mode is false */ { get_pfc(DEF_CFG_STORE, &pfc_df_store); pfc_temp = pfc_df_store.admin; } else { pfc_temp = pfc_data; } for (i = 0; i < MAX_TRAFFIC_CLASSES; i++) { if (pfc_temp[i]) pfc[i] = pfc_enabled; else pfc[i] = pfc_disabled; } rval = set_pfc_cfg(ifname, &pfc[0]); if (!rval) rval = set_pfc_state(ifname, (__u8)oper_mode); return rval; } /* returns: 0 on success * 1 on failure */ int set_hw_app(char *ifname, appgroup_attribs *app_data) { struct nlmsghdr *nlh; struct rtattr *rta_parent, *rta_child; int seq; LLDPAD_DBG("set_hw_app: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_SAPP); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_APP, NULL, 0); rta_child = add_rta(nlh, DCB_APP_ATTR_IDTYPE, (void *)&app_data->dcb_app_idtype, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_APP_ATTR_ID, (void *)&app_data->dcb_app_id, sizeof(__u16)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_APP_ATTR_PRIORITY, (void *)&app_data->dcb_app_priority, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_SAPP, DCB_ATTR_APP, seq)); } int run_cmd(char *cmd, ...) { char cbuf[128]; va_list args; va_start(args, cmd); vsprintf(cbuf, cmd, args); va_end(args); return system(cbuf); } int set_hw_all(char *ifname) { struct nlmsghdr *nlh; int status = 1; /* status is always true */ int retval = -EIO; int seq; nlh = start_msg(RTM_SETDCB, DCB_CMD_SET_ALL); if (NULL == nlh) return -EIO; seq = nlh->nlmsg_seq; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_SET_ALL, (void *)&status, sizeof(__u8)); if (send_msg(nlh)) return -EIO; retval = recv_msg(DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, seq); /* driver will respond with * 0 = hw config changes made - with link reset * 1 = no hw config changes were necessary * 2 = hw config changes made - with no link reset */ if (retval == 0) set_port_hw_resetting(ifname, 1); return 0; } bool check_port_dcb_mode(char *ifname) { int dcb_state = 0; if (get_hw_state(ifname, &dcb_state)) dcb_state = 0; if (dcb_state) { LLDPAD_DBG("config.c: %s dcb mode is ON.\n", ifname); return true; } else { LLDPAD_DBG("config.c: %s dcb mode is OFF. \n", ifname); return false; } } lldpad-0.9.46/lldp_ecp.c000066400000000000000000000717701215142747300150220ustar00rootroot00000000000000/****************************************************************************** Implementation of ECP according to 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include #include #include #include #include #include "eloop.h" #include "lldp.h" #include "lldp_evb.h" #include "lldp_qbg_utils.h" #include "lldp_vdp.h" #include "messages.h" #include "config.h" #include "lldp/l2_packet.h" #include "lldp_tlv.h" static void ecp_tx_run_sm(struct vdp_data *); static void ecp_rx_run_sm(struct vdp_data *); /* ecp_localchange_handler - triggers the processing of a local change * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called from ecp_somethingchangedlocal when a change is pending. Calls * the ECP tx station state machine. A oneshot handler. This detour is taken * to not having to call the ecp code from the vdp state machine. Instead, we * return to the event loop, giving other code a chance to do work. */ static void ecp_localchange_handler(UNUSED void *eloop_data, void *user_ctx) { struct vdp_data *vd; vd = (struct vdp_data *) user_ctx; if (vd->ecp.tx.localChange) { LLDPAD_DBG("%s:%s ecp.tx.localChange %i\n", __func__, vd->ecp.ifname, vd->ecp.tx.localChange); ecp_tx_run_sm(vd); } } /* ecp_start_localchange_timer - starts the ECP localchange timer * @vd: vdp_data for the interface * * returns 0 on success, -1 on error * * starts the ECP localchange timer when a localchange has been signaled from * the VDP state machine. */ static int ecp_start_localchange_timer(struct vdp_data *vd) { return eloop_register_timeout(0, ECP_LOCALCHANGE_TIMEOUT, ecp_localchange_handler, NULL, (void *) vd); } /* ecp_stop_localchange_timer - stop the ECP localchange timer * @vd: vdp_data for the interface * * returns the number of removed handlers * * stops the ECP localchange timer. Used e.g. when the host interface goes down. */ static int ecp_stop_localchange_timer(struct vdp_data *vd) { LLDPAD_DBG("%s:%s stopping ecp localchange timer\n", __func__, vd->ecp.ifname); return eloop_cancel_timeout(ecp_localchange_handler, NULL, (void *) vd); } /* ecp_ackTimer_expired - checks for expired ack timer * @vd: vdp_data for interface * * returns true or false * * returns true if ack timer has expired, false otherwise. */ static bool ecp_ackTimer_expired(struct vdp_data *vd) { return (vd->ecp.ackTimer == 0); } /* ecp_ack_timeout_handler - handles the ack timer expiry * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called when the ECP timer has expired. Calls the ECP station state machine. */ static void ecp_ack_timeout_handler(UNUSED void *eloop_data, void *user_ctx) { struct vdp_data *vd; vd = (struct vdp_data *) user_ctx; if (vd->ecp.ackTimer > 0) vd->ecp.ackTimer -= ECP_ACK_TIMER_DEFAULT; if (ecp_ackTimer_expired(vd) == true) { LLDPAD_DBG("%s:%s ecp_ackTimer_expired (%i)\n", __func__, vd->ecp.ifname, vd->ecp.ackTimer); ecp_tx_run_sm(vd); } else { LLDPAD_DBG("%s:%s BUG! handler called but" "vdp->ecp.ackTimer not expired (%i)\n", __func__, vd->ecp.ifname, vd->ecp.ackTimer); } } /* ecp_start_ack_timer - starts the ECP ack timer * @vd: vdp_data for the interface * * returns 0 on success, -1 on error * * starts the ECP ack timer when a frame has been sent out. */ static int ecp_start_ack_timer(struct vdp_data *vd) { return eloop_register_timeout(0, ECP_ACK_TIMER_DEFAULT, ecp_ack_timeout_handler, NULL, (void *) vd); } /* ecp_stop_ack_timer - stop the ECP ack timer * @vd: vdp_data for the interface * * returns the number of removed handlers * * stops the ECP ack timer. Used e.g. when the host interface goes down. */ static int ecp_stop_ack_timer(struct vdp_data *vd) { LLDPAD_DBG("%s:%s stopping ecp ack timer\n", __func__, vd->ecp.ifname); return eloop_cancel_timeout(ecp_ack_timeout_handler, NULL, (void *) vd); } /* ecp_tx_stop_ackTimer - stop the ECP ack timer * @vd: currently used port * * returns the number of removed handlers * * stops the ECP ack timer. used when a ack frame for the port has been * received. */ static void ecp_tx_stop_ackTimer(struct vdp_data *vd) { vd->ecp.ackTimer = ECP_ACK_TIMER_STOPPED; LLDPAD_DBG("%s:%s stopped ecp ack timer\n", __func__, vd->ecp.ifname); ecp_stop_ack_timer(vd); } int ecp_deinit(char *ifname) { struct vdp_data *vd; LLDPAD_DBG("%s:%s stopping ECP\n", __func__, ifname); vd = vdp_data(ifname); if (!vd) { LLDPAD_ERR("%s:%s unable to find vd\n", __func__, ifname); return -1; } ecp_stop_ack_timer(vd); ecp_stop_localchange_timer(vd); ecp_tx_stop_ackTimer(vd); return 0; } static const char *ecp_tx_states[] = { "ECP_TX_INIT_TRANSMIT", "ECP_TX_TRANSMIT_ECPDU", "ECP_TX_WAIT_FOR_ACK", "ECP_TX_REQUEST_PDU" }; /* ecp_somethingChangedLocal - set flag if port has changed * @vd: port to set the flag for * @mode: mode to set the flag to * * no return value * * set the localChange flag with a mode to indicate a port has changed. * used to signal an ecpdu needs to be sent out. */ void ecp_somethingChangedLocal(struct vdp_data *vd, bool flag) { if (!vd) return; LLDPAD_DBG("%s:%s vd->ecp.tx.localChange to %s\n", __func__, vd->ecp.ifname, (flag == true) ? "true" : "false"); vd->ecp.tx.localChange = flag; ecp_start_localchange_timer(vd); } /* * Append some data at the end of the transmit data buffer. Make sure the * End TLV always fits into the buffer. */ static u8 end_tlv[2] = { 0x0, 0x0 }; /* END TLV */ static int ecp_append(u8 *buffer, u32 *pos, void *data, u32 len) { if (*pos + len > ETH_FRAME_LEN - sizeof end_tlv) return 0; memcpy(buffer + *pos, data, len); *pos += len; return 1; } /* ecp_build_ECPDU - create an ecp protocol data unit * @vd: currently used port * * returns true on success, false on failure * * creates the frame header with the ports mac address, the ecp header with REQ * plus a list of packed TLVs created from the profiles on this * port. */ static bool ecp_build_ECPDU(struct vdp_data *vd) { struct l2_ethhdr eth; struct ecp_hdr ecp_hdr; u8 own_addr[ETH_ALEN]; u32 fb_offset = 0; struct packed_tlv *ptlv = NULL; struct vsi_profile *p; int rc; /* TODO: use LLDP group MAC addresses to support * S-channels/multichannel */ memcpy(eth.h_dest, nearest_bridge, ETH_ALEN); l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr); memcpy(eth.h_source, &own_addr, ETH_ALEN); eth.h_proto = htons(ETH_P_ECP); memset(vd->ecp.tx.frame, 0, sizeof vd->ecp.tx.frame); ecp_append(vd->ecp.tx.frame, &fb_offset, (void *)ð, sizeof eth); ecp_hdr.oui[0] = 0x0; ecp_hdr.oui[1] = 0x1b; ecp_hdr.oui[2] = 0x3f; ecp_hdr.pad1 = 0x0; ecp_hdr.subtype = ECP_SUBTYPE; ecp_hdr.mode = ECP_REQUEST; vd->ecp.lastSequence++; ecp_hdr.seqnr = htons(vd->ecp.lastSequence); ecp_append(vd->ecp.tx.frame, &fb_offset, (void *)&ecp_hdr, sizeof ecp_hdr); /* create packed_tlvs for all profiles on this interface */ LIST_FOREACH(p, &vd->profile_head, profile) { if (!p->localChange) { LLDPAD_DBG("%s:%s skipping unchanged profile\n", __func__, vd->ecp.ifname); continue; } ptlv = vdp_gettlv(vd, p); if (!ptlv) { LLDPAD_DBG("%s:%s ptlv not created\n", __func__, vd->ecp.ifname); continue; } rc = ecp_append(vd->ecp.tx.frame, &fb_offset, ptlv->tlv, ptlv->size); ptlv = free_pkd_tlv(ptlv); if (rc) p->seqnr = vd->ecp.lastSequence; else break; } ecp_append(vd->ecp.tx.frame, &fb_offset, end_tlv, sizeof end_tlv); vd->ecp.tx.frame_len = MAX(fb_offset, (unsigned)ETH_ZLEN); return true; } /* ecp_tx_Initialize - initializes the ecp tx state machine * @vd: currently used port * * no return value * * initializes some variables for the ecp tx state machine. */ static void ecp_tx_Initialize(struct vdp_data *vd) { memset(vd->ecp.tx.frame, 0, sizeof vd->ecp.tx.frame); ecp_somethingChangedLocal(vd, true); vd->ecp.lastSequence = ECP_SEQUENCE_NR_START; vd->ecp.stats.statsFramesOutTotal = 0; vd->ecp.ackTimer = ECP_ACK_TIMER_STOPPED; vd->ecp.retries = 0; } /* ecp_txFrame - transmit ecp frame * @vd: currently used port * * returns the number of characters sent on success, -1 on failure * * sends out the frame stored in the frame structure using l2_packet_send. */ static u8 ecp_txFrame(struct vdp_data *vd) { int status = 0; status = l2_packet_send(vd->ecp.l2, (u8 *)&nearest_bridge, htons(ETH_P_ECP), vd->ecp.tx.frame, vd->ecp.tx.frame_len); vd->ecp.stats.statsFramesOutTotal++; vd->ecp.tx.frame_len = 0; return status; } /* ecp_tx_create_frame - create ecp frame * @vd: currently used port * * no return value */ static void ecp_tx_create_frame(struct vdp_data *vd) { /* send REQs */ if (vd->ecp.tx.localChange) { int ret; LLDPAD_DBG("%s:%s sending REQs\n", __func__, vd->ecp.ifname); ret = ecp_build_ECPDU(vd); /* ECPDU construction succesful, send out frame */ if (ret == true) { hexdump_frame(vd->ecp.ifname, "frame-out", vd->ecp.tx.frame, vd->ecp.tx.frame_len); ecp_txFrame(vd); } } ecp_somethingChangedLocal(vd, false); } /* ecp_tx_start_ackTimer - starts the ECP ack timer * @vd: vdp_data to process * * returns 0 on success, -1 on error * * starts the ack timer when a frame has been sent out. */ static void ecp_tx_start_ackTimer(struct vdp_data *vd) { vd->ecp.ackTimer = ECP_ACK_TIMER_DEFAULT; LLDPAD_DBG("%s-%s: starting ecp ack timer\n", __func__, vd->ifname); ecp_start_ack_timer(vd); } /* ecp_tx_change_state - changes the ecp tx sm state * @vd: currently used port * @newstate: new state for the sm * * no return value * * checks state transistion for consistency and finally changes the state of * the profile. */ static void ecp_tx_change_state(struct vdp_data *vd, u8 newstate) { switch(newstate) { case ECP_TX_INIT_TRANSMIT: break; case ECP_TX_TRANSMIT_ECPDU: assert((vd->ecp.tx.state == ECP_TX_INIT_TRANSMIT) || (vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK) || (vd->ecp.tx.state == ECP_TX_REQUEST_PDU)); break; case ECP_TX_WAIT_FOR_ACK: assert(vd->ecp.tx.state == ECP_TX_TRANSMIT_ECPDU); break; case ECP_TX_REQUEST_PDU: assert(vd->ecp.tx.state == ECP_TX_WAIT_FOR_ACK); break; default: LLDPAD_ERR("%s: LLDP TX state machine invalid state %d\n", vd->ifname, newstate); } LLDPAD_DBG("%s-%s: state change %s -> %s\n", __func__, vd->ifname, ecp_tx_states[vd->ecp.tx.state], ecp_tx_states[newstate]); vd->ecp.tx.state = newstate; return; } /* ecp_set_tx_state - sets the ecp tx sm state * @vd: currently used port * * returns true or false * * switches the state machine to the next state depending on the input * variables. returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool ecp_set_tx_state(struct vdp_data *vd) { struct port *port = port_find_by_name(vd->ifname); if (!port) { LLDPAD_ERR("%s: port not found\n", __func__); return 0; } if ((port->portEnabled == false) && (port->prevPortEnabled == true)) { LLDPAD_ERR("set_tx_state: port was disabled\n"); ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT); } port->prevPortEnabled = port->portEnabled; switch (vd->ecp.tx.state) { case ECP_TX_INIT_TRANSMIT: if (port->portEnabled && (vd->enabletx == true) && vd->ecp.tx.localChange) { ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); return true; } return false; case ECP_TX_TRANSMIT_ECPDU: if (vd->enabletx == false) { ecp_tx_change_state(vd, ECP_TX_INIT_TRANSMIT); return true; } ecp_tx_change_state(vd, ECP_TX_WAIT_FOR_ACK); return false; case ECP_TX_WAIT_FOR_ACK: if (ecp_ackTimer_expired(vd)) { vd->ecp.retries++; if (vd->ecp.retries < ECP_MAX_RETRIES) { ecp_somethingChangedLocal(vd, true); ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); return true; } if (vd->ecp.retries == ECP_MAX_RETRIES) { LLDPAD_DBG("%s-%s: retries expired\n", __func__, vd->ifname); ecp_tx_stop_ackTimer(vd); ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU); return true; } } if (vd->ecp.ackReceived && vd->ecp.seqECPDU == vd->ecp.lastSequence) { vd->ecp.ackReceived = false; if (vdp_vsis_pending(vd)) { LLDPAD_DBG("%s-%s: still work pending\n", __func__, vd->ifname); ecp_somethingChangedLocal(vd, true); } ecp_tx_change_state(vd, ECP_TX_REQUEST_PDU); return true; } return false; case ECP_TX_REQUEST_PDU: if (vd->ecp.tx.localChange) { ecp_tx_change_state(vd, ECP_TX_TRANSMIT_ECPDU); return true; } return false; default: LLDPAD_ERR("%s: LLDP TX state machine in invalid state %d\n", vd->ifname, vd->ecp.tx.state); return false; } } /* ecp_tx_run_sm - state machine for ecp tx * @vd: currently used vdp_data * * no return value * * runs the state machine for ecp tx. */ void ecp_tx_run_sm(struct vdp_data *vd) { do { LLDPAD_DBG("%s-%s: ecp_tx - %s\n", __func__, vd->ifname, ecp_tx_states[vd->ecp.tx.state]); switch(vd->ecp.tx.state) { case ECP_TX_INIT_TRANSMIT: ecp_tx_Initialize(vd); break; case ECP_TX_TRANSMIT_ECPDU: ecp_tx_create_frame(vd); ecp_tx_start_ackTimer(vd); ecp_somethingChangedLocal(vd, false); break; case ECP_TX_WAIT_FOR_ACK: if (vd->ecp.ackReceived) { LLDPAD_DBG("%s-%s: ECP_TX_WAIT_FOR_ACK " "ackReceived seqECPDU %#x " "lastSequence %#x\n", __func__, vd->ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence); ecp_somethingChangedLocal(vd, false); ecp_tx_stop_ackTimer(vd); } break; case ECP_TX_REQUEST_PDU: vd->ecp.retries = 0; LLDPAD_DBG("%s-%s: ECP_TX_REQUEST_PDU lastSeq %#x\n", __func__, vd->ifname, vd->ecp.lastSequence); break; default: LLDPAD_ERR("%s: LLDP TX state machine in invalid state %d\n", vd->ifname, vd->ecp.tx.state); } } while (ecp_set_tx_state(vd) == true); } static const char *ecp_rx_states[] = { "ECP_RX_IDLE", "ECP_RX_INIT_RECEIVE", "ECP_RX_RECEIVE_WAIT", "ECP_RX_RECEIVE_ECPDU", "ECP_RX_SEND_ACK", "ECP_RX_RESEND_ACK", }; /* ecp_rx_Initialize - initializes the ecp rx state machine * @vd: vd for the state machine * * no return value * * initialize some variables, get rid of old frame if necessary */ static void ecp_rx_Initialize(struct vdp_data *vd) { vd->ecp.rx.rcvFrame = false; vd->ecp.ackReceived = false; vd->ecp.rx.frame_len = 0; } /* ecp_rx_SendAckFrame - send ack frame * @vd: port used by ecp * * currently always returns 0 * * copies current received frame over to frame out, fills in address of this * port and set mode field to ACK. used by ecp_rx_send_ack_frame. */ static int ecp_rx_SendAckFrame(struct vdp_data *vd) { u16 tlv_offset = 0; struct ecp_hdr *ecp_hdr; struct l2_ethhdr *hdr; u8 own_addr[ETH_ALEN]; LLDPAD_DBG("%s:%s acking frame\n", __func__, vd->ecp.ifname); /* copy over to transmit buffer */ memcpy(vd->ecp.tx.frame, vd->ecp.rx.frame, vd->ecp.rx.frame_len); vd->ecp.tx.frame_len = vd->ecp.rx.frame_len; /* use my own addr to send ACK */ hdr = (struct l2_ethhdr *)vd->ecp.tx.frame; l2_packet_get_own_src_addr(vd->ecp.l2,(u8 *)&own_addr); memcpy(hdr->h_source, &own_addr, ETH_ALEN); tlv_offset = sizeof(struct l2_ethhdr); ecp_hdr = (struct ecp_hdr *)&vd->ecp.tx.frame[tlv_offset]; ecp_hdr->mode = ECP_ACK; tlv_offset = sizeof(struct l2_ethhdr) + sizeof(struct ecp_hdr); LLDPAD_DBG("%s:%s zeroing out rest of ack frame from %i to %i\n", __func__, vd->ecp.ifname, tlv_offset, vd->ecp.rx.frame_len); memset(&vd->ecp.tx.frame[tlv_offset], 0, vd->ecp.rx.frame_len - tlv_offset); return 0; } /* ecp_rx_send_ack_frame - send out ack frame for received frame * @vd: vd for the state machine * * no return value * * creates an ack frame for a just received frame, prints the about to be * sent frame and finally transmits it. */ void ecp_rx_send_ack_frame(struct vdp_data *vd) { ecp_rx_SendAckFrame(vd); hexdump_frame(vd->ecp.ifname, "frame-ack", vd->ecp.tx.frame, vd->ecp.tx.frame_len); ecp_txFrame(vd); } /* ecp_rx_ReceiveFrame - receive ecp frame * @ctx: rx callback context, struct vd * in this case * @ifindex: index of interface * @buf: buffer which contains the frame just received * @len: size of buffer (frame) * * no return value * * creates a local copy of the buffer and checks the header. keeps some * statistics about ecp frames. Checks if it is a request or an ack frame * and branches to ecp rx or ecp tx state machine. */ static void ecp_rx_ReceiveFrame(void *ctx, UNUSED int ifindex, const u8 *buf, size_t len) { struct vdp_data *vd; struct port *port; u8 frame_error = 0; u16 tlv_offset; struct l2_ethhdr *hdr; struct l2_ethhdr example_hdr,*ex; struct ecp_hdr *ecp_hdr; if (!ctx) { LLDPAD_WARN("%s: no ctx - can't process frame\n", __func__); return; } vd = (struct vdp_data *)ctx; port = port_find_by_name(vd->ifname); if (port == NULL) return; LLDPAD_DBG("%s:%s received packet with size %i\n", __func__, vd->ecp.ifname, (int)len); if (vd->enabletx == false) return; if (vd->ecp.rx.frame_len == len && (memcmp(buf, vd->ecp.rx.frame, len) == 0)) { vd->ecp.stats.statsFramesInTotal++; return; } memset(vd->ecp.rx.frame, 0, len); memcpy(vd->ecp.rx.frame, buf, len); vd->ecp.rx.frame_len = (u16)len; ex = &example_hdr; memcpy(ex->h_dest, nearest_bridge, ETH_ALEN); ex->h_proto = htons(ETH_P_ECP); hdr = (struct l2_ethhdr *)vd->ecp.rx.frame; if ((memcmp(hdr->h_dest, ex->h_dest, ETH_ALEN) != 0)) { LLDPAD_ERR("%s:%s ERROR multicast address error in incoming frame." " Dropping frame.\n", __func__, vd->ecp.ifname); frame_error++; return; } if (hdr->h_proto != example_hdr.h_proto) { LLDPAD_ERR("%s:%s ERROR ethertype %#x not ECP ethertype", __func__, vd->ecp.ifname, htons(hdr->h_proto)); frame_error++; return; } if (!frame_error) { vd->ecp.stats.statsFramesInTotal++; vd->ecp.rx.rcvFrame = true; } tlv_offset = sizeof(struct l2_ethhdr); ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.frame[tlv_offset]; vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr); hexdump_frame(vd->ecp.ifname, "frame-in", vd->ecp.rx.frame, vd->ecp.rx.frame_len); switch(ecp_hdr->mode) { case ECP_REQUEST: LLDPAD_DBG("%s:%s received REQ frame\n", __func__, vd->ecp.ifname); vd->ecp.ackReceived = false; ecp_rx_run_sm(vd); break; case ECP_ACK: LLDPAD_DBG("%s:%s received ACK frame\n", __func__, vd->ecp.ifname); vd->ecp.ackReceived = true; vdp_ack_profiles(vd, vd->ecp.seqECPDU); ecp_tx_run_sm(vd); vd->ecp.ackReceived = false; break; default: LLDPAD_ERR("%s:%s ERROR: unknown mode %i\n", __func__, vd->ecp.ifname, ecp_hdr->mode); return; } } /* ecp_rx_change_state - changes the ecp rx sm state * @vd: currently used port * @newstate: new state for the sm * * no return value * * checks state transistion for consistency and finally changes the state of * the profile. */ static void ecp_rx_change_state(struct vdp_data *vd, u8 newstate) { switch(newstate) { case ECP_RX_IDLE: break; case ECP_RX_INIT_RECEIVE: break; case ECP_RX_RECEIVE_WAIT: assert((vd->ecp.rx.state == ECP_RX_INIT_RECEIVE) || (vd->ecp.rx.state == ECP_RX_IDLE) || (vd->ecp.rx.state == ECP_RX_SEND_ACK) || (vd->ecp.rx.state == ECP_RX_RESEND_ACK)); break; case ECP_RX_RECEIVE_ECPDU: assert(vd->ecp.rx.state == ECP_RX_RECEIVE_WAIT); break; case ECP_RX_SEND_ACK: assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU); break; case ECP_RX_RESEND_ACK: assert(vd->ecp.rx.state == ECP_RX_RECEIVE_ECPDU); break; default: LLDPAD_ERR("%s:%s LLDP RX state machine invalid state %d\n", __func__, vd->ecp.ifname, newstate); } LLDPAD_DBG("%s:%s state change %s -> %s\n", __func__, vd->ecp.ifname, ecp_rx_states[vd->ecp.rx.state], ecp_rx_states[newstate]); vd->ecp.rx.state = newstate; } /* ecp_init - initialize ecp module * @ifname: interface for which the module is initialized * * returns 0 on success, -1 on error * * finds the port to the interface name, sets up the receive handle for * incoming ecp frames and initializes the ecp rx and tx state machines. * should usually be called when a successful exchange of EVB TLVs has been * made and ECP and VDP protocols are supported by both sides. */ int ecp_init(char *ifname) { struct vdp_data *vd; LLDPAD_DBG("%s:%s starting ECP\n", __func__, ifname); vd = vdp_data(ifname); if (!vd) { LLDPAD_ERR("%s:%s unable to find vd\n", __func__, ifname); return -1; } if (!vd->ecp.l2) vd->ecp.l2 = l2_packet_init(vd->ifname, NULL, ETH_P_ECP, ecp_rx_ReceiveFrame, vd, 1); if (!vd->ecp.l2) { LLDPAD_ERR("%s:%s failed to access layer 2 access ETH_P_ECP\n", __func__, ifname); return -1; } strncpy(vd->ecp.ifname, ifname, sizeof vd->ecp.ifname); ecp_rx_change_state(vd, ECP_RX_IDLE); ecp_rx_run_sm(vd); ecp_somethingChangedLocal(vd, true); return 0; } /* ecp_rx_validate_frame - validates received frame * @vd: vdp_data used by ecp * * no return value * * checks wether received frame has correct subtype and mode */ static void ecp_rx_validate_frame(struct vdp_data *vd) { u16 tlv_offset = 0; struct ecp_hdr *ecp_hdr; LLDPAD_DBG("%s:%s validating frame\n", __func__, vd->ecp.ifname); tlv_offset = sizeof(struct l2_ethhdr); ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.frame[tlv_offset]; LLDPAD_DBG("%s:%s ecp packet with subtype %#x mode %#x seq %#04x\n", __func__, vd->ecp.ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr)); if (ecp_hdr->subtype != ECP_SUBTYPE) { LLDPAD_ERR("%s:%s ERROR: unknown subtype\n", __func__, vd->ecp.ifname); return; } if ((ecp_hdr->oui[0] != 0x0) || (ecp_hdr->oui[1] != 0x1b) || (ecp_hdr->oui[2] != 0x3f)) { LLDPAD_ERR("%s:%s ERROR: incorrect OUI 0x%02x%02x%02x\n", __func__, vd->ecp.ifname, ecp_hdr->oui[0], ecp_hdr->oui[1], ecp_hdr->oui[2]); return; } switch(ecp_hdr->mode) { case ECP_REQUEST: break; case ECP_ACK: break; default: LLDPAD_ERR("%s:%s ERROR: unknown mode %i\n", __func__, vd->ecp.ifname, ecp_hdr->mode); return; } /* FIXME: also done in ecp_rx_ReceiveFrame, * are both necessary ? */ vd->ecp.seqECPDU = ntohs(ecp_hdr->seqnr); } /* ecp_rx_ProcessFrame - process received ecp frames * @vd: currently used port * * no return value * * walks through the packed vsi tlvs in an ecp frame, extracts them * and passes them to the VDP ULP with vdp_indicate. */ static void ecp_rx_ProcessFrame(struct vdp_data *vd) { u16 tlv_cnt = 0; u8 tlv_type = 0; u16 tlv_length = 0; u16 tlv_offset = 0; u16 *tlv_head_ptr = NULL; u8 frame_error = 0; bool tlv_stored = false; struct ecp_hdr *ecp_hdr; int vdp_called; LLDPAD_DBG("%s:%s processing frame\n", __func__, vd->ecp.ifname); tlv_offset = sizeof(struct l2_ethhdr); ecp_hdr = (struct ecp_hdr *)&vd->ecp.rx.frame[tlv_offset]; LLDPAD_DBG("%s:%s ecp packet with subtype %#x mode %#x seq %#04x\n", __func__, vd->ifname, ecp_hdr->subtype, ecp_hdr->mode, ntohs(ecp_hdr->seqnr)); if (ecp_hdr->mode == ECP_ACK) return; /* processing of VSI_TLVs starts here */ tlv_offset += sizeof(struct ecp_hdr); vdp_called = 0; do { tlv_cnt++; if (tlv_offset > vd->ecp.rx.frame_len) { LLDPAD_ERR("%s:%s ERROR: Frame overrun! tlv_offset %i" " frame_len %i cnt %i\n", __func__, vd->ecp.ifname, tlv_offset, vd->ecp.rx.frame_len, tlv_cnt); frame_error++; goto out; } if (tlv_offset + 2 > vd->ecp.rx.frame_len) { LLDPAD_DBG("%s:%s tlv EOF problem size=%d offset=%d\n", __func__, vd->ecp.ifname, vd->ecp.rx.frame_len, tlv_offset); frame_error++; goto out; } tlv_head_ptr = (u16 *)&vd->ecp.rx.frame[tlv_offset]; tlv_length = htons(*tlv_head_ptr) & 0x01FF; tlv_type = (u8)(htons(*tlv_head_ptr) >> 9); u16 tmp_offset = tlv_offset + tlv_length; if (tmp_offset > vd->ecp.rx.frame_len) { LLDPAD_ERR("%s:%s ERROR: Frame overflow: offset=%d " "rx.size=%d\n", __func__, vd->ecp.ifname, tmp_offset, vd->ecp.rx.frame_len); frame_error++; goto out; } u8 *info = (u8 *)&vd->ecp.rx.frame[tlv_offset + sizeof(*tlv_head_ptr)]; struct unpacked_tlv *tlv = create_tlv(); if (!tlv) { LLDPAD_DBG("%s:%s failed malloc for incoming TLV\n", __func__, vd->ecp.ifname); goto out; } if ((tlv_length == 0) && (tlv->type != TYPE_0)) { LLDPAD_DBG("%s:%s tlv_length == 0\n", __func__, vd->ecp.ifname); free_unpkd_tlv(tlv); goto out; } tlv->type = tlv_type; tlv->length = tlv_length; tlv->info = (u8 *)malloc(tlv_length); if (tlv->info) { memset(tlv->info,0, tlv_length); memcpy(tlv->info, info, tlv_length); } else { LLDPAD_DBG("%s:%s failed malloc for incoming TLV info\n", __func__, vd->ecp.ifname); free_unpkd_tlv(tlv); goto out; } /* Validate the TLV */ tlv_offset += sizeof(*tlv_head_ptr) + tlv_length; if (tlv->type == TYPE_127) { /* private TLV */ /* give VSI TLV to VDP */ if (!vdp_indicate(vd, tlv)) { tlv_stored = true; ++vdp_called; } else { /* TODO * put it in a list and try again later until * timer and retries have expired */ tlv_stored = false; } } if ((tlv->type != TYPE_0) && !tlv_stored) { LLDPAD_DBG("%s:%s TLV (%u) was not stored (%p)\n", __func__, vd->ecp.ifname, tlv->type, tlv); tlv = free_unpkd_tlv(tlv); vd->ecp.stats.statsTLVsUnrecognizedTotal++; } tlv = NULL; tlv_stored = false; } while (tlv_offset < vd->ecp.rx.frame_len); out: if (frame_error) { vd->ecp.stats.statsFramesDiscardedTotal++; vd->ecp.stats.statsFramesInErrorsTotal++; } if (vdp_called) vdp_advance_sm(vd); } /* ecp_set_rx_state - sets the ecp rx sm state * @vd: currently used port * * returns true or false * * switches the state machine to the next state depending on the input * variables. returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool ecp_set_rx_state(struct vdp_data *vd) { struct port *port = port_find_by_name(vd->ifname); if (!port) return false; if (port->portEnabled == false) ecp_rx_change_state(vd, ECP_RX_IDLE); switch(vd->ecp.rx.state) { case ECP_RX_IDLE: if (port->portEnabled == true) { ecp_rx_change_state(vd, ECP_RX_INIT_RECEIVE); return true; } return false; case ECP_RX_INIT_RECEIVE: if (vd->enabletx == true) { ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT); return true; } return false; case ECP_RX_RECEIVE_WAIT: if (vd->enabletx == false) { ecp_rx_change_state(vd, ECP_RX_IDLE); return true; } if (vd->ecp.rx.rcvFrame == true) { ecp_rx_change_state(vd, ECP_RX_RECEIVE_ECPDU); return true; } return false; case ECP_RX_RECEIVE_ECPDU: if (vd->ecp.seqECPDU == vd->ecp.lastSequence) { LLDPAD_DBG("%s:%s seqECPDU %x, lastSequence %x\n", __func__, vd->ecp.ifname, vd->ecp.seqECPDU, vd->ecp.lastSequence); ecp_rx_change_state(vd, ECP_RX_RESEND_ACK); return true; } if (vd->ecp.seqECPDU != vd->ecp.lastSequence) { ecp_rx_change_state(vd, ECP_RX_RESEND_ACK); return true; } return false; case ECP_RX_SEND_ACK: case ECP_RX_RESEND_ACK: ecp_rx_change_state(vd, ECP_RX_RECEIVE_WAIT); return false; default: LLDPAD_DBG("%s:%s ECP RX state machine in invalid state %d\n", __func__, vd->ecp.ifname, vd->ecp.rx.state); return false; } } /* ecp_rx_run_sm - state machine for ecp rx * @vd: currently used port * * no return value * * runs the state machine for ecp rx. */ static void ecp_rx_run_sm(struct vdp_data *vd) { ecp_set_rx_state(vd); do { LLDPAD_DBG("%s:%s ecp_rx - %s\n", __func__, vd->ecp.ifname, ecp_rx_states[vd->ecp.tx.state]); switch(vd->ecp.rx.state) { case ECP_RX_IDLE: break; case ECP_RX_INIT_RECEIVE: ecp_rx_Initialize(vd); break; case ECP_RX_RECEIVE_WAIT: break; case ECP_RX_RECEIVE_ECPDU: vd->ecp.rx.rcvFrame = false; ecp_rx_validate_frame(vd); break; case ECP_RX_SEND_ACK: ecp_rx_ProcessFrame(vd); break; case ECP_RX_RESEND_ACK: ecp_rx_ProcessFrame(vd); if (!vd->ecp.ackReceived) { ecp_rx_send_ack_frame(vd); } break; default: LLDPAD_DBG("%s:%s ECP RX state machine in invalid state %d\n", __func__, vd->ecp.ifname, vd->ecp.rx.state); } } while (ecp_set_rx_state(vd) == true); } lldpad-0.9.46/lldp_evb.c000066400000000000000000000304431215142747300150170ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #define _GNU_SOURCE #include #include #include "lldp.h" #include "lldp_tlv.h" #include "lldp_evb.h" #include "lldp_evb_cmds.h" #include "lldp_vdp.h" #include "messages.h" #include "config.h" extern struct lldp_head lldp_head; struct evb_data *evb_data(char *ifname, enum agent_type type) { struct evb_user_data *ud; struct evb_data *ed = NULL; ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_EVB); if (ud) { LIST_FOREACH(ed, &ud->head, entry) { if (!strncmp(ifname, ed->ifname, IFNAMSIZ) && (type == ed->agenttype)) return ed; } } return NULL; } static void evb_print_tlvinfo(char *ifname, struct tlv_info_evb *tlv) { LLDPAD_INFO("%s evb supported/configured forwarding mode: %#02x/%#02x " "capabilities: %#02x/%#02x vsis: %04i/%04i " "rte: %02i\n", ifname, tlv->smode, tlv->cmode, tlv->scap, tlv->ccap, ntohs(tlv->svsi), ntohs(tlv->cvsi), tlv->rte); } static void evb_dump_tlv(char *ifname, struct unpacked_tlv *tlv) { int i, left = 0; char buffer[256]; for (i = 0; i < tlv->length; i++) { int c; c = snprintf(buffer + left, sizeof buffer - left, "%02x ", tlv->info[i]); if (c < 0 || (c >= (int)sizeof buffer - left)) break; else left += c; } LLDPAD_DBG("%s:%s type %i length %i info %s\n", __func__, ifname, tlv->type, tlv->length, buffer); } /* * Checks values received in TLV and takes over some values. * Sets the new suggestion in member tie to be send out to switch. */ static void evb_update_tlv(struct evb_data *ed) { struct tlv_info_evb *recv = &ed->last; u8 valid = recv->smode & (LLDP_EVB_CAPABILITY_FORWARD_STANDARD | LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY); /* * Waiting for valid packets to pour in if valid packet was received, * - check parameters with what we have offered for this interface, * - fill structure with data, * - enable local tx */ if (!valid) { LLDPAD_ERR("Neither standard nor rr set as forwarding modes "); LLDPAD_ERR("for interface - %s\n", ed->ifname); return; } /* * Check bridge capabilities against local policy * if bridge supports RR and we support it as well, request it * by setting smode in tlv to be sent out (ed->tie.smode) */ if ((recv->smode & ed->policy.smode) & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) ed->tie.cmode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; else ed->tie.cmode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; /* If both sides support RTE, support and configure it */ if ((recv->scap & ed->policy.scap) & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) ed->tie.ccap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; else ed->tie.ccap &= ~LLDP_EVB_CAPABILITY_PROTOCOL_RTE; if ((ed->tie.scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) && (recv->rte > 0) && (ed->policy.rte > 0)) ed->tie.rte = MAX(ed->policy.rte, recv->rte); /* If both sides support ECP, set it */ if ((recv->scap & ed->policy.scap) & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) ed->tie.ccap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; else ed->tie.ccap &= ~LLDP_EVB_CAPABILITY_PROTOCOL_ECP; /* If both sides support VDP, set it. Also set number of VSIs */ if ((recv->scap & ed->policy.scap) & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) { ed->tie.ccap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; ed->tie.cvsi = htons(vdp_vsis(ed->ifname)); ed->tie.svsi = ed->policy.svsi; } else { ed->tie.ccap &= ~LLDP_EVB_CAPABILITY_PROTOCOL_VDP; ed->tie.cvsi = 0; ed->tie.svsi = 0; } } /* * Build the packed EVB TLV. * Returns a pointer to the packed tlv or 0 on failure. */ static struct packed_tlv *evb_build_tlv(struct evb_data *ed) { struct packed_tlv *ptlv = 0; u8 infobuf[sizeof(struct tlv_info_evb)]; struct unpacked_tlv tlv = { .type = ORG_SPECIFIC_TLV, .length = sizeof(struct tlv_info_evb), .info = infobuf }; if (!ed->txmit) return ptlv; evb_update_tlv(ed); memcpy(tlv.info, &ed->tie, tlv.length); ptlv = pack_tlv(&tlv); if (ptlv) { LLDPAD_DBG("%s:%s TLV about to be sent out:\n", __func__, ed->ifname); evb_dump_tlv(ed->ifname, &tlv); } else LLDPAD_DBG("%s:%s failed to pack EVB TLV\n", __func__, ed->ifname); return ptlv; } /* * Function call to build and return module specific packed EVB TLV. * Returned packed_tlv is free'ed by caller of this function. */ static struct packed_tlv *evb_gettlv(struct port *port, struct lldp_agent *agent) { struct evb_data *ed; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb_data(port->ifname, agent->type); if (!ed) { LLDPAD_ERR("%s:%s agent %d failed\n", __func__, port->ifname, agent->type); return 0; } return evb_build_tlv(ed); } /* * evb_rchange: process received EVB TLV LLDPDU * * TLV not consumed on error */ static int evb_rchange(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { struct evb_data *ed; u8 oui_subtype[OUI_SUB_SIZE] = LLDP_OUI_SUBTYPE; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb_data(port->ifname, agent->type); if (!ed) return SUBTYPE_INVALID; if (tlv->type == TYPE_127) { /* check for length */ if (tlv->length < OUI_SUB_SIZE) return TLV_ERR; /* check for oui */ if (memcmp(tlv->info, &oui_subtype, OUI_SUB_SIZE)) return SUBTYPE_INVALID; /* disable rx if tx has been disabled by administrator */ if (!ed->txmit) { LLDPAD_WARN("%s:%s agent %d EVB Config disabled\n", __func__, ed->ifname, agent->type); return TLV_OK; } LLDPAD_DBG("%s:%s agent %d received tlv:\n", __func__, port->ifname, agent->type); evb_dump_tlv(ed->ifname, tlv); memcpy(&ed->last, tlv->info, tlv->length); evb_print_tlvinfo(ed->ifname, &ed->last); evb_update_tlv(ed); somethingChangedLocal(ed->ifname, agent->type); LLDPAD_DBG("%s:%s agent %d new tlv:\n", __func__, port->ifname, agent->type); evb_print_tlvinfo(ed->ifname, &ed->tie); vdp_update(port->ifname, ed->tie.ccap); } return TLV_OK; } /* * Stop all modules which depend on EVB capabilities. */ static void evb_stop_modules(char *ifname, struct lldp_agent *agent) { LLDPAD_DBG("%s:%s agent %d STOP\n", __func__, ifname, agent->type); vdp_ifdown(ifname, agent); } static void evb_ifdown(char *ifname, struct lldp_agent *agent) { struct evb_data *ed; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return; LLDPAD_DBG("%s:%s agent %d called\n", __func__, ifname, agent->type); ed = evb_data(ifname, agent->type); if (!ed) { LLDPAD_DBG("%s:%s agent %d does not exist.\n", __func__, ifname, agent->type); return; } if (ed->vdp_start) evb_stop_modules(ifname, agent); LIST_REMOVE(ed, entry); free(ed); LLDPAD_INFO("%s:%s agent %d removed\n", __func__, ifname, agent->type); } /* * Fill up evb structure with reasonable info from the configuration file. */ static void evb_init_tlv(struct evb_data *ed, struct lldp_agent *agent) { memset(&ed->last, 0, sizeof ed->last); memset(&ed->tie, 0, sizeof ed->tie); memset(&ed->policy, 0, sizeof ed->policy); ed->txmit = evb_conf_enabletx(ed->ifname, agent->type); if (!ed->txmit) LLDPAD_DBG("%s:%s agent %d EVB tx is currently disabled\n", __func__, ed->ifname, agent->type); hton24(ed->policy.oui, LLDP_MOD_EVB); /* Get fmode/capabilities/rte/vsi from configuration file into policy */ ed->policy.smode = evb_conf_fmode(ed->ifname, agent->type); ed->policy.scap = evb_conf_capa(ed->ifname, agent->type); ed->policy.rte = evb_conf_rte(ed->ifname, agent->type); ed->policy.svsi = htons(evb_conf_vsis(ed->ifname, agent->type)); ed->policy.cmode = 0; ed->policy.ccap = 0; hton24(ed->tie.oui, LLDP_MOD_EVB); ed->tie.smode = ed->policy.smode; ed->tie.cmode = 0; ed->tie.scap = ed->policy.scap; ed->tie.ccap = 0; ed->tie.svsi = ed->policy.svsi; ed->tie.cvsi = 0; ed->tie.rte = ed->policy.rte; ed->last.smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD | LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; evb_update_tlv(ed); } static void evb_ifup(char *ifname, struct lldp_agent *agent) { struct evb_data *ed; struct evb_user_data *ud; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return; LLDPAD_DBG("%s:%s agent %d called\n", __func__, ifname, agent->type); ed = evb_data(ifname, agent->type); if (ed) { LLDPAD_DBG("%s:%s agent %d already exists\n", __func__, ifname, agent->type); return; } /* not found, alloc/init per-port tlv data */ ed = (struct evb_data *) calloc(1, sizeof(struct evb_data)); if (!ed) { LLDPAD_ERR("%s:%s agent %d malloc %zu failed\n", __func__, ifname, agent->type, sizeof(*ed)); return; } strncpy(ed->ifname, ifname, IFNAMSIZ); ed->agenttype = agent->type; evb_init_tlv(ed, agent); ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_EVB); LIST_INSERT_HEAD(&ud->head, ed, entry); LLDPAD_DBG("%s:%s agent %d added\n", __func__, ifname, agent->type); } static u8 evb_mibdelete(struct port *port, struct lldp_agent *agent) { struct evb_data *ed; ed = evb_data(port->ifname, agent->type); if (ed && (agent->type == ed->agenttype)) { memset(&ed->last, 0, sizeof ed->last); vdp_update(port->ifname, 0); } return 0; } /* * Start all modules which depend on EVB capabilities: ECP, VDP, CDCP. */ static void evb_start_modules(char *ifname, struct lldp_agent *agent) { LLDPAD_DBG("%s:%s agent %d START\n", __func__, ifname, agent->type); vdp_ifup(ifname, agent); } /* * Check for stable interfaces. When an interface goes up the carrier might * come and go during a start up time. Define a window during which the port * is considered unstable for EVB/VDP protocols. * * Use the dormantDelay counter of the port to determine a stable interface. */ int evb_timer(struct port *port, struct lldp_agent *agent) { struct evb_data *ed; int bits = LLDP_EVB_CAPABILITY_PROTOCOL_ECP | LLDP_EVB_CAPABILITY_PROTOCOL_VDP; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb_data(port->ifname, agent->type); if (!ed) return 0; if (!ed->vdp_start && (port->dormantDelay == 1 || (bits & ed->tie.ccap) == bits)) { ed->vdp_start = true; evb_start_modules(port->ifname, agent); vdp_update(port->ifname, ed->tie.ccap); } return 0; } /* * Remove all interface/agent specific evb data. */ static void evb_free_data(struct evb_user_data *ud) { struct evb_data *ed; if (ud) { while (!LIST_EMPTY(&ud->head)) { ed = LIST_FIRST(&ud->head); LIST_REMOVE(ed, entry); free(ed); } } } void evb_unregister(struct lldp_module *mod) { if (mod->data) { evb_free_data((struct evb_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s:done\n", __func__); } static const struct lldp_mod_ops evb_ops = { .lldp_mod_register = evb_register, .lldp_mod_unregister = evb_unregister, .lldp_mod_gettlv = evb_gettlv, .lldp_mod_rchange = evb_rchange, .lldp_mod_ifup = evb_ifup, .lldp_mod_ifdown = evb_ifdown, .lldp_mod_mibdelete = evb_mibdelete, .timer = evb_timer, .get_arg_handler = evb_get_arg_handlers }; struct lldp_module *evb_register(void) { struct lldp_module *mod; struct evb_user_data *ud; mod = calloc(1, sizeof *mod); if (!mod) { LLDPAD_ERR("%s: failed to malloc module data\n", __func__); return NULL; } ud = calloc(1, sizeof(struct evb_user_data)); if (!ud) { free(mod); LLDPAD_ERR("%s failed to malloc module user data\n", __func__); return NULL; } LIST_INIT(&ud->head); mod->id = LLDP_MOD_EVB; mod->ops = &evb_ops; mod->data = ud; LLDPAD_DBG("%s:done\n", __func__); return mod; } lldpad-0.9.46/lldp_evb_clif.c000066400000000000000000000121331215142747300160100ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include "lldp_tlv.h" #include "clif_msgs.h" #include "lldp_mod.h" #include "lldptool.h" #include "lldp.h" #include "lldp_evb.h" #include "lldp_evb_clif.h" static void evb_print_cfg_tlv(u16, char *); static struct type_name_info evb_tlv_names[] = { { .type = TLVID_8021Qbg(LLDP_EVB_SUBTYPE), .name = "EVB draft 0.2 Configuration TLV", .key = "evbcfg", .print_info = evb_print_cfg_tlv }, { .type = INVALID_TLVID } }; static int evb_print_help() { struct type_name_info *tn = &evb_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } static void evb_cli_unregister(struct lldp_module *mod) { free(mod); } static void evb_print_cfg_tlv(u16 len, char *info) { u8 smode; u8 scap; u8 cmode; u8 ccap; u16 svsi; u16 cvsi; u8 rte; if (len != 9) { printf("Bad Cfg TLV: %s\n", info); return; } if (!hexstr2bin(info, &smode, sizeof(smode))) { printf("supported forwarding mode: (%#x)", smode); if (smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) printf(" reflective relay"); if (smode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) printf(" standard 802.1Q"); printf("\n"); } else printf("Unable to decode smode !\n"); if (!hexstr2bin(info+2, &scap, sizeof(scap))) { printf("\tsupported capabilities: (%#02hhx)", scap); if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) printf(" RTE"); if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) printf(" ECP"); if ( scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) printf(" VDP"); printf("\n"); } else printf("Unable to decode scap !\n"); if (!hexstr2bin(info+4, &cmode, sizeof(cmode))) { printf("\tconfigured forwarding mode: (%#02hhx)", cmode); if (cmode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) printf(" reflective relay"); if (cmode & LLDP_EVB_CAPABILITY_FORWARD_STANDARD) printf(" standard 802.1Q"); printf("\n"); } else printf("Unable to decode cmode !\n"); if (!hexstr2bin(info+6, &ccap, sizeof(ccap))) { printf("\tconfigured capabilities: (%#02hhx)", ccap); if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) printf(" RTE"); if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) printf(" ECP"); if ( ccap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) printf(" VDP"); printf("\n"); } else printf("Unable to decode ccap !\n"); if (!hexstr2bin(info+8, (u8 *)&svsi, sizeof(svsi))) printf("\tno. of supported VSIs: %04i\n", ntohs(svsi)); else printf("Unable to decode svsi !\n"); if (!hexstr2bin(info+12, (u8 *)&cvsi, sizeof(cvsi))) printf("\tno. of configured VSIs: %04i\n", ntohs(cvsi)); else printf("Unable to decode cvsi !\n"); if (!hexstr2bin(info+16, &rte, sizeof(rte))) printf("\tRTE: %i\n",rte); else printf("Unable to decode cvsi !\n"); printf("\n"); } /* return 1: if it printed the TLV * 0: if it did not */ static int evb_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &evb_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } static u32 evb_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &evb_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } static const struct lldp_mod_ops evb_ops_clif = { .lldp_mod_register = evb_cli_register, .lldp_mod_unregister = evb_cli_unregister, .print_tlv = evb_print_tlv, .lookup_tlv_name = evb_lookup_tlv_name, .print_help = evb_print_help, }; struct lldp_module *evb_cli_register(void) { struct lldp_module *mod; mod = calloc(1, sizeof(*mod)); if (!mod) { fprintf(stderr, "%s failed to malloc module data\n", __func__); return NULL; } mod->id = LLDP_MOD_EVB; mod->ops = &evb_ops_clif; return mod; } lldpad-0.9.46/lldp_evb_cmds.c000066400000000000000000000400611215142747300160220ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #define _GNU_SOURCE #include #include #include #include "lldp.h" #include "lldp_evb.h" #include "lldp_vdp.h" #include "lldp_tlv.h" #include "lldp_mand_clif.h" #include "config.h" #include "clif_msgs.h" #include "messages.h" #define EVB_BUF_SIZE 256 #define ARG_EVB_FORWARDING_MODE "fmode" #define VAL_EVB_FMODE_BRIDGE "bridge" #define VAL_EVB_FMODE_REFLECTIVE_RELAY "reflectiverelay" #define ARG_EVB_CAPABILITIES "capabilities" #define VAL_EVB_CAPA_RTE "rte" #define VAL_EVB_CAPA_ECP "ecp" #define VAL_EVB_CAPA_VDP "vdp" #define VAL_EVB_CAPA_NONE "none" #define ARG_EVB_VSIS "vsis" #define ARG_EVB_RTE "rte" /* * Read EVB specific data from the configuration file. */ static const char *evb_conf_string(char *ifname, enum agent_type type, char *ext, int def) { char arg_path[EVB_BUF_SIZE]; const char *param = NULL; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_8021Qbg(LLDP_EVB_SUBTYPE), ext); if (get_cfg(ifname, type, arg_path, ¶m, CONFIG_TYPE_STRING)) { LLDPAD_INFO("%s:%s agent %d loading EVB policy for %s" " failed, using default (%d)\n", __func__, ifname, type, ext, def); return 0; } return param; } /* * Read forwarding mode from configuration file. */ u8 evb_conf_fmode(char *ifname, enum agent_type type) { u8 smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; const char *value; value = evb_conf_string(ifname, type, ARG_EVB_FORWARDING_MODE, smode); if (value) { if (strcasestr(value, VAL_EVB_FMODE_BRIDGE)) smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; if (strcasestr(value, VAL_EVB_FMODE_REFLECTIVE_RELAY)) smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; LLDPAD_DBG("%s:%s agent %d policy %s %s(%#x)\n", __func__, ifname, type, ARG_EVB_FORWARDING_MODE, value, smode); } return smode; } /* * Read maximum number of VSIs from configuration file. */ u16 evb_conf_vsis(char *ifname, enum agent_type type) { u16 svsi = LLDP_EVB_DEFAULT_SVSI; const char *value; value = evb_conf_string(ifname, type, ARG_EVB_VSIS, svsi); if (value) { svsi = atoi(value); LLDPAD_DBG("%s:%s agent %d policy %s %s(%#x)\n", __func__, ifname, type, ARG_EVB_VSIS, value, svsi); } return svsi; } /* * Read capabilities from configuration file. */ u8 evb_conf_capa(char *ifname, enum agent_type type) { u8 scap = 0; const char *value; value = evb_conf_string(ifname, type, ARG_EVB_CAPABILITIES, scap); if (value) { if (strcasestr(value, VAL_EVB_CAPA_RTE)) scap = LLDP_EVB_CAPABILITY_PROTOCOL_RTE; if (strcasestr(value, VAL_EVB_CAPA_ECP)) scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; if (strcasestr(value, VAL_EVB_CAPA_VDP)) scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; if (strcasestr(value, VAL_EVB_CAPA_NONE)) scap = 0; LLDPAD_DBG("%s:%s agent %d policy %s %s(%#x)\n", __func__, ifname, type, ARG_EVB_CAPABILITIES, value, scap); } return scap; } /* * Read RTE value from configuration file. */ u8 evb_conf_rte(char *ifname, enum agent_type type) { u8 rte = LLDP_EVB_DEFAULT_RTE; const char *value; value = evb_conf_string(ifname, type, ARG_EVB_RTE, rte); if (value) { rte = atoi(value); LLDPAD_DBG("%s:%s agent %d policy %s %s(%#x)\n", __func__, ifname, type, ARG_EVB_RTE, value, rte); } return rte; } /* * Read transmit status from configuration file. */ int evb_conf_enabletx(char *ifname, enum agent_type type) { return is_tlv_txenabled(ifname, type, TLVID_8021Qbg(LLDP_EVB_SUBTYPE)); } static int evb_cmdok(struct cmd *cmd, cmd_status expected) { if (cmd->cmd != expected) return cmd_invalid; switch (cmd->tlvid) { case TLVID_8021Qbg(LLDP_EVB_SUBTYPE): return cmd_success; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } } static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { struct evb_data *ed; char *s; cmd_status good_cmd = evb_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (ed->txmit) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, bool test) { int value; char arg_path[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_bad_params; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (vdp_vsis(ed->ifname)) return cmd_failed; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)){ LLDPAD_ERR("%s: error saving EVB enabletx (%s)\n", ed->ifname, argvalue); return cmd_failed; } ed->txmit = value; LLDPAD_INFO("%s: changed EVB enabletx (%s)\n", ed->ifname, argvalue); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, true); } static int get_arg_fmode(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { char *s; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (ed->policy.smode & LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY) s = VAL_EVB_FMODE_REFLECTIVE_RELAY; else s = VAL_EVB_FMODE_BRIDGE; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_fmode(struct cmd *cmd, const char *argvalue, bool test) { u8 smode = 0; char arg_path[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; if (!strcasecmp(argvalue, VAL_EVB_FMODE_BRIDGE)) smode = LLDP_EVB_CAPABILITY_FORWARD_STANDARD; if (!strcasecmp(argvalue, VAL_EVB_FMODE_REFLECTIVE_RELAY)) smode = LLDP_EVB_CAPABILITY_FORWARD_REFLECTIVE_RELAY; if (smode == 0) return cmd_bad_params; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (vdp_vsis(ed->ifname)) return cmd_failed; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.fmode", TLVID_PREFIX, cmd->tlvid); if (set_cfg(ed->ifname, cmd->type, arg_path, &argvalue, CONFIG_TYPE_STRING)) { LLDPAD_ERR("%s: saving EVB forwarding mode failed\n", ed->ifname); return cmd_failed; } ed->policy.smode = smode; LLDPAD_INFO("%s: changed EVB forwarding mode (%s)\n", ed->ifname, argvalue); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_fmode(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_fmode(cmd, argvalue, false); } static int test_arg_fmode(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_fmode(cmd, argvalue, true); } static int get_arg_capabilities(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int comma = 0; char t[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; memset(t, 0, sizeof t); if (ed->policy.scap & LLDP_EVB_CAPABILITY_PROTOCOL_RTE) { strcat(t, VAL_EVB_CAPA_RTE); comma = 1; } if (ed->policy.scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP) { if (comma) strcat(t, " "); strcat(t, VAL_EVB_CAPA_ECP); comma = 1; } if (ed->policy.scap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP) { if (comma) strcat(t, " "); strcat(t, VAL_EVB_CAPA_VDP); } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, (unsigned int) strlen(t), t); return cmd_success; } /* * Check for valid parameters: rte,ecp,vpd,none and combinations thereof */ static int check_capabilities(const char *capabilities, u8 *scap) { char *cp, *old_string, *string; int retcode = 0; *scap = 0; old_string = string = strdup(capabilities); if (!string) return -1; while ((cp = strtok(string, ", "))) { if (!strcasecmp(cp, VAL_EVB_CAPA_RTE)) *scap |= LLDP_EVB_CAPABILITY_PROTOCOL_RTE; else if (!strcasecmp(cp, VAL_EVB_CAPA_ECP)) *scap |= LLDP_EVB_CAPABILITY_PROTOCOL_ECP; else if (!strcasecmp(cp, VAL_EVB_CAPA_VDP)) *scap |= LLDP_EVB_CAPABILITY_PROTOCOL_VDP; else if (!strcasecmp(cp, VAL_EVB_CAPA_NONE)) { if (*scap) /* Invalid combination */ retcode = -1; break; } else { retcode = -1; break; } string = 0; } free(old_string); return retcode; } static int _set_arg_capabilities(struct cmd *cmd, const char *argvalue, bool test) { u8 scap = 0; char arg_path[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; if (check_capabilities(argvalue, &scap) < 0) return cmd_bad_params; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (vdp_vsis(ed->ifname)) return cmd_failed; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.capabilities", TLVID_PREFIX, cmd->tlvid); if (set_cfg(ed->ifname, cmd->type, arg_path, &argvalue, CONFIG_TYPE_STRING)) { LLDPAD_ERR("%s: saving EVB capabilities (%#x) failed\n", ed->ifname, scap); return cmd_failed; } ed->policy.scap = scap; LLDPAD_INFO("%s: changed EVB capabilities (%#x)\n", ed->ifname, scap); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_capabilities(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_capabilities(cmd, argvalue, false); } static int test_arg_capabilities(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_capabilities(cmd, argvalue, true); } static int get_arg_rte(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { char s[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (sprintf(s, "%i", ed->policy.rte) <= 0) return cmd_invalid; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, (unsigned int) strlen(s), s); return cmd_success; } static int _set_arg_rte(struct cmd *cmd, const char *argvalue, bool test) { int value; char arg_path[EVB_BUF_SIZE]; struct evb_data *ed = NULL; cmd_status good_cmd = evb_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; value = atoi(argvalue); if ((value < 0) || value > LLDP_EVB_DEFAULT_MAX_RTE) return cmd_bad_params; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (vdp_vsis(ed->ifname)) return cmd_failed; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.rte", TLVID_PREFIX, cmd->tlvid); if (set_cfg(ed->ifname, cmd->type, arg_path, &argvalue, CONFIG_TYPE_STRING)){ LLDPAD_ERR("%s: error saving EVB rte (%d)\n", ed->ifname, value); return cmd_failed; } ed->policy.rte = value; LLDPAD_INFO("%s: changed EVB rte (%#x)\n", ed->ifname, value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_rte(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_rte(cmd, argvalue, false); } static int test_arg_rte(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_rte(cmd, argvalue, true); } static int get_arg_vsis(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { char s[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (sprintf(s, "%04i", ed->policy.svsi) <= 0) return cmd_invalid; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_vsis(struct cmd *cmd, const char *argvalue, bool test) { int value; char arg_path[EVB_BUF_SIZE]; struct evb_data *ed; cmd_status good_cmd = evb_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; value = atoi(argvalue); if ((value < 0) || (value > LLDP_EVB_DEFAULT_MAX_VSI)) return cmd_bad_params; ed = evb_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (vdp_vsis(ed->ifname)) return cmd_failed; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.vsis", TLVID_PREFIX, cmd->tlvid); if (set_cfg(ed->ifname, cmd->type, arg_path, &argvalue, CONFIG_TYPE_STRING)){ LLDPAD_ERR("%s: error saving EVB vsi (%d)\n", ed->ifname, value); return cmd_failed; } ed->policy.svsi = htons(value); LLDPAD_INFO("%s: changed EVB vsis (%#x)\n", ed->ifname, value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_vsis(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_vsis(cmd, argvalue, false); } static int test_arg_vsis(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_vsis(cmd, argvalue, true); } static struct arg_handlers arg_handlers[] = { { .arg = ARG_EVB_FORWARDING_MODE, .arg_class = TLV_ARG, .handle_get = get_arg_fmode, .handle_set = set_arg_fmode, .handle_test = test_arg_fmode }, { .arg = ARG_EVB_CAPABILITIES, .arg_class = TLV_ARG, .handle_get = get_arg_capabilities, .handle_set = set_arg_capabilities, .handle_test = test_arg_capabilities }, { .arg = ARG_EVB_VSIS, .arg_class = TLV_ARG, .handle_get = get_arg_vsis, .handle_set = set_arg_vsis, .handle_test = test_arg_vsis }, { .arg = ARG_EVB_RTE, .arg_class = TLV_ARG, .handle_get = get_arg_rte, .handle_set = set_arg_rte, .handle_test = test_arg_rte }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable }, { .arg = 0 } }; struct arg_handlers *evb_get_arg_handlers() { return &arg_handlers[0]; } lldpad-0.9.46/lldp_mand.c000066400000000000000000000415041215142747300151620ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include "config.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_mand.h" #include "lldp_mand_clif.h" #include "lldp_mand_cmds.h" #include "lldpad_shm.h" #include "messages.h" #include "lldp/l2_packet.h" #include "lldp_tlv.h" extern struct lldp_head lldp_head; static const struct lldp_mod_ops mand_ops = { .lldp_mod_register = mand_register, .lldp_mod_unregister = mand_unregister, .lldp_mod_gettlv = mand_gettlv, .lldp_mod_ifup = mand_ifup, .lldp_mod_ifdown = mand_ifdown, .client_cmd = mand_clif_cmd, .get_arg_handler = mand_get_arg_handlers, }; struct mand_data *mand_data(const char *ifname, enum agent_type type) { struct mand_user_data *mud; struct mand_data *md = NULL; mud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_MAND); if (mud) { LIST_FOREACH(md, &mud->head, entry) { if (!strncmp(ifname, md->ifname, IFNAMSIZ) && (type == md->agenttype)) return md; } } return NULL; } /* * mand_bld_end_tlv - build mandatory End TLV * @md: the mand data struct * * Returns 0 for success or error code for failure * */ static int mand_bld_end_tlv(struct mand_data *md) { int rc = EINVAL; struct unpacked_tlv *tlv; if (md->end) return 0; tlv = create_tlv(); if(tlv) { tlv->type = END_OF_LLDPDU_TLV; tlv->length = 0; tlv->info = NULL; md->end = tlv; rc = 0; } return rc; } static int mand_bld_mac_chassis(struct mand_data *md, struct tlv_info_chassis *chassis) { get_mac(md->ifname, chassis->id.mac); if (is_valid_mac(chassis->id.mac)) chassis->sub = CHASSIS_ID_MAC_ADDRESS; return sizeof(chassis->id.mac) + sizeof(chassis->sub); } static int mand_bld_ip_chassis(struct mand_data *md, struct tlv_info_chassis *chassis) { unsigned int len; if (!get_ipaddr(md->ifname, &chassis->id.na.ip.v4)) { chassis->sub = CHASSIS_ID_NETWORK_ADDRESS; chassis->id.na.type = MANADDR_IPV4; len = sizeof(chassis->id.na.ip.v4); } else if (!get_ipaddr6(md->ifname, &chassis->id.na.ip.v6)) { chassis->sub = CHASSIS_ID_NETWORK_ADDRESS; chassis->id.na.type = MANADDR_IPV6; len = sizeof(chassis->id.na.ip.v6); } else return -1; return sizeof(chassis->id.na.type) + len + sizeof(chassis->sub); } static int mand_bld_ifname_chassis(struct mand_data *md, struct tlv_info_chassis *chassis) { chassis->sub = CHASSIS_ID_INTERFACE_NAME; strncpy((char *)chassis->id.ifname, md->ifname, IFNAMSIZ); return strlen(md->ifname) + sizeof(chassis->sub); } /* * mand_bld_chassis_tlv - build mandatory End TLV * @md: the mand data struct * * Returns 0 for success or error code for failure * * Load from config if is configured, otherwise build from * scratc. Note that for LLDP-MED, * - Mandatory for LLDP-MED Network Connectivity w/ default to MAC * - Mandatory for LLDP-MED Endpoint w/ default to network addr * * In the case of MED being enabled w/ undefined or invalid devtype? * we will just use network addr, assuming Endpoint device. * * If MED is not enabled, the order, as spec says, is: * - CHASSIS_ID_CHASSIS_COMPONENT: only from config * - CHASSIS_ID_INTERFACE_ALIAS : only from config * - CHASSIS_ID_PORT_COMPONENT : only from config * - CHASSIS_ID_MAC_ADDRESS : first from config, then built from scratch * - CHASSIS_ID_NETWORK_ADDRESS : first from config, then built from scratch * - CHASSIS_ID_INTERFACE_NAME : first from config, then built from scratch * - CHASSIS_ID_LOCALLY_ASSIGNED: only load from config * * TODO: * - Specs says chassis should remain constant for all LLDPUs * while the connection remains operational, so this is built only * once. * - No validation on data loaded from config other than the subtype * */ static int mand_bld_chassis_tlv(struct mand_data *md, struct lldp_agent *agent) { int rc = EINVAL; int devtype, subtype; size_t length; char arg_path[512] = { 0 }; struct unpacked_tlv *tlv; struct tlv_info_chassis chassis; /* build only once */ if ((!md->rebuild_chassis) && (md->chassis)) { rc = 0; goto out_err; } /* free before building it */ md->rebuild_chassis = 1; FREE_UNPKD_TLV(md, chassis); memset(&chassis, 0, sizeof(chassis)); /* check for value in shared memory first */ if (!md->read_shm && agent->type == NEAREST_BRIDGE && lldpad_shm_get_msap(md->ifname, CHASSIS_ID_TLV, (char *)&chassis, &length)) goto bld_tlv; /* subtype may differ when LLDP-MED is enabled */ if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_RESERVED))) { int err; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_NOUI(CHASSIS_ID_TLV), ARG_MAND_SUBTYPE); err = get_config_setting(md->ifname, agent->type, arg_path, &subtype, CONFIG_TYPE_INT); if (err) subtype = 0; switch (subtype) { case CHASSIS_ID_INTERFACE_NAME: length = mand_bld_ifname_chassis(md, &chassis); break; case CHASSIS_ID_NETWORK_ADDRESS: length = mand_bld_ip_chassis(md, &chassis); if (length > 0) break; /* Fall through on IP error */ case CHASSIS_ID_MAC_ADDRESS: default: length = mand_bld_mac_chassis(md, &chassis); } } else { devtype = get_med_devtype(md->ifname, agent->type); LLDPAD_DBG("%s:%s:MED enabled w/ devtype=%d)\n", __func__, md->ifname, devtype); switch (devtype) { case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: length = mand_bld_ip_chassis(md, &chassis); if (length > 0) break; /* Fall through on IP error */ case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: default: length = mand_bld_ifname_chassis(md, &chassis); break; } } bld_tlv: tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = CHASSIS_ID_TLV; tlv->length = length; tlv->info = (u8 *)malloc(length); if(!tlv->info){ free(tlv); goto out_err; } memset(tlv->info, 0, tlv->length); memcpy(tlv->info, &chassis, tlv->length); md->chassis = tlv; md->rebuild_chassis = 0; /* write this back */ if (agent->type == NEAREST_BRIDGE) lldpad_shm_set_msap(md->ifname, CHASSIS_ID_TLV, (char *)tlv->info, tlv->length); set_config_tlvinfo_bin(md->ifname, agent->type, TLVID_NOUI(CHASSIS_ID_TLV), tlv->info, tlv->length); rc = 0; out_err: return rc; } /* * mand_bld_portid_tlv - build mandatory End TLV * @md: the mand data struct * * Returns 0 for success or error code for failure * * Load from config if is configured, otherwise build from * scratc. Note that for LLDP-MED, * - Mandatory and default to MAC for Network Connectivity, and * Endpoint Devices * * In the case of MED being enabled w/ undefined or invalid devtype? * we will just use mac * * If MED is not enabled, the order, as spec says, is: * - PORT_ID_INTERFACE_ALIAS : only from config * - PORT_ID_PORT_COMPONENT : only from config * - PORT_ID_MAC_ADDRESS : first from config, then built from scratch * - PORT_ID_NETWORK_ADDRESS : first from config, then built from scratch * - PORT_ID_INTERFACE_NAME : first from config, then built from scratch * - PORT_ID_AGENT_CIRCUIT_ID : only from config * - PORT_ID_LOCALLY_ASSIGNED : only load from config * * TODO: * - The port id should remain constant for all LLDPUs while the connection * remains operational, so this is built only once. * - No validation on data loaded from config other than the subtype * */ static int mand_bld_portid_tlv(struct mand_data *md, struct lldp_agent *agent) { int rc = EINVAL; int devtype, subtype; size_t length; char arg_path[512] = { 0 }; struct unpacked_tlv *tlv; struct tlv_info_portid portid; /* build only once */ if ((!md->rebuild_portid) && (md->portid)) { rc = 0; goto out_err; } /* free before building it */ md->rebuild_portid = 1; FREE_UNPKD_TLV(md, portid); memset(&portid, 0, sizeof(portid)); /* check for value in shared memory first */ if (!md->read_shm && agent->type == NEAREST_BRIDGE && lldpad_shm_get_msap(md->ifname, PORT_ID_TLV, (char *)&(portid), &length)) goto bld_tlv; /* subtype may differ when LLDP-MED is enabled */ devtype = get_med_devtype(md->ifname, agent->type); if (LLDP_MED_DEVTYPE_DEFINED(devtype) && is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_RESERVED))) { subtype = PORT_ID_MAC_ADDRESS; LLDPAD_INFO("%s:%s: MED set portID=PORT_ID_MAC_ADDRESS\n", __func__, md->ifname); } else { int err; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_NOUI(PORT_ID_TLV), ARG_MAND_SUBTYPE); err = get_config_setting(md->ifname, agent->type, arg_path, &subtype, CONFIG_TYPE_INT); if (err) subtype = 0; } /* If build from scratch, try mac, then ip, then ifname. * This switch statement falls through case stmts until * a good address is found. Notice if user specified * fail order may change I think this is OK. */ switch (subtype) { default: case PORT_ID_MAC_ADDRESS: get_mac(md->ifname, portid.id.mac); if (is_valid_mac(portid.id.mac)) { portid.sub = PORT_ID_MAC_ADDRESS; length = sizeof(portid.id.mac) + sizeof(portid.sub); break; } case PORT_ID_NETWORK_ADDRESS: /* uses ipv4 first */ if (!get_ipaddr(md->ifname, &portid.id.na.ip.v4)) { portid.sub = PORT_ID_NETWORK_ADDRESS; portid.id.na.type = MANADDR_IPV4; length = sizeof(portid.id.na.type) + sizeof(portid.id.na.ip.v4) + sizeof(portid.sub); break; } /* ipv4 fails, get ipv6 */ if (!get_ipaddr6(md->ifname, &portid.id.na.ip.v6)) { portid.sub = PORT_ID_NETWORK_ADDRESS; portid.id.na.type = MANADDR_IPV6; length = sizeof(portid.id.na.type) + sizeof(portid.id.na.ip.v6) + sizeof(portid.sub); break; } case PORT_ID_INTERFACE_NAME: portid.sub = PORT_ID_INTERFACE_NAME; strncpy((char *)portid.id.ifname, md->ifname, IFNAMSIZ); length = strlen(md->ifname) + sizeof(portid.sub); break; } bld_tlv: tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = PORT_ID_TLV; tlv->length = length; tlv->info = (u8 *)malloc(length); if(!tlv->info){ free(tlv); goto out_err; } memset(tlv->info, 0, tlv->length); memcpy(tlv->info, &portid, tlv->length); md->portid = tlv; md->rebuild_portid = 0; /* write this back */ if (agent->type == NEAREST_BRIDGE) lldpad_shm_set_msap(md->ifname, PORT_ID_TLV, (char *)tlv->info, tlv->length); set_config_tlvinfo_bin(md->ifname, agent->type, TLVID_NOUI(PORT_ID_TLV), tlv->info, tlv->length); rc = 0; out_err: return rc; } static int mand_bld_ttl_tlv(struct mand_data *md, struct lldp_agent *agent) { int rc = EINVAL; u16 ttl; struct unpacked_tlv *tlv; if (md->ttl) free_unpkd_tlv(md->ttl); tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = TIME_TO_LIVE_TLV; tlv->length = 2; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); goto out_err; } memset(tlv->info, 0, tlv->length); if (agent->tx.txTTL) ttl = htons(agent->tx.txTTL); else ttl = htons(DEFAULT_TX_HOLD * DEFAULT_TX_INTERVAL); memcpy(tlv->info, &ttl, tlv->length); LLDPAD_DBG("%s:%s:done:type=%d length=%d ttl=%d\n", __func__, md->ifname, tlv->type, tlv->length, ntohs(ttl)); md->ttl = tlv; rc = 0; out_err: return rc; } /* build unpacked tlvs */ static int mand_bld_tlv(struct mand_data *md, struct lldp_agent *agent) { int rc = EPERM; if (!port_find_by_name(md->ifname)) { rc = EEXIST; goto out_err; } if (mand_bld_chassis_tlv(md, agent)) { LLDPAD_DBG("%s:%s:mand_bld_chassis_tlv() failed\n", __func__, md->ifname); goto out_err; } if (mand_bld_portid_tlv(md, agent)) { LLDPAD_DBG("%s:%s:mand_bld_portid_tlv() failed\n", __func__, md->ifname); goto out_err; } if (mand_bld_ttl_tlv(md, agent)) { LLDPAD_DBG("%s:%s:mand_bld_ttl_tlv() failed\n", __func__, md->ifname); goto out_err; } if (mand_bld_end_tlv(md)) { LLDPAD_DBG("%s:%s:mand_bld_end_tlv() failed\n", __func__, md->ifname); goto out_err; } rc = 0; out_err: return rc; } struct packed_tlv *mand_gettlv(struct port *port, struct lldp_agent *agent) { struct mand_data *md; struct packed_tlv *ptlv = NULL; size_t size; int err; md = mand_data(port->ifname, agent->type); if (!md) { LLDPAD_DBG("%s:%s: not found port\n", __func__, port->ifname); goto out_err; } err = mand_bld_tlv(md, agent); if (err) LLDPAD_DBG("%s:%s: building mandotory TLV error.\n", __func__, port->ifname); size = TLVSIZE(md->chassis) + TLVSIZE(md->portid) + TLVSIZE(md->ttl); if (!size) goto out_err; ptlv = create_ptlv(); if (!ptlv) goto out_err; ptlv->tlv = malloc(size); if (!ptlv->tlv) goto out_free; ptlv->size = 0; PACK_TLV_AFTER(md->chassis, ptlv, size, out_free); PACK_TLV_AFTER(md->portid, ptlv, size, out_free); PACK_TLV_AFTER(md->ttl, ptlv, size, out_free); return ptlv; out_free: ptlv = free_pkd_tlv(ptlv); out_err: LLDPAD_DBG("%s:%s: failed\n", __func__, port->ifname); return NULL; } static void mand_free_tlv(struct mand_data *md) { if (md) { FREE_UNPKD_TLV(md, chassis); FREE_UNPKD_TLV(md, portid); FREE_UNPKD_TLV(md, ttl); FREE_UNPKD_TLV(md, end); } } static void mand_free_data(struct mand_user_data *mud) { struct mand_data *md; if (mud) { while (!LIST_EMPTY(&mud->head)) { md = LIST_FIRST(&mud->head); LIST_REMOVE(md, entry); mand_free_tlv(md); free(md); } } } void mand_ifdown(char *ifname, struct lldp_agent *agent) { struct mand_data *md; md = mand_data(ifname, agent->type); if (!md) goto out_err; md->rebuild_chassis = 1; md->rebuild_portid = 1; mand_free_tlv(md); LLDPAD_INFO("%s:port %s removed\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s:port %s adding failed\n", __func__, ifname); return; } void mand_ifup(char *ifname, struct lldp_agent *agent) { struct mand_data *md; struct mand_user_data *mud; md = mand_data(ifname, agent->type); if (!md) { /* not found, alloc/init per-port tlv data */ md = (struct mand_data *) malloc(sizeof(*md)); if (!md) { LLDPAD_WARN("%s:%s malloc %zu failed\n", __func__, ifname, sizeof(*md)); return; } memset(md, 0, sizeof(struct mand_data)); strncpy(md->ifname, ifname, IFNAMSIZ); md->agenttype = agent->type; mud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_MAND); LIST_INSERT_HEAD(&mud->head, md, entry); } if (mand_bld_tlv(md, agent)) { LLDPAD_INFO("%s:%s mand_bld_tlv failed\n", __func__, ifname); LIST_REMOVE(md, entry); mand_free_tlv(md); free(md); return; } /* Only read shared memory on first ifup */ md->read_shm = 1; LLDPAD_INFO("%s:port %s added\n", __func__, ifname); return; } struct lldp_module *mand_register(void) { struct lldp_module *mod; struct mand_user_data *mud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("failed to malloc LLDP Mandatory module data\n"); goto out_err; } mud = malloc(sizeof(struct mand_user_data)); if (!mud) { free(mod); LLDPAD_ERR("failed to malloc LLDP Mandatory module user data\n"); goto out_err; } LIST_INIT(&mud->head); mod->id = LLDP_MOD_MAND; mod->ops = &mand_ops; mod->data = mud; LLDPAD_INFO("%s:done\n", __func__); return mod; out_err: LLDPAD_INFO("%s:failed\n", __func__); return NULL; } void mand_unregister(struct lldp_module *mod) { struct if_nameindex *nameidx, *p; struct port *port; struct lldp_agent *agent; struct mand_data *md; nameidx = if_nameindex(); if (nameidx == NULL) { LLDPAD_DBG("error calling if_nameindex()\n"); return; } for (p = nameidx; p->if_index != 0; p++) { port = port_find_by_name(p->if_name); if (!port) continue; LIST_FOREACH(agent, &port->agent_head, entry) { md = mand_data(p->if_name, agent->type); if (!md) continue; LIST_REMOVE(md, entry); mand_free_tlv(md); free(md); } } if_freenameindex(nameidx); if (mod->data) { mand_free_data((struct mand_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_INFO("%s:done\n", __func__); } lldpad-0.9.46/lldp_mand_clif.c000066400000000000000000000172201215142747300161550ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include "lldp.h" #include "lldp_mod.h" #include "lldp_mand.h" #include "lldp_mand_clif.h" #include "clif_msgs.h" void print_port_id(u16, char *info); void print_chassis_id(u16, char *info); void print_ttl(u16, char *info); u32 mand_lookup_tlv_name(char *tlvid_str); int mand_print_help(); static const struct lldp_mod_ops mand_ops_clif = { .lldp_mod_register = mand_cli_register, .lldp_mod_unregister = mand_cli_unregister, .print_tlv = mand_print_tlv, .lookup_tlv_name = mand_lookup_tlv_name, .print_help = mand_print_help, }; struct type_name_info mand_tlv_names[] = { { .type = END_OF_LLDPDU_TLV, .name = "End of LLDPDU TLV", .key = "", }, { .type = CHASSIS_ID_TLV, .name = "Chassis ID TLV", .key = "chassisID", .print_info = print_chassis_id, }, { .type = PORT_ID_TLV, .name = "Port ID TLV", .key = "portID", .print_info = print_port_id, }, { .type = TIME_TO_LIVE_TLV, .name = "Time to Live TLV", .key = "TTL", .print_info = print_ttl, }, { .type = INVALID_TLVID, } }; int mand_print_help() { struct type_name_info *tn = &mand_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *mand_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); goto out_err; } mod->id = LLDP_MOD_MAND; mod->ops = &mand_ops_clif; return mod; out_err: return NULL; } void mand_cli_unregister(struct lldp_module *mod) { free(mod); } void print_chassis_id(u16 len, char *info) { u8 subtype; u8 addrnum; char buf[512]; int i; if (!len || len > 256) { printf("Invalid length = %d\n", len); return; } hexstr2bin(info, (u8 *)&subtype, sizeof(subtype)); memset(buf, 0, sizeof(buf)); switch (subtype) { case CHASSIS_ID_MAC_ADDRESS: if (len != 1 + 6) return; printf("MAC: "); for (i = 0; i < 12; i+=2) { printf("%2.2s", info + 2 + i); if (i < 10) printf(":"); else printf("\n"); } break; case CHASSIS_ID_NETWORK_ADDRESS: if (len <=2) { printf("Bad Network Address\n"); break; } hexstr2bin(info+2, (u8 *)&addrnum, sizeof(addrnum)); switch(addrnum) { case MANADDR_IPV4: if (len == 6) { struct in_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); inet_ntop(AF_INET, (void *)&addr, buf, sizeof(buf)); printf("IPv4: %s\n", buf); } else { printf("Bad IPv4: %*.*s\n", 2*(len-2), 2*(len-2), info+4); } break; case MANADDR_IPV6: if (len == 18) { struct in6_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); inet_ntop(AF_INET6, (void *)&addr, buf, sizeof(buf)); printf("IPv6: %s\n", buf); } else { printf("Bad IPv6: %*.*s\n", 2*(len-2), 2*(len-2), info+4); } break; default: printf("Network Address Type %d: %*.*s\n", addrnum, 2*(len-2), 2*(len-2), info+2); break; } break; case CHASSIS_ID_CHASSIS_COMPONENT: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Chassis Component: %s\n", buf); break; case CHASSIS_ID_INTERFACE_ALIAS: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("IfAlias: %s\n", buf); break; case CHASSIS_ID_PORT_COMPONENT: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Port Component: %s\n", buf); break; case CHASSIS_ID_INTERFACE_NAME: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Ifname: %s\n", buf); break; case CHASSIS_ID_LOCALLY_ASSIGNED: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Local: %s\n", buf); break; default: printf("Bad Chassis ID: %*.*s\n", 2*len, 2*len, info); break; } } void print_port_id(u16 len, char *info) { u8 subtype; u8 addrnum; char buf[512]; int i; if (!len || len > 256) { printf("Invalid length = %d\n", len); return; } hexstr2bin(info, (u8 *)&subtype, sizeof(subtype)); memset(buf, 0, sizeof(buf)); switch (subtype) { case PORT_ID_MAC_ADDRESS: if (len != 1 + 6) return; printf("MAC: "); for (i = 0; i < 12; i+=2) { printf("%2.2s", info + 2 + i); if (i < 10) printf(":"); else printf("\n"); } break; case PORT_ID_NETWORK_ADDRESS: if (len <=2) { printf("Bad Network Address\n"); break; } hexstr2bin(info+2, (u8 *)&addrnum, sizeof(addrnum)); switch(addrnum) { case MANADDR_IPV4: if (len == 6) { struct in_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); inet_ntop(AF_INET, (void *)&addr, buf, sizeof(buf)); printf("IPv4: %s\n", buf); } else { printf("Bad IPv4: %*.*s\n", 2*(len-2), 2*(len-2), info+4); } break; case MANADDR_IPV6: if (len == 18) { struct in6_addr addr; hexstr2bin(info+4, (u8 *)&addr, sizeof(addr)); inet_ntop(AF_INET6, (void *)&addr, buf, sizeof(buf)); printf("IPv6: %s\n", buf); } else { printf("Bad IPv6: %*.*s\n", 2*(len-2), 2*(len-2), info+4); } break; default: printf("Network Address Type %d: %*.*s\n", addrnum, 2*(len-2), 2*(len-2), info+2); break; } break; case PORT_ID_INTERFACE_ALIAS: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Interface Alias: %s\n", buf); break; case PORT_ID_PORT_COMPONENT: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Port Component: %s\n", buf); break; case PORT_ID_INTERFACE_NAME: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Ifname: %s\n", buf); break; case PORT_ID_LOCALLY_ASSIGNED: hexstr2bin(info+2, (u8 *)&buf[0], len-1); printf("Local: %s\n", buf); break; case PORT_ID_AGENT_CIRCUIT_ID: printf("Agent Circuit ID: %*.*s\n", 2*(len-1), 2*(len-1), info+2); break; default: printf("Bad Port ID: %*.*s\n", 2*len, 2*len, info); break; } } void print_ttl(UNUSED u16 len, char *info) { u16 ttl; hexstr2bin(info, (u8 *)&ttl, sizeof(ttl)); ttl = ntohs(ttl); printf("%d\n", ttl); } /* return 1: if it printed the TLV * 0: if it did not */ int mand_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &mand_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len, info); } return 1; } tn++; } return 0; } u32 mand_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &mand_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_mand_cmds.c000066400000000000000000000464271215142747300162010ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include "lldpad.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_tlv.h" #include "lldp_mand.h" #include "lldp_mand_clif.h" #include "lldp/ports.h" #include "libconfig.h" #include "config.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/states.h" #include "lldp_util.h" #include "messages.h" static int get_arg_adminstatus(struct cmd *, char *, char *, char *, int); static int set_arg_adminstatus(struct cmd *, char *, char *, char *, int); static int test_arg_adminstatus(struct cmd *, char *, char *, char *, int); static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int handle_get_arg(struct cmd *, char *, char *, char *, int); static int handle_set_arg(struct cmd *, char *, char *, char *, int); static int handle_test_arg(struct cmd *, char *, char *, char *, int); static int get_mand_subtype(struct cmd *, char *, char *, char *, int); static int set_mand_subtype(struct cmd *, char *, char *, char *, int); static int test_mand_subtype(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_ADMINSTATUS, .arg_class = LLDP_ARG, .handle_get = get_arg_adminstatus, .handle_set = set_arg_adminstatus, .handle_test = test_arg_adminstatus, }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = set_arg_tlvtxenable, }, { .arg = ARG_MAND_SUBTYPE, .arg_class = TLV_ARG, .handle_get = get_mand_subtype, .handle_set = set_mand_subtype, .handle_test = test_mand_subtype, }, { .arg = 0 } }; static int get_mand_subtype(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { struct mand_data *md; int subtype; char *string, arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; md = mand_data(cmd->ifname, cmd->type); if (!md) return cmd_device_not_found; switch (cmd->tlvid) { case CHASSIS_ID_TLV: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_NOUI(CHASSIS_ID_TLV), ARG_MAND_SUBTYPE); get_config_setting(cmd->ifname, cmd->type, arg_path, &subtype, CONFIG_TYPE_INT); switch (subtype) { case CHASSIS_ID_CHASSIS_COMPONENT: string = "CHASSIS_ID_CHASSIS_COMPONENT"; break; case CHASSIS_ID_INTERFACE_ALIAS: string = "CHASSIS_ID_INTERFACE_ALIAS"; break; case CHASSIS_ID_PORT_COMPONENT: string = "CHASSIS_ID_PORT_COMPONENT"; break; case CHASSIS_ID_MAC_ADDRESS: string = "CHASSIS_ID_MAC_ADDRESS"; break; case CHASSIS_ID_NETWORK_ADDRESS: string = "CHASSIS_ID_NETWORK_ADDRESS"; break; case CHASSIS_ID_INTERFACE_NAME: string = "CHASSIS_ID_INTERFACE_NAME"; break; case CHASSIS_ID_LOCALLY_ASSIGNED: string = "CHASSIS_ID_LOCALLY_ASSIGNED"; break; default: string = "DEFAULT"; break; } break; case PORT_ID_TLV: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID_NOUI(PORT_ID_TLV), ARG_MAND_SUBTYPE); get_config_setting(cmd->ifname, cmd->type, arg_path, &subtype, CONFIG_TYPE_INT); switch (subtype) { case PORT_ID_INTERFACE_ALIAS: string = "PORT_ID_INTERFACE_ALIAS"; break; case PORT_ID_PORT_COMPONENT: string = "PORT_ID_PORT_COMPONENT"; break; case PORT_ID_MAC_ADDRESS: string = "PORT_ID_MAC_ADDRESS"; break; case PORT_ID_NETWORK_ADDRESS: string = "PORT_ID_NETWORK_ADDRESS"; break; case PORT_ID_INTERFACE_NAME: string = "PORT_ID_INTERFACE_NAME"; break; case PORT_ID_AGENT_CIRCUIT_ID: string = "PORT_ID_AGENT_CIRCUIT_ID"; break; case PORT_ID_LOCALLY_ASSIGNED: string = "PORT_ID_LOCALLY_ASSIGNED"; break; default: string = "DEFAULT"; break; } break; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, (unsigned int)strlen(string), string); return 0; } static int _set_mand_subtype(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { struct mand_data *md; int subtype; char *end; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; md = mand_data(cmd->ifname, cmd->type); if (!md) return cmd_device_not_found; switch (cmd->tlvid) { case CHASSIS_ID_TLV: break; case PORT_ID_TLV: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } errno = 0; subtype = strtoul(argvalue, &end, 0); if (cmd->tlvid == CHASSIS_ID_TLV) { if (errno || *end != '\0') { if (strcasecmp(argvalue, "CHASSIS_ID_MAC_ADDRESS") == 0) subtype = CHASSIS_ID_MAC_ADDRESS; else if (strcasecmp(argvalue, "CHASSIS_ID_NETWORK_ADDRESS") == 0) subtype = CHASSIS_ID_NETWORK_ADDRESS; else if (strcasecmp(argvalue, "CHASSIS_ID_INTERFACE_NAME") == 0) subtype = CHASSIS_ID_INTERFACE_NAME; else { snprintf(obuf, obuf_len, "subtype=[Unsupported subtype]"); return cmd_invalid; } } else { switch (subtype) { case CHASSIS_ID_MAC_ADDRESS: case CHASSIS_ID_NETWORK_ADDRESS: case CHASSIS_ID_INTERFACE_NAME: break; default: snprintf(obuf, obuf_len, "subtype=[Unsupported subtype]"); return cmd_invalid; } } } else { if (errno || *end != '\0') { if (strcasecmp(argvalue, "PORT_ID_MAC_ADDRESS") == 0) subtype = PORT_ID_MAC_ADDRESS; else if (strcasecmp(argvalue, "PORT_ID_NETWORK_ADDRESS") == 0) subtype = PORT_ID_NETWORK_ADDRESS; else if (strcasecmp(argvalue, "PORT_ID_INTERFACE_NAME") == 0) subtype = PORT_ID_INTERFACE_NAME; else { snprintf(obuf, obuf_len, "subtype=[Unsupported subtype]"); return cmd_invalid; } } else { switch (subtype) { case PORT_ID_MAC_ADDRESS: case PORT_ID_NETWORK_ADDRESS: case PORT_ID_INTERFACE_NAME: break; default: snprintf(obuf, obuf_len, "subtype=[Unsupported subtype]"); return cmd_invalid; } } } if (test) return cmd_success; md->read_shm = 1; md->rebuild_chassis = 1; md->rebuild_portid = 1; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); snprintf(obuf, obuf_len, "%s=%s\n", arg, argvalue); set_config_setting(cmd->ifname, cmd->type, arg_path, &subtype, CONFIG_TYPE_INT); somethingChangedLocal(cmd->ifname, cmd->type); return 0; } static int set_mand_subtype(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_mand_subtype(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_mand_subtype(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_mand_subtype(cmd, arg, argvalue, obuf, obuf_len, true); } struct arg_handlers *mand_get_arg_handlers() { return &arg_handlers[0]; } int get_arg_adminstatus(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; if (cmd->cmd != cmd_get_lldp) return cmd_bad_params; if (cmd->tlvid != INVALID_TLVID) return cmd_bad_params; if (get_config_setting(cmd->ifname, cmd->type, arg, &value, CONFIG_TYPE_INT)) value = disabled; switch (value) { case disabled: s = VAL_DISABLED; break; case enabledTxOnly: s = VAL_TX; break; case enabledRxOnly: s = VAL_RX; break; case enabledRxTx: s = VAL_RXTX; break; default: s = VAL_INVALID; return cmd_invalid; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; if (cmd->cmd != cmd_gettlv) return cmd_bad_params; switch (cmd->tlvid) { case CHASSIS_ID_TLV: case PORT_ID_TLV: case TIME_TO_LIVE_TLV: case END_OF_LLDPDU_TLV: value = true; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } int handle_get_args(struct cmd *cmd, UNUSED char *arg, char *argvalue, char *obuf, int obuf_len) { struct lldp_module *np; struct arg_handlers *ah; int rval; char *nbuf; int nbuf_len; nbuf = obuf; nbuf_len = obuf_len; LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops->get_arg_handler) continue; if (!(ah = np->ops->get_arg_handler())) continue; while (ah->arg) { if (ah->handle_get && (ah->arg_class == TLV_ARG)) { rval = ah->handle_get(cmd, ah->arg, argvalue, nbuf, nbuf_len); if (rval != cmd_success && rval != cmd_not_applicable) return rval; nbuf_len -= strlen(nbuf); nbuf = nbuf + strlen(nbuf); } ah++; } } return cmd_success; } int handle_get_arg(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { struct lldp_module *np; struct arg_handlers *ah; int rval, status = cmd_not_applicable; LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops->get_arg_handler) continue; if (!(ah = np->ops->get_arg_handler())) continue; while (ah->arg) { if (!strcasecmp(ah->arg, arg) && ah->handle_get) { rval = ah->handle_get(cmd, ah->arg, argvalue, obuf, obuf_len); if (rval != cmd_success && rval != cmd_not_applicable) return rval; else if (rval == cmd_success) status = rval; break; } ah++; } } return status; } int _set_arg_adminstatus(struct cmd *cmd, char *arg, char *argvalue, char *obuf, UNUSED int obuf_len, bool test) { int value; if (cmd->cmd != cmd_set_lldp || cmd->tlvid != INVALID_TLVID) return cmd_bad_params; if (!strcasecmp(argvalue, VAL_RXTX)) value = enabledRxTx; else if (!strcasecmp(argvalue, VAL_RX)) value = enabledRxOnly; else if (!strcasecmp(argvalue, VAL_TX)) value = enabledTxOnly; else if (!strcasecmp(argvalue, VAL_DISABLED)) value = disabled; else return cmd_invalid; /* ignore invalid value */ if (test) return cmd_success; if (set_config_setting(cmd->ifname, cmd->type, arg, &value, CONFIG_TYPE_INT)) return cmd_failed; set_lldp_agent_admin(cmd->ifname, cmd->type, value); snprintf(obuf, obuf_len, "adminStatus = %s\n", argvalue); return cmd_success; } int test_arg_adminstatus(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_adminstatus(cmd, arg, argvalue, obuf, obuf_len, true); } int set_arg_adminstatus(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_adminstatus(cmd, arg, argvalue, obuf, obuf_len, false); } int set_arg_tlvtxenable(struct cmd *cmd, UNUSED char *arg, UNUSED char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { if (cmd->cmd != cmd_settlv) return cmd_bad_params; switch (cmd->tlvid) { case CHASSIS_ID_TLV: case PORT_ID_TLV: case TIME_TO_LIVE_TLV: case END_OF_LLDPDU_TLV: /* Cannot modify for Mandatory TLVs */ return cmd_invalid; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } } int handle_test_arg(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { struct lldp_module *np; struct arg_handlers *ah; int rval, status = cmd_not_applicable; LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops->get_arg_handler) continue; if (!(ah = np->ops->get_arg_handler())) continue; while (ah->arg) { if (!strcasecmp(ah->arg, arg) && ah->handle_test) { rval = ah->handle_test(cmd, ah->arg, argvalue, obuf, obuf_len); if (rval != cmd_not_applicable && rval != cmd_success) return rval; else if (rval == cmd_success) status = rval; break; } ah++; } } return status; } int handle_set_arg(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { struct lldp_module *np; struct arg_handlers *ah; int rval, status = cmd_not_applicable; LIST_FOREACH(np, &lldp_head, lldp) { if (!np->ops->get_arg_handler) continue; if (!(ah = np->ops->get_arg_handler())) continue; while (ah->arg) { if (!strcasecmp(ah->arg, arg) && ah->handle_set) { rval = ah->handle_set(cmd, ah->arg, argvalue, obuf, obuf_len); if (rval != cmd_not_applicable && rval != cmd_success) return rval; else if (rval == cmd_success) status = rval; break; } ah++; } } return status; } int get_tlvs(struct cmd *cmd, char *rbuf, int rlen) { u8 tlvs[2048]; int size = 0; int i; u32 tlvid; int off = 0; int moff = 0; u16 type, len; int res; if (cmd->ops & op_local) { res = get_local_tlvs(cmd->ifname, cmd->type, &tlvs[0], &size); if (res) return res; } else if (cmd->ops & op_neighbor) { res = get_neighbor_tlvs(cmd->ifname, cmd->type, &tlvs[0], &size); if (res) return res; } else return cmd_failed; /* filter down response if a specific TLVID was requested */ if (cmd->tlvid != INVALID_TLVID) { /* step through PDU buffer and move matching TLVs to front */ while (off < size) { type = *((u16 *) (tlvs+off)); type = htons(type); len = type & 0x01ff; type >>= 9; if (type < INVALID_TLVID) { tlvid = type; } else { tlvid = *((u32 *)(tlvs+off+2)); tlvid = ntohl(tlvid); } if (tlvid == cmd->tlvid) { memcpy(tlvs+moff, tlvs+off, sizeof(u16)+len); moff += sizeof(u16)+len; } off += (sizeof(u16)+len); } size = moff; } for (i = 0; i < size; i++) { snprintf(rbuf + 2*i, rlen - strlen(rbuf), "%02x", tlvs[i]); } return cmd_success; } int get_agent_stats(struct cmd *cmd, char *rbuf, int rlen) { int offset=0; struct agentstats stats; if (get_lldp_agent_statistics(cmd->ifname, &stats, cmd->type)) return cmd_device_not_found; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsFramesOutTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsFramesDiscardedTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsFramesInErrorsTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsFramesInTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsTLVsDiscardedTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsTLVsUnrecognizedTotal); offset+=8; snprintf(rbuf+offset, rlen - strlen(rbuf), "%08x", stats.statsAgeoutsTotal); return cmd_success; } int mand_clif_cmd(UNUSED void *data, UNUSED struct sockaddr_un *from, UNUSED socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen) { struct cmd cmd; struct port *port; u8 len, version; int ioff, roff; int rstatus = cmd_invalid; char **args; char **argvals; bool test_failed = false; int numargs = 0; int i, offset; /* pull out the command elements of the command message */ hexstr2bin(ibuf+MSG_VER, (u8 *)&version, sizeof(u8)); version = version >> 4; hexstr2bin(ibuf+CMD_CODE, (u8 *)&cmd.cmd, sizeof(cmd.cmd)); hexstr2bin(ibuf+CMD_OPS, (u8 *)&cmd.ops, sizeof(cmd.ops)); cmd.ops = ntohl(cmd.ops); hexstr2bin(ibuf+CMD_IF_LEN, &len, sizeof(len)); ioff = CMD_IF; if (len < sizeof(cmd.ifname)) memcpy(cmd.ifname, ibuf+CMD_IF, len); else return 1; cmd.ifname[len] = '\0'; ioff += len; if (version == CLIF_MSG_VERSION) { hexstr2bin(ibuf+ioff, &cmd.type, sizeof(cmd.type)); ioff += 2*sizeof(cmd.type); } else { cmd.type = NEAREST_BRIDGE; LLDPAD_WARN("deprecated client interface message version %x\n", version); } if (cmd.cmd == cmd_gettlv || cmd.cmd == cmd_settlv) { hexstr2bin(ibuf+ioff, (u8 *)&cmd.tlvid, sizeof(cmd.tlvid)); cmd.tlvid = ntohl(cmd.tlvid); ioff += 2*sizeof(cmd.tlvid); } else { cmd.tlvid = INVALID_TLVID; } /* count args and argvalus */ offset = ioff; for (numargs = 0; (ilen - offset) > 2; numargs++) { offset += 2; if (ilen - offset > 0) { offset++; if (ilen - offset > 4) offset += 4; } } args = calloc(numargs, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(numargs, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } if ((cmd.ops & op_arg) && (cmd.ops & op_argval)) numargs = get_arg_val_list(ibuf, ilen, &ioff, args, argvals); else if (cmd.ops & op_arg) numargs = get_arg_list(ibuf, ilen, &ioff, args); snprintf(rbuf, rlen, "%c%1x%02x%08x%02x%s", CMD_REQUEST, CLIF_MSG_VERSION, cmd.cmd, cmd.ops, (unsigned int)strlen(cmd.ifname), cmd.ifname); roff = strlen(rbuf); /* Confirm port is a lldpad managed port */ port = port_find_by_name(cmd.ifname); if (!port) { free(argvals); free(args); return cmd_device_not_found; } switch (cmd.cmd) { case cmd_getstats: if (numargs) break; rstatus = get_agent_stats(&cmd, rbuf + roff, rlen - roff); break; case cmd_gettlv: snprintf(rbuf + roff, rlen - roff, "%08x", cmd.tlvid); roff+=8; if (!numargs) { if (cmd.ops & op_config) { if (cmd.ops & op_neighbor) break; rstatus = handle_get_args(&cmd, NULL, NULL, rbuf + strlen(rbuf), rlen - strlen(rbuf)); } else { rstatus = get_tlvs(&cmd, rbuf+roff, rlen-roff); } } else if ((cmd.ops & op_config) && !(cmd.ops & op_neighbor)) { for (i = 0; i < numargs; i++) rstatus = handle_get_arg(&cmd, args[i], NULL, rbuf + strlen(rbuf), rlen - strlen(rbuf)); } break; case cmd_settlv: snprintf(rbuf + roff, rlen - roff, "%08x", cmd.tlvid); roff+=8; for (i = 0; i < numargs; i++) { rstatus = handle_test_arg(&cmd, args[i], argvals[i], rbuf+strlen(rbuf), rlen - strlen(rbuf)); if (rstatus != cmd_not_applicable && rstatus != cmd_success) { test_failed = true; break; } } if (test_failed) break; for (i = 0; i < numargs; i++) rstatus = handle_set_arg(&cmd, args[i], argvals[i], rbuf + strlen(rbuf), rlen - strlen(rbuf)); break; case cmd_get_lldp: for (i = 0; i < numargs; i++) rstatus = handle_get_arg(&cmd, args[i], NULL, rbuf + strlen(rbuf), rlen - strlen(rbuf)); break; case cmd_set_lldp: for (i = 0; i < numargs; i++) rstatus = handle_set_arg(&cmd, args[i], argvals[i], rbuf + strlen(rbuf), rlen - strlen(rbuf)); break; default: rstatus = cmd_invalid; break; } free(argvals); free(args); return rstatus; } lldpad-0.9.46/lldp_med.c000066400000000000000000000602041215142747300150060ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include "lldp.h" #include "lldp_med.h" #include "messages.h" #include "config.h" #include "libconfig.h" #include "lldp_mand_clif.h" #include "lldp_med_cmds.h" extern struct lldp_head lldp_head; struct tlv_info_medcaps { u8 oui[OUI_SIZE]; u8 subtype; u16 medcaps; u8 devtype; } __attribute__ ((__packed__)); struct tlv_info_netpoli { u8 oui[OUI_SIZE]; u8 subtype; u8 apptype; u16 unknown:1; u16 tagged:1; u16 reserved:1; u16 vid:12; u16 priority:3; u16 dscp:6; } __attribute__ ((__packed__)); struct tlv_info_extpvm { u8 oui[OUI_SIZE]; u8 subtype; u8 powtype:2; u8 powsrc:2; u8 powprio:4; u16 powval; } __attribute__ ((__packed__)); struct tlv_info_locid { u8 oui[OUI_SIZE]; u8 subtype; u8 format; union { u8 coord[16]; u8 civic[256]; u8 ecselin[25]; } lci; } __attribute__ ((__packed__)); static const struct lldp_mod_ops med_ops = { .lldp_mod_register = med_register, .lldp_mod_unregister = med_unregister, .lldp_mod_gettlv = med_gettlv, .lldp_mod_ifup = med_ifup, .lldp_mod_ifdown = med_ifdown, .get_arg_handler = med_get_arg_handlers, }; static struct med_data *med_data(const char *ifname, enum agent_type type) { struct med_user_data *mud; struct med_data *md = NULL; mud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_MED); if (mud) { LIST_FOREACH(md, &mud->head, entry) { if (!strncmp(ifname, md->ifname, IFNAMSIZ) && (type == md->agenttype)) return md; } } return NULL; } /* TODO : check config for optional caps */ static u16 med_get_caps(u8 devtype) { u16 medcaps = 0; switch(devtype) { case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: medcaps = (LLDP_MED_CAPABILITY_CAPAPILITIES | LLDP_MED_CAPABILITY_INVENTORY); break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: medcaps = (LLDP_MED_CAPABILITY_CAPAPILITIES | LLDP_MED_CAPABILITY_INVENTORY); break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: medcaps = (LLDP_MED_CAPABILITY_CAPAPILITIES | LLDP_MED_CAPABILITY_INVENTORY | LLDP_MED_CAPABILITY_LOCATION_ID); break; case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: LLDPAD_DBG("%s:WARNING:NETWORK_CONNECTIVITY is" "supported for bridge only\n", __func__); medcaps = (LLDP_MED_CAPABILITY_CAPAPILITIES | LLDP_MED_CAPABILITY_NETWORK_POLICY | LLDP_MED_CAPABILITY_LOCATION_ID); break; case LLDP_MED_DEVTYPE_NOT_DEFINED: default: LLDPAD_DBG("%s:devtype %d not supported\n", __func__, devtype); break; } return medcaps; } /* * med_bld_medcaps_tlv - build the LLDP-MED Capabilities TLV * @md: the med data struct * * Try to load the med caps from the config if it exists, otherwise * build the med caps tlv from the scratch * * Returns 0 on success */ static int med_bld_medcaps_tlv(struct med_data *md, struct lldp_agent *agent) { int rc = EPERM; struct tlv_info_medcaps medcaps; struct unpacked_tlv *tlv = NULL; /* free old one if it exists */ FREE_UNPKD_TLV(md, medcaps); /* must be enabled */ if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_CAPABILITIES))) { LLDPAD_DBG("%s:%s:MED Caps is not enabled\n", __func__, md->ifname); rc = 0; goto out_err; } /* load cap tlv info from config */ memset(&medcaps, 0, sizeof(medcaps)); if (get_config_tlvinfo_bin(md->ifname, agent->type, TLVID_MED(LLDP_MED_CAPABILITIES), &medcaps, sizeof(medcaps))) { LLDPAD_DBG("%s:%s:Build MED Caps as Endpoint Class I\n", __func__, md->ifname); goto out_bld; } /* validate the data loaded */ if (LLDP_MED_DEVTYPE_DEFINED(medcaps.devtype) && (medcaps.devtype == get_med_devtype(md->ifname, agent->type))) { LLDPAD_DBG("%s:%s:MED Caps loaded from config as type %d\n", __func__, md->ifname, medcaps.devtype); goto out_create; } LLDPAD_DBG("%s:%s:Load MED Caps is invalid\n", __func__, md->ifname); out_bld: /* Not in config, build from scratch */ hton24(medcaps.oui, OUI_TIA_TR41); medcaps.subtype = LLDP_MED_CAPABILITIES; medcaps.devtype = get_med_devtype(md->ifname, agent->type); medcaps.medcaps = htons(med_get_caps(medcaps.devtype)); out_create: tlv = create_tlv(); if (!tlv) { rc = ENOMEM; goto out_err; } tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(medcaps); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; rc = ENOMEM; goto out_err; } memcpy(tlv->info, &medcaps, tlv->length); md->medcaps = tlv; set_med_devtype(md->ifname, agent->type, medcaps.devtype); set_config_tlvinfo_bin(md->ifname, agent->type, TLVID_MED(LLDP_MED_CAPABILITIES), &medcaps, sizeof(medcaps)); rc = 0; out_err: return rc; } #define SYSFS_INV_PATH "/sys/class/dmi/id" #define PROC_INV_PATH "/proc/sys/kernel" #define PATH_INV_HWREV SYSFS_INV_PATH "/board_version" #define PATH_INV_FWREV SYSFS_INV_PATH "/bios_version" #define PATH_INV_SWREV PROC_INV_PATH "/osrelease" #define PATH_INV_MANUFACTURER SYSFS_INV_PATH "/sys_vendor" #define PATH_INV_SERIAL SYSFS_INV_PATH "/product_uuid" #define PATH_INV_MODELNAME SYSFS_INV_PATH "/product_name" #define PATH_INV_ASSETID SYSFS_INV_PATH "/chassis_serial" /* * TODO: If supports IETF RFC 2737 */ int med_read_inventory(u8 subtype, char *buf, size_t size) { char path[256]; struct utsname uts; FILE *f = NULL; memset(buf, 0, size); memset(path, 0, sizeof(path)); switch (subtype) { case LLDP_MED_INV_HWREV: strncpy(path, PATH_INV_HWREV, sizeof(path) - 1); break; case LLDP_MED_INV_FWREV: strncpy(path, PATH_INV_FWREV, sizeof(path) - 1); break; case LLDP_MED_INV_SWREV: if (!uname(&uts)) { strncpy(buf, uts.release, size - 1); goto out_err; } LLDPAD_DBG("%s: uname() failed for %d, try" " proc fs\n", __func__, subtype); strncpy(path, PATH_INV_SWREV, sizeof(path) - 1); break; case LLDP_MED_INV_SERIAL: strncpy(path, PATH_INV_SERIAL, sizeof(path) - 1); break; case LLDP_MED_INV_MANUFACTURER: strncpy(path, PATH_INV_MANUFACTURER, sizeof(path) - 1); break; case LLDP_MED_INV_MODELNAME: strncpy(path, PATH_INV_MODELNAME, sizeof(path) - 1); break; case LLDP_MED_INV_ASSETID: strncpy(path, PATH_INV_ASSETID, sizeof(path) - 1); break; default: LLDPAD_DBG("%s: unknown inventory subtype %d\n", __func__, subtype); goto out_err; } f = fopen(path, "r"); if (!f) { LLDPAD_DBG("%s: fopen(%s) failed for type %d\n", __func__, path, subtype); goto out_err; } if (!fgets(buf, size, f)) { LLDPAD_DBG("%s: fgets(%s) failed for type %d\n", __func__, path, subtype); memset(buf, 0, size); } fclose(f); out_err: return strlen(buf); } /* * med_bld_invtlv - builds inventory tlv by subtype * @md: the med data struct * @subtype: LLDP-MED inventory tlv subtype */ static struct unpacked_tlv *med_bld_invtlv(struct med_data *md, struct lldp_agent *agent, u8 subtype) { int length; u8 desc[33]; struct unpacked_tlv *tlv = NULL; if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(subtype))) { LLDPAD_DBG("%s:%s:subtype %d tx disabled\n", __func__, md->ifname, subtype); goto out_err; } length = med_read_inventory(subtype, (char *)desc, sizeof(desc)); if (!length) { LLDPAD_DBG("%s:%s:med_read_inventory(%d) failed\n", __func__, md->ifname, subtype); goto out_err; } if (desc[length - 1] == '\n') length--; tlv = create_tlv(); if (!tlv) { LLDPAD_DBG("%s:%s:creat_tlv(%d) failed\n", __func__, md->ifname, subtype); goto out_err; } memset(tlv, 0, sizeof(struct unpacked_tlv)); tlv->type = ORG_SPECIFIC_TLV; tlv->length = length + OUI_SUB_SIZE; tlv->info = (u8 *) malloc(tlv->length); if (!tlv->info) { free(tlv); tlv = NULL; goto out_err; } hton24(tlv->info, OUI_TIA_TR41); tlv->info[OUI_SIZE] = subtype; memcpy(&tlv->info[OUI_SUB_SIZE], desc, length); out_err: return tlv; } /* * med_bld_inventory_tlv - builds all inventory tlvs * @md: the med data struct * */ static int med_bld_inventory_tlv(struct med_data *md, struct lldp_agent *agent) { FREE_UNPKD_TLV(md, inv_hwrev); FREE_UNPKD_TLV(md, inv_fwrev); FREE_UNPKD_TLV(md, inv_swrev); FREE_UNPKD_TLV(md, inv_serial); FREE_UNPKD_TLV(md, inv_manufacturer); FREE_UNPKD_TLV(md, inv_modelname); FREE_UNPKD_TLV(md, inv_assetid); md->inv_hwrev = med_bld_invtlv(md, agent, LLDP_MED_INV_HWREV); md->inv_fwrev = med_bld_invtlv(md, agent, LLDP_MED_INV_FWREV); md->inv_swrev = med_bld_invtlv(md, agent, LLDP_MED_INV_SWREV); md->inv_serial = med_bld_invtlv(md, agent, LLDP_MED_INV_SERIAL); md->inv_manufacturer = med_bld_invtlv(md, agent, LLDP_MED_INV_MANUFACTURER); md->inv_modelname = med_bld_invtlv(md, agent, LLDP_MED_INV_MODELNAME); md->inv_assetid = med_bld_invtlv(md, agent, LLDP_MED_INV_ASSETID); return 0; } static int med_is_pd(const char *ifname) { LLDPAD_DBG("%s:%s: TODO\n", __func__, ifname); return 0; } static int med_is_pse(const char *ifname) { LLDPAD_DBG("%s:%s: TODO\n", __func__, ifname); return 0; } /* * med_bld_powvmdi_tlv - builds power via mdi tlv * @port: the port associated * * Returns 0 for success or error code for failure * * TODO: * When Extended Power via MDI TLV is enabled, it is recommended * by the spec ANSI-TIA-1057 Clause 9.2.4 to to disable 802.3AB * Optional Power via MDI TLV. * */ static int med_bld_powvmdi_tlv(struct med_data *md, struct lldp_agent *agent) { int rc = EINVAL; int devtype; int mandatory = 0; struct tlv_info_extpvm extpvm; struct unpacked_tlv *tlv = NULL; /* free old one if it exists */ FREE_UNPKD_TLV(md, extpvm); devtype = get_med_devtype(md->ifname, agent->type); switch (devtype) { case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: mandatory = med_is_pse(md->ifname); break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: mandatory = med_is_pd(md->ifname); break; default: LLDPAD_DBG("%s:%s: Unknown device type %d\n", __func__, md->ifname, devtype); rc = EINVAL; goto out_err; } if (!mandatory) { LLDPAD_DBG("%s:%s: No need to send Extended Power via MDI TLV\n", __func__, md->ifname); rc = 0; goto out_err; } /* Mandatory */ if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_EXTENDED_PVMDI))) { LLDPAD_DBG("%s:%s: Must enable Extended Power via MDI TLV as it" " mandatory for device type %d\n", __func__, md->ifname, devtype); rc = EPERM; goto out_err; } /* Load from config */ if (get_config_tlvinfo_bin(md->ifname, agent->type, TLVID_MED(LLDP_MED_NETWORK_POLICY), &extpvm, sizeof(extpvm))) { LLDPAD_DBG("%s:%s: Must configure Extended Power via MDI TLV as " " currently it has to be manually configured\n", __func__, md->ifname); rc = EINVAL; goto out_err; } /* disable Optional Power via MDI */ tlv_disabletx(md->ifname, agent->type, TLVID_8023(LLDP_8023_POWER_VIA_MDI)); /* We should have a valid tlv_info_extpvm here */ if (extpvm.subtype != LLDP_MED_EXTENDED_PVMDI) { LLDPAD_DBG("%s:%s: Wrong subtype %d: should be %d\n", __func__, md->ifname, extpvm.subtype, LLDP_MED_EXTENDED_PVMDI); rc = EINVAL; goto out_err; } tlv = create_tlv(); if (!tlv) { rc = ENOMEM; goto out_err; } tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(extpvm); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); rc = ENOMEM; goto out_err; } memcpy(tlv->info, &extpvm, tlv->length); md->extpvm = tlv; rc = 0; out_err: return rc; } /* * med_bld_locid_tlv - builds location id tlv * @md: the med data struct * * Returns 0 for success or error code for failure * * TODO: currently only supports load from config, will not transmit if * if it's not configured * */ static int med_bld_locid_tlv(struct med_data *md, struct lldp_agent *agent) { int rc = 0; size_t length; char *locstr = NULL; struct tlv_info_locid locid; struct unpacked_tlv *tlv = NULL; /* free old one if it exists */ FREE_UNPKD_TLV(md, locid); if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_LOCATION_ID))) { LLDPAD_DBG("%s:%s:Location Id TLV is not enabled\n", __func__, md->ifname); rc = 0; goto out_err; } /* location data size varies from type, query as string w/ max length */ length = sizeof(struct tlv_info_locid) * 2 + 1; locstr = malloc(length); if (!locstr) goto out_err; memset(locstr, 0, length); if (get_config_tlvinfo_str(md->ifname, agent->type, TLVID_MED(LLDP_MED_LOCATION_ID), locstr, length)) { LLDPAD_DBG("%s:%s:Location Id TLV must be" " administratively configured\n", __func__, md->ifname); goto out_err; } /* calculate the size in binary */ length = strlen(locstr) / 2; if (hexstr2bin(locstr, (u8 *)&locid, length)) { LLDPAD_DBG("%s:%s:Location Id TLV info corrupted: %s\n", __func__, md->ifname, locstr); goto out_err; } /* validate the loaded data */ if (LLDP_MED_LOCID_FORMAT_INVALID(locid.format)) { LLDPAD_DBG("%s:%s:Location Id TLV info invalid: %s\n", __func__, md->ifname, locstr); goto out_err; } tlv = create_tlv(); if (!tlv) goto out_err; tlv->type = ORG_SPECIFIC_TLV; tlv->length = length; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); goto out_err; } memcpy(tlv->info, &locid, tlv->length); md->locid = tlv; rc = 0; out_err: if (locstr) free(locstr); return rc; } /* * med_get_netpoli - builds tlv_info_netploi struct * @md: the med data struct * @n: the tlv_info_netpoli data struct * * Returns 0 for success or error code for failure * * Either gets the netpoli from the config or build from scratch. * * TODO: currently only supports load from config, will fail if it's * not configured */ static int med_get_netpoli(struct med_data *md, struct lldp_agent *agent, struct tlv_info_netpoli *n) { if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_NETWORK_POLICY))) return ENOENT; if (!get_config_tlvinfo_bin(md->ifname, agent->type, TLVID_MED(LLDP_MED_NETWORK_POLICY), n, sizeof(*n))) return 0; return EPERM; } /* * med_bld_netpoli_tlv - builds network policy tlv * @port: the port associated * * Returns 0 for success or error code for failure */ static int med_bld_netpoli_tlv(struct med_data *md, struct lldp_agent *agent) { int rc = EPERM; int devtype; struct tlv_info_netpoli netpoli; struct unpacked_tlv *tlv = NULL; /* free old one if it exists */ FREE_UNPKD_TLV(md, netpoli); devtype = get_med_devtype(md->ifname, agent->type); switch (devtype) { case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: /* Only transmit if it is administratively configured */ rc = med_get_netpoli(md, agent, &netpoli); if (rc == ENOENT) goto out_nobld; if (rc == EPERM) { LLDPAD_DBG("%s:%s: Must configure Network" " Policy TLV if it is enabled for Network" " Connectivity Devcie\n", __func__, md->ifname); goto out_err; } break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: /* Optional: skip if failed to get it */ if (med_get_netpoli(md, agent, &netpoli)) { LLDPAD_DBG("%s:%s: Skipping" " Network Policy TLV for Class I Device\n", __func__, md->ifname); goto out_nobld; } break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: /* Mandatory */ if (med_get_netpoli(md, agent, &netpoli)) { LLDPAD_DBG("%s:%s: Must enable and configure" " Network Policy TLV for Class II/III Device\n", __func__, md->ifname); goto out_err; } break; default: LLDPAD_DBG("%s:%s: unknown dev type %d:\n", __func__, md->ifname, devtype); goto out_err; } /* We should have a valid tlv_info_netpoli */ if (netpoli.subtype != LLDP_MED_NETWORK_POLICY) { LLDPAD_DBG("%s:%s: Wrong subtype %d: should be %d\n", __func__, md->ifname, netpoli.subtype, LLDP_MED_NETWORK_POLICY); rc = EINVAL; goto out_err; } tlv = create_tlv(); if (!tlv) { rc = ENOMEM; goto out_err; } tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(netpoli); tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); rc = ENOMEM; goto out_err; } memcpy(tlv->info, &netpoli, tlv->length); md->netpoli = tlv; out_nobld: rc = 0; out_err: return rc; } /* * med_bld_tlv - build all LLDP-MED TLVs * @md: the med data struct * * Returns 0 for success or error code for failure * */ static int med_bld_tlv(struct med_data *md, struct lldp_agent *agent) { int rc = EPERM; if (!port_find_by_name(md->ifname)) { rc = EEXIST; goto out_err; } /* no build if not enabled */ if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_MED(LLDP_MED_RESERVED))) { LLDPAD_DBG("%s:%s:LLDP-MED is not enabled\n", __func__, md->ifname); rc = 0; goto out_err; } /* no build if enabled no devtype is given */ if (!LLDP_MED_DEVTYPE_DEFINED(get_med_devtype(md->ifname, agent->type))) { LLDPAD_DBG("%s:%s:LLDP-MED devtype is not defined\n", __func__, md->ifname); goto out_err; } /* MED Cap is always mandatory for MED */ if (med_bld_medcaps_tlv(md, agent)) { LLDPAD_DBG("%s:%s:MED Capabilities TLV is mandatory!\n", __func__, md->ifname); goto out_err; } /* MAC PHY TLV is mandatory for MED */ if (!is_tlv_txenabled(md->ifname, agent->type, TLVID_8023(LLDP_8023_MACPHY_CONFIG_STATUS))) { LLDPAD_DBG("%s:%s MAC PHY Config is mandatory for MED\n", __func__, md->ifname); rc = ENOTTY; goto out_err; } /* Build optional and conditional ones based on device type: * * LLDP-MED Endpoint Class I TLV Set: ANSI/TIA-1057-2006, 10.2.1.2 * - Capabilities: mandatory * - Network Policy: optional * - Extended Power-via-MDI: mandatory only for 802.3af PD * - Inventory: optional * LLDP-MED Endpoint Class II TLV Set: ANSI/TIA-1057-2006, 10.2.1.3 * - Capabilities: mandatory * - Network Policy: mandatory * - Extended Power-via-MDI: mandatory only for 802.3.af PD * - Inventory: optional * LLDP-MED Endpoint Class III TLV Set: ANSI/TIA-1057-2006, 10.2.1.4 * - Capabilities: mandatory * - Network Policy: mandatory * - Location Identification: optional * - Extended Power-via-MDI: mandatory only for 802.3.af PD * - Inventory: optional * LLDP-MED Network Connectivity TLV Set: ANSI/TIA-1057-2006, 10.2.1.1 * - Capabilities: mandatory * - Network Policy: exists only if administratively * configured * - Location Identification: exists only if * administratively configured * - Extended Power-via-MDI: mandatory only for * 802.3.af PSE * - Inventory: optional * */ if (med_bld_netpoli_tlv(md, agent)) { LLDPAD_DBG("%s:%s:med_bld_netpoli_tlv() failed\n", __func__, md->ifname); goto out_err; } if (med_bld_locid_tlv(md, agent)) { LLDPAD_DBG("%s:%s:med_bld_locid_tlv() failed\n", __func__, md->ifname); goto out_err; } if (med_bld_powvmdi_tlv(md, agent)) { LLDPAD_DBG("%s:%s:med_bld_powvmdi_tlv() failed\n", __func__, md->ifname); goto out_err; } if (med_bld_inventory_tlv(md, agent)) { LLDPAD_DBG("%s:%s:med_bld_inventory_tlv() failed\n", __func__, md->ifname); goto out_err; } rc = 0; out_err: return rc; } /* * med_free_tlv - free allocated tlvs * @md: the private med data struct */ static void med_free_tlv(struct med_data *md) { if (md) { FREE_UNPKD_TLV(md, medcaps); FREE_UNPKD_TLV(md, netpoli); FREE_UNPKD_TLV(md, locid); FREE_UNPKD_TLV(md, extpvm); FREE_UNPKD_TLV(md, inv_hwrev); FREE_UNPKD_TLV(md, inv_fwrev); FREE_UNPKD_TLV(md, inv_swrev); FREE_UNPKD_TLV(md, inv_serial); FREE_UNPKD_TLV(md, inv_manufacturer); FREE_UNPKD_TLV(md, inv_modelname); FREE_UNPKD_TLV(md, inv_assetid); } } static void med_free_data(struct med_user_data *mud) { struct med_data *md; if (mud) { while (!LIST_EMPTY(&mud->head)) { md = LIST_FIRST(&mud->head); LIST_REMOVE(md, entry); med_free_tlv(md); free(md); } } } struct packed_tlv *med_gettlv(struct port *port, struct lldp_agent *agent) { size_t size; struct med_data *md; struct packed_tlv *ptlv = NULL; md = med_data(port->ifname, agent->type); if (!md) goto out_err; med_free_tlv(md); if (med_bld_tlv(md, agent)) { goto out_err; } size = TLVSIZE(md->medcaps) + TLVSIZE(md->netpoli) + TLVSIZE(md->locid) + TLVSIZE(md->extpvm) + TLVSIZE(md->inv_hwrev) + TLVSIZE(md->inv_fwrev) + TLVSIZE(md->inv_swrev) + TLVSIZE(md->inv_serial) + TLVSIZE(md->inv_manufacturer) + TLVSIZE(md->inv_modelname) + TLVSIZE(md->inv_assetid); if (!size) goto out_err; ptlv = create_ptlv(); if (!ptlv) goto out_err; ptlv->tlv = malloc(size); if (!ptlv->tlv) goto out_free; /* Pack all previously-built tlvs, any failure in packing fails all */ ptlv->size = 0; PACK_TLV_AFTER(md->medcaps, ptlv, size, out_free); PACK_TLV_AFTER(md->netpoli, ptlv, size, out_free); PACK_TLV_AFTER(md->locid, ptlv, size, out_free); PACK_TLV_AFTER(md->extpvm, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_hwrev, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_fwrev, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_swrev, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_serial, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_manufacturer, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_modelname, ptlv, size, out_free); PACK_TLV_AFTER(md->inv_assetid, ptlv, size, out_free); return ptlv; out_free: free_pkd_tlv(ptlv); out_err: LLDPAD_DBG("%s:%s: failed\n", __func__, port->ifname); return NULL; } void med_ifdown(char *ifname, struct lldp_agent *agent) { struct med_data *md; md = med_data(ifname, agent->type); if (!md) goto out_err; LIST_REMOVE(md, entry); med_free_tlv(md); free(md); LLDPAD_INFO("%s:port %s removed\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s:port %s adding failed\n", __func__, ifname); return; } void med_ifup(char *ifname, struct lldp_agent *agent) { struct med_data *md; struct med_user_data *mud; md = med_data(ifname, agent->type); if (md) { LLDPAD_DBG("%s:%s exists\n", __func__, ifname); goto out_err; } /* not found, alloc/init per-port tlv data */ md = (struct med_data *) malloc(sizeof(*md)); if (!md) { LLDPAD_DBG("%s:%s malloc %zu failed\n", __func__, ifname, sizeof(*md)); goto out_err; } memset(md, 0, sizeof(struct med_data)); strncpy(md->ifname, ifname, IFNAMSIZ); md->agenttype = agent->type; if (med_bld_tlv(md, agent)) { LLDPAD_DBG("%s:%s med_bld_tlv failed\n", __func__, ifname); free(md); goto out_err; } mud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_MED); LIST_INSERT_HEAD(&mud->head, md, entry); LLDPAD_INFO("%s:port %s added\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s:port %s adding failed\n", __func__, ifname); return; } struct lldp_module *med_register(void) { struct lldp_module *mod; struct med_user_data *mud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("failed to malloc LLDP-MED module data\n"); goto out_err; } mud = malloc(sizeof(struct med_user_data)); if (!mud) { free(mod); LLDPAD_ERR("failed to malloc LLDP-MED module user data\n"); goto out_err; } LIST_INIT(&mud->head); mod->id = LLDP_MOD_MED; mod->ops = &med_ops; mod->data = mud; LLDPAD_INFO("%s:done\n", __func__); return mod; out_err: LLDPAD_INFO("%s:failed\n", __func__); return NULL; } void med_unregister(struct lldp_module *mod) { if (mod->data) { med_free_data((struct med_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_INFO("%s:done\n", __func__); } lldpad-0.9.46/lldp_med_clif.c000066400000000000000000000147711215142747300160130ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldp.h" #include "lldp_mod.h" #include "lldptool.h" #include "clif_msgs.h" #include "lldp_med.h" #include "lldp_med_clif.h" void med_print_string_tlv(u16, char *info); void med_print_cap_tlv(u16, char *info); void med_print_hex_tlv(u16, char *info); u32 med_lookup_tlv_name(char *tlvid_str); int med_print_help(); static const struct lldp_mod_ops med_ops_clif = { .lldp_mod_register = med_cli_register, .lldp_mod_unregister = med_cli_unregister, .print_tlv = med_print_tlv, .lookup_tlv_name = med_lookup_tlv_name, .print_help = med_print_help, }; struct type_name_info med_tlv_names[] = { { .type = (OUI_TIA_TR41 << 8), .name = "LLDP-MED Settings", .key = "LLDP-MED", }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES, .name = "LLDP-MED Capabilities TLV", .key = "medCap", .print_info = med_print_cap_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY, .name = "LLDP-MED Network Policy TLV", .key = "medPolicy", .print_info = med_print_hex_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID, .name = "LLDP-MED Location TLV", .key = "medLoc", .print_info = med_print_hex_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI, .name = "LLDP-MED Extended Power-via-MDI TLV", .key = "medPower", .print_info = med_print_hex_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV, .name = "LLDP-MED Hardware Revision TLV", .key = "medHwRev", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV, .name = "LLDP-MED Firmware Revision TLV", .key = "medFwRev", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV, .name = "LLDP-MED Software Revision TLV", .key = "medSwRev", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL, .name = "LLDP-MED Serial Number TLV", .key = "medSerNum", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER, .name = "LLDP-MED Manufacturer Name TLV", .key = "medManuf", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME, .name = "LLDP-MED Model Name TLV", .key = "medModel", .print_info = med_print_string_tlv, }, { .type = (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID, .name = "LLDP-MED Asset ID TLV", .key = "medAssetID", .print_info = med_print_string_tlv, }, { .type = INVALID_TLVID, } }; int med_print_help() { struct type_name_info *tn = &med_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *med_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); goto out_err; } mod->id = LLDP_MOD_MED; mod->ops = &med_ops_clif; return mod; out_err: return NULL; } void med_cli_unregister(struct lldp_module *mod) { free(mod); } void med_print_cap_tlv(u16 len, char *info) { u16 caps; u8 devtype; char *s; int i; int print_comma; if (len != 3) { printf("Bad LLDP-MED Capabilities TLV: %*.*s\n", 2*len, 2*len, info); return; } hexstr2bin(info, (u8 *)&caps, sizeof(caps)); caps = ntohs(caps); hexstr2bin(info+4, (u8 *)&devtype, sizeof(devtype)); switch (devtype) { case LLDP_MED_DEVTYPE_NOT_DEFINED: s = VAL_MED_NOT; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: s = VAL_MED_CLASS_I; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: s = VAL_MED_CLASS_II; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: s = VAL_MED_CLASS_III; break; case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: s = VAL_MED_NETCON; break; default: s = VAL_MED_NOT; break; } printf("Device Type: %s\n\tCapabilities: ", s); print_comma = 0; for (i = 0; i < 16; i++) { switch ((1<type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n\t", tn->name); if (tn->print_info) tn->print_info(len-4, info); return 1; } tn++; } return 0; } u32 med_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &med_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_med_cmds.c000066400000000000000000000261061215142747300160170ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include "lldpad.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_med.h" #include "lldp_8023.h" #include "lldp_mand_clif.h" #include "lldp_med_clif.h" #include "lldp/ports.h" #include "libconfig.h" #include "config.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/states.h" static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int test_arg_tlvtxenable(struct cmd *, char *, char *, char *, int); static int get_arg_med_devtype(struct cmd *, char *, char *, char *, int); static int set_arg_med_devtype(struct cmd *, char *, char *, char *, int); static int test_arg_med_devtype(struct cmd *, char *, char *, char *, int); static struct arg_handlers arg_handlers[] = { { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable, }, { .arg = ARG_MED_DEVTYPE, .arg_class = TLV_ARG, .handle_get = get_arg_med_devtype, .handle_set = set_arg_med_devtype, .handle_test = test_arg_med_devtype, }, { .arg = 0 } }; static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[256]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_TIA_TR41 << 8): case (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES: case (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY: case (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID: case (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID: snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (get_config_setting(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02zx%s%04zx%s", strlen(arg), arg, strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len, bool test) { int value; char arg_path[256]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_TIA_TR41 << 8): case (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES: case (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY: case (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID: case (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_config_setting(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) return cmd_failed; snprintf(obuf, obuf_len, "enableTx = %s\n", value ? "yes" : "no"); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, obuf, obuf_len, true); } static int get_arg_med_devtype(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { long value; char *s; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_TIA_TR41 << 8): value = get_med_devtype(cmd->ifname, cmd->type); break; case (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES: case (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY: case (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID: case (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID: return cmd_not_applicable; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } switch (value) { case LLDP_MED_DEVTYPE_NOT_DEFINED: s = VAL_MED_NOT; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: s = VAL_MED_CLASS_I; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: s = VAL_MED_CLASS_II; break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: s = VAL_MED_CLASS_III; break; case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: s = VAL_MED_NETCON; break; default: return cmd_failed; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int _set_arg_med_devtype(struct cmd *cmd, char *argvalue, char *obuf, int obuf_len, bool test) { long value; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case (OUI_TIA_TR41 << 8): break; case (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES: case (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY: case (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID: case (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME: case (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID: case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_MED_NOT)) value = LLDP_MED_DEVTYPE_NOT_DEFINED; else if (!strcasecmp(argvalue, VAL_MED_CLASS_I)) value = LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I; else if (!strcasecmp(argvalue, VAL_MED_CLASS_II)) value = LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II; else if (!strcasecmp(argvalue, VAL_MED_CLASS_III)) value = LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III; else if (!strcasecmp(argvalue, VAL_MED_NETCON)) value = LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY; else return cmd_invalid; if (test) return cmd_success; set_med_devtype(cmd->ifname, cmd->type, value); /* set up default enabletx values based on class type */ switch (value) { case LLDP_MED_DEVTYPE_NOT_DEFINED: tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_RESERVED); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_LOCATION_ID); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_EXTENDED_PVMDI); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME); tlv_disabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID); break; case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_III: case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_II: tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_NETWORK_POLICY); case LLDP_MED_DEVTYPE_ENDPOINT_CLASS_I: case LLDP_MED_DEVTYPE_NETWORK_CONNECTIVITY: tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_RESERVED); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_CAPABILITIES); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_HWREV); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_FWREV); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SWREV); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_SERIAL); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MANUFACTURER); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_MODELNAME); tlv_enabletx(cmd->ifname, cmd->type, (OUI_TIA_TR41 << 8) | LLDP_MED_INV_ASSETID); tlv_enabletx(cmd->ifname, cmd->type, SYSTEM_CAPABILITIES_TLV); tlv_enabletx(cmd->ifname, cmd->type, (LLDP_MOD_8023 << 8) | LLDP_8023_MACPHY_CONFIG_STATUS); break; default: return cmd_failed; } snprintf(obuf, obuf_len, "devtype = %li\n", value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int set_arg_med_devtype(struct cmd *cmd, UNUSED char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_med_devtype(cmd, argvalue, obuf, obuf_len, false); } static int test_arg_med_devtype(struct cmd *cmd, UNUSED char *arg, char *argvalue, char *obuf, int obuf_len) { return _set_arg_med_devtype(cmd, argvalue, obuf, obuf_len, true); } struct arg_handlers *med_get_arg_handlers() { return &arg_handlers[0]; } lldpad-0.9.46/lldp_orgspec_clif.c000066400000000000000000000173551215142747300167110ustar00rootroot00000000000000/******************************************************************************* Implementation of Organisation Specific TLVs for LLDP (c) Copyright SuSE Linux Products GmbH, 2011 Author(s): Hannes Reinecke This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #include #include #include #include #include #include "lldp_mod.h" #include "clif_msgs.h" #include "lldp.h" #include "lldp_orgspec_clif.h" #define VNAME_SIZE 32 #define PROTOID_SIZE 256 static void orgspec_print_pvid_tlv(u16 len, char *info); static void orgspec_print_ppvid_tlv(u16 len, char *info); static void orgspec_print_vlan_name_tlv(u16 len, char *info); static void orgspec_print_protoid_tlv(u16 len, char *info); static void orgspec_print_vid_usage_tlv(u16 len, char *info); static void orgspec_print_mgmt_vid_tlv(u16 len, char *info); static void orgspec_print_link_aggr_tlv(u16 len, char *info); static int orgspec_print_help(); u32 orgspec_lookup_tlv_name(char *tlvid_str); static const struct lldp_mod_ops orgspec_ops_clif = { .lldp_mod_register = orgspec_cli_register, .lldp_mod_unregister = orgspec_cli_unregister, .print_tlv = orgspec_print_tlv, .lookup_tlv_name = orgspec_lookup_tlv_name, .print_help = orgspec_print_help, }; struct type_name_info orgspec_tlv_names[] = { { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_PVID, .name = "Port VLAN ID TLV", .key = "PVID", .print_info = orgspec_print_pvid_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_PPVID, .name = "Port and Protocol VLAN ID TLV", .key = "PPVID", .print_info = orgspec_print_ppvid_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_VLAN_NAME, .name = "VLAN Name TLV", .key = "vlanName", .print_info = orgspec_print_vlan_name_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_PROTO_ID, .name = "Protocol Identity TLV", .key = "ProtoID", .print_info = orgspec_print_protoid_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_VID_USAGE, .name = "VID Usage Digest TLV", .key = "vidUsage", .print_info = orgspec_print_vid_usage_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_MGMT_VID, .name = "Management VID TLV", .key = "mgmtVID", .print_info = orgspec_print_mgmt_vid_tlv }, { .type = (OUI_IEEE_8021 << 8) | ORG_SPEC_LINK_AGGR, .name = "Link Aggregation TLV", .key = "linkAggr", .print_info = orgspec_print_link_aggr_tlv }, { .type = INVALID_TLVID, } }; static int orgspec_print_help() { struct type_name_info *tn = &orgspec_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } struct lldp_module *orgspec_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); return NULL; } mod->id = OUI_IEEE_8021; mod->ops = &orgspec_ops_clif; return mod; } void orgspec_cli_unregister(struct lldp_module *mod) { free(mod); } static void orgspec_print_pvid_tlv(u16 len, char *info) { u16 pvid; if (len != 2) { printf("Bad PVID TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&pvid, sizeof(pvid))) { printf("Unable to decode PVID !\n"); return; } pvid = ntohs(pvid); if (pvid == 0) printf("PVID: unsupported"); else printf("PVID: %d", pvid); printf("\n"); } static void orgspec_print_ppvid_tlv(u16 len, char *info) { u16 pvid; u8 flags; if (len != 3) { printf("Bad PPVID TLV: %s\n", info); return; } if (hexstr2bin(info, &flags, sizeof(flags))) { printf("Unable to decode PVID flags !\n"); return; } if (flags & 2 && (flags & 1) != 0) { printf("Error decoding ppvid, discard\n"); return; } if (hexstr2bin(info + 2, (u8 *)&pvid, sizeof(pvid))) { printf("Unable to decode PVID !\n"); return; } printf("PVID: %x,%s supported,%s enabled", ntohs(pvid), flags & 2 ? "" : " not", flags & 1 ? "" : " not"); printf("\n"); } static void orgspec_print_vlan_name_tlv(u16 len, char *info) { u16 vid; u8 name_len; unsigned char vlan_name[VNAME_SIZE] = {0}; if (len < 4) { printf("Bad VLAN Name TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&vid, sizeof(vid))) { printf("Unable to decode VID !\n"); return; } if (hexstr2bin(info + 4, &name_len, sizeof(name_len))) name_len = 0; if (!hexstr2bin(info + 6, vlan_name, name_len)) printf("VID %d: Name %s", ntohs(vid), vlan_name); printf("\n"); } static void orgspec_print_protoid_tlv(u16 len, char *info) { u8 protoid_len; unsigned char protoid[PROTOID_SIZE] = {0}; int i; if (len < 1) { printf("Bad Protocol Identity TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&protoid_len, sizeof(protoid_len))) { printf("Unable to decode VID !\n"); return; } if (!hexstr2bin(info + 2, protoid, protoid_len)) { for (i = 0; i < protoid_len; i++) printf("%02x.", protoid[i]); } printf("\n"); } static void orgspec_print_vid_usage_tlv(u16 len, char *info) { u16 vid; u8 name_len; unsigned char vlan_name[VNAME_SIZE] = {0}; if (len < 4) { printf("Bad VLAN Name TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&vid, sizeof(vid))) { printf("Unable to decode VID !\n"); return; } if (hexstr2bin(info + 4, &name_len, sizeof(name_len))) name_len = 0; if (!hexstr2bin(info + 6, vlan_name, name_len)) printf("VID %d: Name %s", ntohs(vid), vlan_name); printf("\n"); } static void orgspec_print_mgmt_vid_tlv(u16 len, char *info) { u16 mgmt_vid; if (len != 2) { printf("Bad Mgmt VID TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&mgmt_vid, sizeof(mgmt_vid))) { printf("Unable to decode PVID !\n"); return; } mgmt_vid = ntohs(mgmt_vid); if (mgmt_vid == 0) printf("Mgmt VID: unsupported\n"); else printf("Mgmt VID: %d\n", mgmt_vid); } static void orgspec_print_link_aggr_tlv(u16 len, char *info) { u8 agg_status; u32 agg_portid; if (len != 5) { printf("Bad Link Aggregation TLV: %s\n", info); return; } if (hexstr2bin(info, (u8 *)&agg_status, sizeof(agg_status))) { printf("Unable to decode Link Aggregation TLV !\n"); return; } if (!hexstr2bin(info + 2, (u8 *)&agg_portid, sizeof(agg_portid))) { printf("Aggregation %scapable\n", (agg_status & 0x01) ? "" : "not "); printf("\tCurrently %saggregated\n", (agg_status & 0x02) ? "" : "not "); printf("\tAggregated Port ID: %d\n", ntohl(agg_portid)); } } /* return 1: if it printed the TLV * 0: if it did not */ int orgspec_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &orgspec_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } u32 orgspec_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &orgspec_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } lldpad-0.9.46/lldp_qbg_utils.c000066400000000000000000000055731215142747300162420ustar00rootroot00000000000000/****************************************************************************** Implementation of ECP according to 802.1Qbg (c) Copyright IBM Corp. 2010, 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ /* * This file contains common support utilities for the ECP protocols. */ #include #include #include "lldp.h" #include "lldp_mod.h" #include "messages.h" #include "lldp_qbg_utils.h" extern int loglvl; /* Global lldpad log level */ extern struct lldp_head lldp_head; /* * hexdump_frame - print raw evb/ecp/vdp frame */ void hexdump_frame(const char *ifname, char *txt, const unsigned char *buf, size_t len) { size_t i; int left = 0; char buffer[ETH_FRAME_LEN * 3]; /* Only collect data when the loglvl ensures data printout */ if (LOG_DEBUG < loglvl) return; for (i = 0; i < len; i++) { int c; c = snprintf(buffer + left, sizeof buffer - left, "%02x%c", buf[i], !((i + 1) % 16) ? '\n' : ' '); if (c > 0 && (c < (int)sizeof buffer - left)) left += c; } LLDPAD_DBG("%s:%s %s\n%s\n", __func__, ifname, txt, buffer); } /* * Function to advertise changed variables to other modules. * * Parameters are interface name, target module id and data. * When sending the data, the module call back function contains the * module id of the sender. * * Return 0 when no addressee found or addressess found but addressee was * unable to handle data. */ int modules_notify(int id, int sender_id, char *ifname, void *data) { struct lldp_module *mp = find_module_by_id(&lldp_head, id); int rc = 0; if (mp && mp->ops->lldp_mod_notify) rc = mp->ops->lldp_mod_notify(sender_id, ifname, data); LLDPAD_DBG("%s:%s target-id:%#x rc:%d\n", __func__, ifname, id, rc); return rc; } int vdp_uuid2str(const u8 *p, char *dst, size_t size) { if (dst && size > VDP_UUID_STRLEN) { snprintf(dst, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x" "-%02x%02x-%02x%02x%02x%02x%02x%02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); return 0; } return -1; } lldpad-0.9.46/lldp_rtnl.c000066400000000000000000000203631215142747300152220ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "linux/netlink.h" #include "linux/rtnetlink.h" #include "linux/dcbnl.h" #include "linux/if.h" #include "lldp_util.h" #include "lldp_rtnl.h" #include "messages.h" #include "lldp.h" #define NLMSG(c) ((struct nlmsghdr *) (c)) #define NLMSG_SIZE 1024 /* * Helper functions to construct a netlink message. * The functions assume the nlmsghdr.nlmsg_len is set correctly. */ void mynla_nest_end(struct nlmsghdr *nlh, struct nlattr *start) { start->nla_type |= NLA_F_NESTED; start->nla_len = (void *)nlh + nlh->nlmsg_len - (void *)start; } struct nlattr *mynla_nest_start(struct nlmsghdr *nlh, int type) { struct nlattr *ap = (struct nlattr *)((void *)nlh + nlh->nlmsg_len); ap->nla_type = type; nlh->nlmsg_len += NLA_HDRLEN; return ap; } void mynla_put(struct nlmsghdr *nlh, int type, size_t len, void *data) { struct nlattr *ap = (struct nlattr *)((void *)nlh + nlh->nlmsg_len); ap->nla_type = type; ap->nla_len = NLA_HDRLEN + len; memcpy(ap + 1, data, len); nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(len); } void mynla_put_u16(struct nlmsghdr *nlh, int type, __u16 data) { mynla_put(nlh, type, sizeof data, &data); } void mynla_put_u32(struct nlmsghdr *nlh, int type, __u32 data) { mynla_put(nlh, type, sizeof data, &data); } typedef int rtnl_handler(struct nlmsghdr *nh, void *arg); /** * rtnl_recv - receive from a routing netlink socket * @s: routing netlink socket with data ready to be received * * Returns: 0 when NLMSG_DONE is received * <0 on error * >0 when more data is expected */ static int rtnl_recv(int s, UNUSED rtnl_handler *fn, UNUSED void *arg) { char buf[8192]; struct nlmsghdr *nh; int res; int rc = 0; unsigned len; bool more = false; more: res = recv(s, buf, sizeof(buf), 0); if (res < 0) return res; len = res; for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_flags & NLM_F_MULTI) more = true; switch (nh->nlmsg_type) { case NLMSG_NOOP: break; case NLMSG_ERROR: rc = ((struct nlmsgerr *)NLMSG_DATA(nh))->error; break; case NLMSG_DONE: more = false; break; default: break; } } if (more) goto more; return rc; } #define NLMSG_TAIL(nmsg) \ ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) static void add_rtattr(struct nlmsghdr *n, int type, const void *data, int alen) { struct rtattr *rta = NLMSG_TAIL(n); int len = RTA_LENGTH(alen); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); } static ssize_t rtnl_send_linkmode(int s, int ifindex, const char *ifname, __u8 linkmode) { struct { struct nlmsghdr nh; struct ifinfomsg ifm; char attrbuf[ RTA_SPACE(IFNAMSIZ) /* IFNAME */ + RTA_SPACE(1)]; /* LINKMODE */ } req = { .nh = { .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .nlmsg_type = RTM_SETLINK, .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, }, .ifm = { .ifi_index = ifindex, }, }; if (ifname) add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname)); add_rtattr(&req.nh, IFLA_LINKMODE, &linkmode, 1); return send(s, &req, req.nh.nlmsg_len, 0); } static int rtnl_set_linkmode(int ifindex, const char *ifname, __u8 linkmode) { int s; int rc; s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (s < 0) return s; rc = rtnl_send_linkmode(s, ifindex, ifname, linkmode); if (rc < 0) goto out; rc = rtnl_recv(s, NULL, NULL); out: close(s); return rc; } static ssize_t rtnl_send_operstate(int s, int ifindex, char *ifname, __u8 operstate) { struct { struct nlmsghdr nh; struct ifinfomsg ifm; char attrbuf[ RTA_SPACE(IFNAMSIZ) /* IFNAME */ + RTA_SPACE(1)]; /* OPERSTATE */ } req = { .nh = { .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .nlmsg_type = RTM_SETLINK, .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, }, .ifm = { .ifi_index = ifindex, }, }; if (ifname) add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname)); add_rtattr(&req.nh, IFLA_OPERSTATE, &operstate, 1); return send(s, &req, req.nh.nlmsg_len, 0); } static ssize_t rtnl_recv_operstate(int s, int ifindex, char *ifname, __u8 *operstate) { struct nlmsghdr *nlh; struct ifinfomsg *ifi; struct rtattr *rta; int attrlen; int rc = -1; nlh = malloc(NLMSG_SIZE); if (!nlh) return rc; memset(nlh, 0, NLMSG_SIZE); /* send ifname request */ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_GETLINK; nlh->nlmsg_flags = NLM_F_REQUEST; ifi = NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = ifindex; if (ifname) add_rtattr(nlh, IFLA_IFNAME, ifname, strlen(ifname)); rc = send(s, nlh, nlh->nlmsg_len, 0); if (rc < 0) goto out; /* recv ifname reply */ memset(nlh, 0, NLMSG_SIZE); rc = recv(s, (void *) nlh, NLMSG_SIZE, MSG_DONTWAIT); if (rc < 0) goto out; ifi = NLMSG_DATA(nlh); rta = IFLA_RTA(ifi); attrlen = NLMSG_PAYLOAD(nlh, 0) - sizeof(struct ifinfomsg); while (RTA_OK(rta, attrlen)) { if (rta->rta_type == IFLA_OPERSTATE) memcpy(operstate, RTA_DATA(rta), sizeof(__u8)); rta = RTA_NEXT(rta, attrlen); } out: free(nlh); return rc; } int set_operstate(char *ifname, __u8 operstate) { int s; int rc; int ifindex = 0; s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (s < 0) return s; rc = rtnl_send_operstate(s, ifindex, ifname, operstate); if (rc < 0) goto out; rc = rtnl_recv(s, NULL, NULL); out: close(s); return rc; } int get_operstate(char *ifname) { int s; int ifindex; __u8 operstate = IF_OPER_UNKNOWN; ifindex = get_ifidx(ifname); s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (s < 0) return s; rtnl_recv_operstate(s, ifindex, ifname, &operstate); close(s); return operstate; } int set_linkmode(const char *ifname, __u8 linkmode) { return rtnl_set_linkmode(0, ifname, linkmode); } int get_perm_hwaddr(const char *ifname, u8 *buf_perm, u8 *buf_san) { int s; struct rtattr *rta; int rc = 0; struct { struct nlmsghdr nh; struct dcbmsg d; union { struct rtattr rta; char attrbuf[RTA_SPACE(2 * IFNAMSIZ)]; } u; } req = { .nh = { .nlmsg_len = NLMSG_LENGTH(sizeof(struct dcbmsg)), .nlmsg_type = RTM_GETDCB, .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, }, .d = { .cmd = DCB_CMD_GPERM_HWADDR, .dcb_family = AF_UNSPEC, .dcb_pad = 0, }, }; s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (s < 0) { rc = -EIO; goto out_nosock; } add_rtattr(&req.nh, DCB_ATTR_IFNAME, ifname, strlen(ifname) + 1); add_rtattr(&req.nh, DCB_ATTR_PERM_HWADDR, NULL, 0); rc = send(s, &req.nh, req.nh.nlmsg_len, 0); if (rc < 0) goto out; /* recv ifname reply */ memset(&req, 0, sizeof(req)); rc = recv(s, (void *) &req, sizeof(req), MSG_DONTWAIT); if (rc < 0) goto out; if (req.d.cmd != DCB_CMD_GPERM_HWADDR) { rc = -EIO; goto out; } rta = &req.u.rta; if (rta->rta_type != DCB_ATTR_PERM_HWADDR) { /* Do we really want to code up an attribute parser?? */ rc = -EIO; goto out; } memcpy(buf_perm, RTA_DATA(rta), ETH_ALEN); memcpy(buf_san, RTA_DATA(rta) + ETH_ALEN, ETH_ALEN); out: close(s); out_nosock: return rc; } lldpad-0.9.46/lldp_tlv.c000066400000000000000000000117351215142747300150530ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include "lldp_tlv.h" #include "lldp.h" #include "lldp/ports.h" #include "lldp/l2_packet.h" #include "dcb_types.h" #include "messages.h" void somethingChangedLocal(const char *ifname, int type) { struct lldp_agent *agent; agent = lldp_agent_find_by_type(ifname, type); if (agent == NULL) return; agent->tx.localChange = 1; agent->tx.txFast = agent->timers.txFastInit; return; } int tlv_ok(struct unpacked_tlv *tlv) { if (!tlv || (!tlv->length && tlv->type)) return 0; else return 1; } struct packed_tlv *pack_tlv(struct unpacked_tlv *tlv) { u16 tl = 0; struct packed_tlv *pkd_tlv = NULL; if (!tlv_ok(tlv)) return NULL; tl = tlv->type; tl = tl << 9; tl |= tlv->length & 0x01ff; tl = htons(tl); pkd_tlv = (struct packed_tlv *)malloc(sizeof(struct packed_tlv)); if(!pkd_tlv) { LLDPAD_DBG("pack_tlv: Failed to malloc pkd_tlv\n"); return NULL; } memset(pkd_tlv,0,sizeof(struct packed_tlv)); pkd_tlv->size = tlv->length + sizeof(tl); pkd_tlv->tlv = (u8 *)malloc(pkd_tlv->size); if(pkd_tlv->tlv) { memset(pkd_tlv->tlv,0, pkd_tlv->size); memcpy(pkd_tlv->tlv, &tl, sizeof(tl)); if (tlv->length) memcpy(&pkd_tlv->tlv[sizeof(tl)], tlv->info, tlv->length); } else { LLDPAD_DBG("pack_tlv: Failed to malloc tlv\n"); free(pkd_tlv); pkd_tlv = NULL; return NULL; } return pkd_tlv; } /* * pack the input tlv and put it at the end of the already packed tlv mtlv * update the total size of the out put mtlv */ int pack_tlv_after(struct unpacked_tlv *tlv, struct packed_tlv *mtlv, int length) { struct packed_tlv *ptlv; if (!tlv) return 0; /* no TLV is ok */ if (!tlv_ok(tlv)) return -1; ptlv = pack_tlv(tlv); if (!ptlv) return -1; if (ptlv->size + mtlv->size > length) { ptlv = free_pkd_tlv(ptlv); return -1; } memcpy(&mtlv->tlv[mtlv->size], ptlv->tlv, ptlv->size); mtlv->size += ptlv->size; ptlv = free_pkd_tlv(ptlv); return 0; } struct unpacked_tlv *unpack_tlv(struct packed_tlv *tlv) { u16 tl = 0; struct unpacked_tlv *upkd_tlv = NULL; if(!tlv) { return NULL; } memcpy(&tl,tlv->tlv, sizeof(tl)); tl = ntohs(tl); upkd_tlv = (struct unpacked_tlv *)malloc(sizeof(struct unpacked_tlv)); if(upkd_tlv) { memset(upkd_tlv,0, sizeof(struct unpacked_tlv)); upkd_tlv->length = tl & 0x01ff; upkd_tlv->info = (u8 *)malloc(upkd_tlv->length); if(upkd_tlv->info) { memset(upkd_tlv->info,0, upkd_tlv->length); tl = tl >> 9; upkd_tlv->type = (u8)tl; memcpy(upkd_tlv->info, &tlv->tlv[sizeof(tl)], upkd_tlv->length); } else { LLDPAD_DBG("unpack_tlv: Failed to malloc info\n"); free (upkd_tlv); return NULL; } } else { LLDPAD_DBG("unpack_tlv: Failed to malloc upkd_tlv\n"); return NULL; } return upkd_tlv; } struct unpacked_tlv *free_unpkd_tlv(struct unpacked_tlv *tlv) { if (tlv != NULL) { if (tlv->info != NULL) { free(tlv->info); tlv->info = NULL; } free(tlv); tlv = NULL; } return NULL; } struct packed_tlv *free_pkd_tlv(struct packed_tlv *tlv) { if (tlv != NULL) { if (tlv->tlv != NULL) { free(tlv->tlv); tlv->tlv = NULL; } free(tlv); tlv = NULL; } return NULL; } struct packed_tlv *create_ptlv() { struct packed_tlv *ptlv = (struct packed_tlv *)malloc(sizeof(struct packed_tlv)); if(ptlv) memset(ptlv, 0, sizeof(struct packed_tlv)); return ptlv; } struct unpacked_tlv *create_tlv() { struct unpacked_tlv *tlv = (struct unpacked_tlv *)malloc(sizeof(struct unpacked_tlv)); if(tlv) { memset(tlv,0, sizeof(struct unpacked_tlv)); return tlv; } else { LLDPAD_DBG("create_tlv: Failed to malloc tlv\n"); return NULL; } } struct unpacked_tlv *bld_end_tlv() { struct unpacked_tlv *tlv = create_tlv(); if(tlv) { tlv->type = END_OF_LLDPDU_TLV; tlv->length = 0; tlv->info = NULL; } return tlv; } struct packed_tlv *pack_end_tlv() { struct unpacked_tlv *tlv; struct packed_tlv *ptlv; tlv = bld_end_tlv(); ptlv = pack_tlv(tlv); free(tlv); return ptlv; } lldpad-0.9.46/lldp_util.c000066400000000000000000000656051215142747300152300ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/if_bonding.h" #include "linux/if_bridge.h" #include "linux/ethtool.h" #include "linux/rtnetlink.h" #include "linux/if_vlan.h" #include "linux/if.h" #include "lldp.h" #include "lldp_util.h" #include "messages.h" #include "lldp_dcbx_nl.h" static int hex2num(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); if (a < 0) return -1; b = hex2num(*hex++); if (b < 0) return -1; return (a << 4) | b; } /** * bin2hexstr - Convert binary data to ASCII string * @hex: ASCII hex string (e.g., "01ab") * @buf: Buffer for the binary data * @len: Length of the text to convert in bytes (of buf); hex will be double * this size * Returns: 0 on success, -1 on failure (invalid hex string) */ #define BYTE2CHAR(b) (((b) > 9) ? ((b) - 0xa + 'A') : ((b) + '0')) int bin2hexstr(const u8 *hex, size_t hexlen, char *buf, size_t buflen) { u8 b; size_t i, j; for (i = j = 0; (i < hexlen) && (j < buflen); i++, j +=2) { b = (hex[i] & 0xf0) >> 4; buf[j] = BYTE2CHAR(b); b = hex[i] & 0x0f; buf[j + 1] = BYTE2CHAR(b); } return 0; } /** * hexstr2bin - Convert ASCII hex string into binary data * @hex: ASCII hex string (e.g., "01ab") * @buf: Buffer for the binary data * @len: Length of the text to convert in bytes (of buf); hex will be double * this size * Returns: 0 on success, -1 on failure (invalid hex string) */ int hexstr2bin(const char *hex, u8 *buf, size_t len) { size_t i; int a; const char *ipos = hex; u8 *opos = buf; for (i = 0; i < len; i++) { a = hex2byte(ipos); if (a < 0) return -1; *opos++ = a; ipos += 2; } return 0; } char *print_mac(char *mac, char *buf) { sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)*(mac + 0), (unsigned char)*(mac + 1), (unsigned char)*(mac + 2), (unsigned char)*(mac + 3), (unsigned char)*(mac + 4), (unsigned char)*(mac + 5)); return buf; } static int get_ioctl_socket(void) { static int ioctl_socket = -1; if (ioctl_socket >= 0) return ioctl_socket; ioctl_socket = socket(PF_INET, SOCK_DGRAM, 0); if (ioctl_socket < 0) { int err = errno; perror("socket create failed\n"); errno = err; } return ioctl_socket; } int is_valid_lldp_device(const char *device_name) { if (is_loopback(device_name)) return 0; if (is_vlan(device_name)) return 0; if (is_bridge(device_name)) return 0; if (is_macvtap(device_name)) return 0; return 1; } /** * is_bond - check if interface is a bond interface * @ifname: name of the interface * * Returns 0 if ifname is not a bond, 1 if it is a bond. */ int is_bond(const char *ifname) { int fd; int rc = 0; struct ifreq ifr; ifbond ifb; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ifname); memset(&ifb, 0, sizeof(ifb)); ifr.ifr_data = (caddr_t)&ifb; if (ioctl(fd, SIOCBONDINFOQUERY, &ifr) == 0) rc = 1; } return rc; } /** * is_san_mac - check if address is a san mac addr * @addr: san mac address * * Returns 0 if addr is NOT san mac, 1 if it is a san mac. * * BUG: this does not do anything to prove it's a sanmac!!!! * SAN MAC is no different than LAN MAC, no way to tell!!! */ int is_san_mac(u8 *addr) { int i; for ( i = 0; i < ETH_ALEN; i++) { if ( addr[i]!= 0xff ) return 1; } return 0; } /** * get_src_mac_from_bond - select a source MAC to use for slave * @bond_port: pointer to port structure for a bond interface * @ifname: interface name of the slave port * @addr: address of buffer in which to return the selected MAC address * * Checks to see if ifname is a slave of the bond port. If it is, * then a * Returns 0 if a source MAC from the bond could not be found. 1 is * returned if the slave was found in the bond. addr is updated with * the source MAC that should be used. */ int get_src_mac_from_bond(struct port *bond_port, char *ifname, u8 *addr) { int fd; struct ifreq ifr; ifbond ifb; ifslave ifs; char act_ifname[IFNAMSIZ]; unsigned char bond_mac[ETH_ALEN], san_mac[ETH_ALEN]; int found = 0; int i; fd = get_ioctl_socket(); if (fd < 0) return 0; memset(bond_mac, 0, sizeof(bond_mac)); memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, bond_port->ifname); memset(&ifb, 0, sizeof(ifb)); ifr.ifr_data = (caddr_t)&ifb; if (ioctl(fd,SIOCBONDINFOQUERY, &ifr) == 0) { /* get the MAC address for the current bond port */ if (ioctl(fd,SIOCGIFHWADDR, &ifr) == 0) memcpy(bond_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); else perror("error getting bond MAC address"); /* scan the bond's slave ports and looking for the * current port and the active slave port. */ memset(act_ifname, 0, sizeof(act_ifname)); for (i = 0; i < ifb.num_slaves; i++) { memset(&ifs, 0, sizeof(ifs)); ifs.slave_id = i; ifr.ifr_data = (caddr_t)&ifs; if (ioctl(fd,SIOCBONDSLAVEINFOQUERY, &ifr) == 0) { if (!strncmp(ifs.slave_name, ifname, IFNAMSIZ)) found = 1; if (ifs.state == BOND_STATE_ACTIVE) strncpy(act_ifname, ifs.slave_name, IFNAMSIZ); } } } /* current port is not a slave of the bond */ if (!found) return 0; /* Get slave port's current perm MAC address * This will be the default return value */ memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ifname); if (ioctl(fd,SIOCGIFHWADDR, &ifr) == 0) { memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); } else { perror("error getting slave MAC address"); return 0; } switch (ifb.bond_mode) { case BOND_MODE_ACTIVEBACKUP: /* If current port is not the active slave, then * if the bond MAC is equal to the port's * permanent MAC, then find and return * the permanent MAC of the active * slave port. Otherwise, return the * permanent MAC of the port. */ if (strncmp(ifname, act_ifname, IFNAMSIZ)) if (get_perm_hwaddr(ifname, addr, san_mac) == 0) if (!memcmp(bond_mac, addr, ETH_ALEN)) get_perm_hwaddr(act_ifname, addr, san_mac); break; default: /* Use the current MAC of the port */ break; } return 1; } int is_valid_mac(const u8 *mac) { return !!(mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]); } int read_int(const char *path) { int rc = -1; char buf[256]; FILE *f = fopen(path, "r"); if (f) { if (fgets(buf, sizeof(buf), f)) rc = atoi(buf); fclose(f); } return rc; } int read_bool(const char *path) { return read_int(path) > 0; } int get_ifflags(const char *ifname) { int fd; int flags = 0; struct ifreq ifr; /* use ioctl */ fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) flags = ifr.ifr_flags; } return flags; } int get_ifpflags(const char *ifname) { int fd; int flags = 0; struct ifreq ifr; /* use ioctl */ fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFPFLAGS, &ifr) == 0) flags = ifr.ifr_flags; } return flags; } int get_iftype(const char *ifname) { char path[256]; snprintf(path, sizeof(path), "/sys/class/net/%s/type", ifname); return read_int(path); } int get_iffeatures(const char *ifname) { char path[256]; snprintf(path, sizeof(path), "/sys/class/net/%s/features", ifname); return read_int(path); } int get_iflink(const char *ifname) { char path[256]; snprintf(path, sizeof(path), "/sys/class/net/%s/iflink", ifname); return read_int(path); } int is_ether(const char *ifname) { /* check for bridge in sysfs */ int type = get_iftype(ifname); return (type == ARPHRD_ETHER) || (type == ARPHRD_EETHER); } int is_loopback(const char *ifname) { return get_ifflags(ifname) & IFF_LOOPBACK; } int is_p2p(const char *ifname) { return get_ifflags(ifname) & IFF_POINTOPOINT; } int is_noarp(const char *ifname) { return get_ifflags(ifname) & IFF_NOARP; } int is_mbond(const char *ifname) { return get_ifflags(ifname) & IFF_MASTER; } int is_sbond(const char *ifname) { return get_ifflags(ifname) & IFF_SLAVE; } int is_slave(const char *ifmaster, const char *ifslave) { int i; int rc = 0; int fd; struct ifreq ifr; struct ifbond ifb; struct ifslave ifs; if (!is_mbond(ifmaster)) goto out_done; fd = get_ioctl_socket(); if (fd < 0) goto out_done; memset(&ifr, 0, sizeof(ifr)); memset(&ifb, 0, sizeof(ifb)); strncpy(ifr.ifr_name, ifmaster, IFNAMSIZ); ifr.ifr_data = (caddr_t)&ifb; if (ioctl(fd, SIOCBONDINFOQUERY, &ifr)) goto out_done; for (i = 0; i < ifb.num_slaves; i++) { memset(&ifs, 0, sizeof(ifs)); ifs.slave_id = i; ifr.ifr_data = (caddr_t)&ifs; if (ioctl(fd, SIOCBONDSLAVEINFOQUERY, &ifr) == 0) { if (!strncmp(ifs.slave_name, ifslave, IFNAMSIZ)) { rc = 1; break; } } } out_done: return rc; } int get_ifidx(const char *ifname) { int fd; int idx = 0; struct ifreq ifreq; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifreq, 0, sizeof(ifreq)); strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); if (ioctl(fd, SIOCGIFINDEX, &ifreq) == 0) idx = ifreq.ifr_ifindex; } return idx; } int get_master(const char *ifname) { int i; int idx = 0; int fd; int cnt; struct ifreq *ifr = NULL; struct ifconf ifc; char ifcbuf[sizeof(struct ifreq) * 32]; /* if it's a master bond, return its own index */ if (is_mbond(ifname)) return get_ifidx(ifname); fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifc, 0, sizeof(ifc)); memset(ifcbuf, 0, sizeof(ifcbuf)); ifc.ifc_buf = ifcbuf; ifc.ifc_len = sizeof(ifcbuf); if (ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc) == 0) { ifr = ifc.ifc_req; cnt = ifc.ifc_len/sizeof(struct ifreq); for (i = 0; i < cnt; i++, ifr++) { if (!strncmp(ifr->ifr_name, ifname, IFNAMSIZ)) continue; if (!is_mbond(ifr->ifr_name)) continue; if (!is_slave(ifr->ifr_name, ifname)) continue; if (ioctl(fd, SIOCGIFINDEX, ifr) == 0) idx = ifr->ifr_ifindex; break; } } } return idx; } int is_bridge(const char *ifname) { int fd; int rc = 0; char path[256]; DIR *dirp; if (!is_ether(ifname)) { return 0; } /* check for bridge in sysfs */ snprintf(path, sizeof(path), "/sys/class/net/%s/bridge", ifname); dirp = opendir(path); if (dirp) { closedir(dirp); rc = 1; } else { /* use ioctl */ fd = get_ioctl_socket(); if (fd >= 0) { struct ifreq ifr; struct __bridge_info bi; unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO, (unsigned long) &bi, 0, 0 }; ifr.ifr_data = (char *)args; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCDEVPRIVATE, &ifr) == 0) rc = 1; } } return rc; } int is_vlan(const char *ifname) { int fd; int rc = 0; struct vlan_ioctl_args ifv; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifv, 0, sizeof(ifv)); ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; strncpy(ifv.device1, ifname, sizeof(ifv.device1)); if (ioctl(fd, SIOCGIFVLAN, &ifv) == 0) rc = 1; } return rc; } int is_vlan_capable(const char *ifname) { int features = get_iffeatures(ifname); #ifndef NETIF_F_VLAN_CHALLENGED #define NETIF_F_VLAN_CHALLENGED 1024 #endif return !(features & NETIF_F_VLAN_CHALLENGED); } int is_wlan(const char *ifname) { int fd; int rc = 0; struct iwreq iwreq; fd = get_ioctl_socket(); if (fd >= 0) { memset(&iwreq, 0, sizeof(iwreq)); strncpy(iwreq.ifr_name, ifname, sizeof(iwreq.ifr_name)); if (ioctl(fd, SIOCGIWNAME, &iwreq) == 0) rc = 1; } return rc; } #define NLMSG_SIZE 1024 static struct nla_policy ifla_info_policy[IFLA_INFO_MAX + 1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING}, [IFLA_INFO_DATA] = { .type = NLA_NESTED }, }; int is_macvtap(const char *ifname) { int ret, s; struct nlmsghdr *nlh; struct ifinfomsg *ifinfo; struct nlattr *tb[IFLA_MAX+1], *tb2[IFLA_INFO_MAX+1]; s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (s < 0) { goto out; } nlh = malloc(NLMSG_SIZE); if (!nlh) { goto out; } memset(nlh, 0, NLMSG_SIZE); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_GETLINK; nlh->nlmsg_flags = NLM_F_REQUEST; ifinfo = NLMSG_DATA(nlh); ifinfo->ifi_family = AF_UNSPEC; ifinfo->ifi_index = get_ifidx(ifname); ret = send(s, nlh, nlh->nlmsg_len, 0); if (ret < 0) { goto out_free; } memset(nlh, 0, NLMSG_SIZE); do { ret = recv(s, (void *) nlh, NLMSG_SIZE, MSG_DONTWAIT); } while ((ret < 0) && errno == EINTR); if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), (struct nlattr **)&tb, IFLA_MAX, NULL)) { goto out_free; } if (tb[IFLA_IFNAME]) { ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); } else { ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); } if (tb[IFLA_LINKINFO]) { if (nla_parse_nested(tb2, IFLA_INFO_MAX, tb[IFLA_LINKINFO], ifla_info_policy)) { goto out_free; } if (tb2[IFLA_INFO_KIND]) { char *kind = (char*)(RTA_DATA(tb2[IFLA_INFO_KIND])); if (!(strcmp("macvtap", kind) && strcmp("macvlan", kind))) { free(nlh); close(s); return true; } } } else { goto out_free; } out_free: free(nlh); out: close(s); return false; } static int is_router(void) { int rc = 0; char path[256]; snprintf(path, sizeof(path), "/proc/sys/net/ipv4/conf/all/forwarding"); rc = read_bool(path); snprintf(path, sizeof(path), "/proc/sys/net/ipv6/conf/all/forwarding"); rc |= read_bool(path); return rc; } int is_active(const char *ifname) { int fd; int rc = 0; struct ifreq ifr; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) if (ifr.ifr_flags & IFF_UP) rc = 1; } return rc; } int is_autoneg_supported(const char *ifname) { int rc = 0; int fd; struct ifreq ifr; struct ethtool_cmd cmd; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = ETHTOOL_GSET; ifr.ifr_data = &cmd; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCETHTOOL, &ifr) == 0) if (cmd.supported & SUPPORTED_Autoneg) rc = 1; } return rc; } int is_autoneg_enabled(const char *ifname) { int rc = 0; int fd; struct ifreq ifr; struct ethtool_cmd cmd; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = ETHTOOL_GSET; ifr.ifr_data = &cmd; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCETHTOOL, &ifr) == 0) rc = cmd.autoneg; } return rc; } /* IETF RFC 3636 dot3MauType: http://www.rfc-editor.org/rfc/rfc3636.txt */ #define MAUCAPADV_bOther (1 << 0) /* other or unknown */ #define MAUCAPADV_b10baseT (1 << 1) /* 10BASE-T half duplex mode */ #define MAUCAPADV_b10baseTFD (1 << 2) /* 10BASE-T full duplex mode */ #define MAUCAPADV_b100baseT4 (1 << 3) /* 100BASE-T4 */ #define MAUCAPADV_b100baseTX (1 << 4) /* 100BASE-TX half duplex mode */ #define MAUCAPADV_b100baseTXFD (1 << 5) /* 100BASE-TX full duplex mode */ #define MAUCAPADV_b100baseT2 (1 << 6) /* 100BASE-T2 half duplex mode */ #define MAUCAPADV_b100baseT2FD (1 << 7) /* 100BASE-T2 full duplex mode */ #define MAUCAPADV_bFdxPause (1 << 8) /* PAUSE for full-duplex links */ #define MAUCAPADV_bFdxAPause (1 << 9) /* Asymmetric PAUSE for full-duplex links */ #define MAUCAPADV_bFdxSPause (1 << 10) /* Symmetric PAUSE for full-duplex links */ #define MAUCAPADV_bFdxBPause (1 << 11) /* Asymmetric and Symmetric PAUSE for full-duplex links */ #define MAUCAPADV_b1000baseX (1 << 12) /* 1000BASE-X, -LX, -SX, -CX half duplex mode */ #define MAUCAPADV_b1000baseXFD (1 << 13) /* 1000BASE-X, -LX, -SX, -CX full duplex mode */ #define MAUCAPADV_b1000baseT (1 << 14) /* 1000BASE-T half duplex mode */ #define MAUCAPADV_b1000baseTFD (1 << 15) /* 1000BASE-T full duplex mode */ int get_maucaps(const char *ifname) { int fd; u16 caps = MAUCAPADV_bOther; struct ifreq ifr; struct ethtool_cmd cmd; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = ETHTOOL_GSET; ifr.ifr_data = &cmd; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCETHTOOL, &ifr) == 0) { if (cmd.advertising & ADVERTISED_10baseT_Half) caps |= MAUCAPADV_b10baseT; if (cmd.advertising & ADVERTISED_10baseT_Full) caps |= MAUCAPADV_b10baseTFD; if (cmd.advertising & ADVERTISED_100baseT_Half) caps |= MAUCAPADV_b100baseTX; if (cmd.advertising & ADVERTISED_100baseT_Full) caps |= MAUCAPADV_b100baseTXFD; if (cmd.advertising & ADVERTISED_1000baseT_Half) caps |= MAUCAPADV_b1000baseT; if (cmd.advertising & ADVERTISED_1000baseT_Full) caps |= MAUCAPADV_b1000baseTFD; if (cmd.advertising & ADVERTISED_Pause) caps |= (MAUCAPADV_bFdxPause | MAUCAPADV_bFdxSPause); if (cmd.advertising & ADVERTISED_Asym_Pause) caps |= MAUCAPADV_bFdxAPause; if (cmd.advertising & (ADVERTISED_Asym_Pause | ADVERTISED_Pause)) caps |= MAUCAPADV_bFdxBPause; } } return caps; } int get_mautype(const char *ifname) { int rc = 0; int fd; struct ifreq ifr; struct ethtool_cmd cmd; u32 speed; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = ETHTOOL_GSET; ifr.ifr_data = &cmd; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCETHTOOL, &ifr) == 0) { /* TODO: too many dot3MauTypes, * should check duplex, speed, and port */ speed = (cmd.speed_hi << 16) | cmd.speed; if (cmd.port == PORT_AUI) rc = DOT3MAUTYPE_AUI; else if (speed == SPEED_10) rc = DOT3MAUTYPE_10BaseT; else if (speed == SPEED_100) rc = DOT3MAUTYPE_100BaseTXFD; else if (speed == SPEED_1000) rc = DOT3MAUTYPE_1000BaseTFD; } } return rc; } int get_mtu(const char *ifname) { int fd; int rc = 0; struct ifreq ifr; fd = get_ioctl_socket(); if (fd >= 0) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) == 0) rc = ifr.ifr_mtu; } return rc; } int get_mfs(const char *ifname) { int mfs = get_mtu(ifname); #ifndef VLAN_HLEN #define VLAN_HLEN 4 #endif if (mfs) { mfs += ETH_HLEN + ETH_FCS_LEN; if (is_vlan_capable(ifname)) mfs += VLAN_HLEN; } return mfs; } int get_mac(const char *ifname, u8 mac[]) { int fd; int rc = EINVAL; struct ifreq ifr; memset(mac, 0, 6); fd = get_ioctl_socket(); if (fd >= 0) { ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (!ioctl(fd, SIOCGIFHWADDR, &ifr)) { memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); rc = 0; } } return rc; } int get_macstr(const char *ifname, char *addr, size_t size) { u8 mac[6]; int rc; rc = get_mac(ifname, mac); if (rc == 0) { snprintf(addr, size, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } return rc; } u16 get_caps(const char *ifname) { u16 caps = 0; /* how to find TPID to determine C-VLAN vs. S-VLAN ? */ if (is_vlan(ifname)) caps |= SYSCAP_CVLAN; if (is_bridge(ifname)) caps |= SYSCAP_BRIDGE; if (is_router()) caps |= SYSCAP_ROUTER; if (is_wlan(ifname)) caps |= SYSCAP_WLAN; #if 0 if (is_phone(ifname)) caps |= SYSCAP_PHONE; if (is_docsis(ifname)) caps |= SYSCAP_DOCSIS; if (is_repeater(ifname)) caps |= SYSCAP_REPEATER; if (is_tpmr(ifname)) caps |= SYSCAP_TPMR; if (is_other(ifname)) caps |= SYSCAP_OTHER; #endif if (!caps) caps = SYSCAP_STATION; return caps; } int get_saddr(const char *ifname, struct sockaddr_in *saddr) { int fd; int rc = EIO; struct ifreq ifr; fd = get_ioctl_socket(); if (fd >= 0) { ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { memcpy(saddr, &ifr.ifr_addr, sizeof(*saddr)); rc = 0; } } return rc; } int get_ipaddr(const char *ifname, struct in_addr *in) { int rc; struct sockaddr_in sa; rc = get_saddr(ifname, &sa); if (rc == 0) memcpy(in, &sa.sin_addr, sizeof(struct in_addr)); return rc; } int get_ipaddrstr(const char *ifname, char *ipaddr, size_t size) { int rc; struct sockaddr_in sa; rc = get_saddr(ifname, &sa); if (rc == 0) { memset(ipaddr, 0, size); strncpy(ipaddr, inet_ntoa(sa.sin_addr), size); } return rc; } int get_saddr6(const char *ifname, struct sockaddr_in6 *saddr) { int rc = 0; struct ifaddrs *ifa; struct ifaddrs *ifaddr; rc = getifaddrs(&ifaddr); if (rc == 0) { for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if ((ifa->ifa_addr->sa_family == AF_INET6) && (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) == 0)) { memcpy(saddr, ifa->ifa_addr, sizeof(*saddr)); rc = 0; break; } } } freeifaddrs(ifaddr); return rc; } int get_ipaddr6(const char *ifname, struct in6_addr *in6) { int rc; struct sockaddr_in6 sa; rc = get_saddr6(ifname, &sa); if (rc == 0) memcpy(in6, &sa.sin6_addr, sizeof(struct in6_addr)); return rc; } int get_ipaddr6str(const char *ifname, char *ip, size_t size) { #define ifa_sia(i, f) (((f) == AF_INET) ? \ ((void *) &((struct sockaddr_in *) (i))->sin_addr) : \ ((void *) &((struct sockaddr_in6 *) (i))->sin6_addr)) #define ifa_sin(i) \ ((void *) &((struct sockaddr_in *) (i)->ifa_addr)->sin_addr) #define ifa_sin6(i) \ (&((struct sockaddr_in6 *) (i)->ifa_addr)->sin6_addr) int rc = 0; struct sockaddr_in6 sa; rc = get_saddr6(ifname, &sa); if (rc == 0) if (inet_ntop(sa.sin6_family, &sa.sin6_addr, ip, size) == NULL) rc = EIO; return rc; } int get_addr(const char *ifname, int domain, void *buf) { if (domain == AF_INET) return get_ipaddr(ifname, (struct in_addr *)buf); else if (domain == AF_INET6) return get_ipaddr6(ifname, (struct in6_addr *)buf); else if (domain == AF_UNSPEC) return get_mac(ifname, (u8 *)buf); else return -1; } /* MAC_ADDR_STRLEN = strlen("00:11:22:33:44:55") */ #define MAC_ADDR_STRLEN 17 int mac2str(const u8 *mac, char *dst, size_t size) { if (dst && size > MAC_ADDR_STRLEN) { snprintf(dst, size, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return 0; } return -1; } int str2mac(const char *src, u8 *mac, size_t size) { int i = 0; int rc = -1; if (size < 6) goto out_err; if (!src) goto out_err; if (strlen(src) != MAC_ADDR_STRLEN) goto out_err; memset(mac, 0, size); for (i = 0; i < 6; i++, mac++) if (1 != sscanf(&src[i * 3], "%02hhX", mac)) goto out_err; rc = 0; out_err: return rc; } int str2addr(int domain, const char *src, void *dst, size_t size) { if ((domain == AF_INET) || (domain == AF_INET6)) { if (1 == inet_pton(domain, src, dst)) return 0; else return -1; } if (domain == AF_UNSPEC) return str2mac(src, (u8 *)dst, size); return -1; } int addr2str(int domain, const void *src, char *dst, size_t size) { if ((domain == AF_INET) || (domain == AF_INET6)) { if (inet_ntop(domain, src, dst, size)) return 0; else return -1; } if (domain == AF_UNSPEC) return mac2str((u8 *)src, dst, size); return -1; } /* * check_link_status - check the link status of the port * @ifname: the port name * * Returns: 0 if error or no link and non-zero if interface has link */ int check_link_status(const char *ifname) { int fd; struct ifreq ifr; int retval = 0; struct link_value { u32 cmd ; u32 data; } linkstatus = { ETHTOOL_GLINK, 0}; fd = get_ioctl_socket(); if (fd < 0) return retval; memset(&ifr, 0, sizeof(ifr)); strcpy(ifr.ifr_name, ifname); ifr.ifr_data = (caddr_t)&linkstatus; if (ioctl(fd,SIOCETHTOOL, &ifr) == 0) retval = linkstatus.data; return retval; } #define NUM_ARGS 8 int get_arg_val_list(char *ibuf, int ilen, int *ioff, char **args, char **argvals) { u8 arglen; u16 argvalue_len; int *arglens = NULL; int *argvallens = NULL; int *p; int numargs; int i; /* parse out args and argvals */ for (i = 0; ilen - *ioff > 2 * (int)sizeof(arglen); i++) { if (!(i % NUM_ARGS)) { p = (int *) realloc(arglens, (i/NUM_ARGS + 1) * NUM_ARGS * sizeof(int)); if (!p) { free(arglens); return 0; } else { arglens = p; } p = (int *) realloc(argvallens, (i/NUM_ARGS + 1) * NUM_ARGS * sizeof(int)); if (!p) { free(argvallens); return 0; } else { argvallens = p; } } hexstr2bin(ibuf+*ioff, &arglen, sizeof(arglen)); *ioff += 2 * (int)sizeof(arglen); if (ilen - *ioff >= arglen) { args[i] = ibuf+*ioff; *ioff += arglen; *(arglens+i) = arglen; if (ilen - *ioff > 2 * (int)sizeof(argvalue_len)) { hexstr2bin(ibuf+*ioff, (u8 *)&argvalue_len, sizeof(argvalue_len)); argvalue_len = ntohs(argvalue_len); *ioff += 2*sizeof(argvalue_len); if (ilen - *ioff >= argvalue_len) { argvals[i] = ibuf+*ioff; *ioff += argvalue_len; *(argvallens+i) = argvalue_len; } } else { free(arglens); free(argvallens); return 0; } } else { free(arglens); free(argvallens); return 0; } } numargs = i; for (i = 0; i < numargs; i++) { args[i][*(arglens+i)] = '\0'; argvals[i][*(argvallens+i)] = '\0'; } free(arglens); free(argvallens); return numargs; } int get_arg_list(char *ibuf, int ilen, int *ioff, char **args) { u8 arglen; int *arglens = NULL; int *p; int numargs; int i; /* parse out args */ for (i = 0; (ilen - *ioff > 2 * (int)sizeof(arglen)); i++) { if (!(i % NUM_ARGS)) { p = (int *) realloc(arglens, (i/NUM_ARGS + 1) * NUM_ARGS * sizeof(int)); if (!p) { free(arglens); return 0; } else { arglens = p; } } hexstr2bin(ibuf+(*ioff), &arglen, sizeof(arglen)); *ioff += 2*sizeof(arglen); if (ilen - *ioff >= arglen) { args[i] = ibuf+(*ioff); *ioff += arglen; *(arglens+i) = arglen; } else { free(arglens); return 0; } } numargs = i; for (i = 0; i < numargs; i++) args[i][*(arglens+i)] = '\0'; free(arglens); return numargs; } lldpad-0.9.46/lldp_vdp.c000066400000000000000000001430021215142747300150300ustar00rootroot00000000000000/****************************************************************************** Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "lldp.h" #include "lldp_vdp.h" #include "lldp_vdpnl.h" #include "eloop.h" #include "lldp_evb.h" #include "messages.h" #include "config.h" #include "lldp_tlv.h" #include "lldp_vdp_cmds.h" #include "lldp_qbg_utils.h" #include "lldp_mand_clif.h" /* Define Module id. Must match with value in lldp_vdp_clif.c */ #define LLDP_MOD_VDP02 ((LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE) static const char * const vsi_responses[] = { [VDP_RESPONSE_SUCCESS] = "success", [VDP_RESPONSE_INVALID_FORMAT] = "invalid format", [VDP_RESPONSE_INSUFF_RESOURCES] = "insufficient resources", [VDP_RESPONSE_UNUSED_VTID] = "unused VTID", [VDP_RESPONSE_VTID_VIOLATION] = "VTID violation", [VDP_RESPONSE_VTID_VER_VIOLATION] = "VTID version violation", [VDP_RESPONSE_OUT_OF_SYNC] = "out of sync", [VDP_RESPONSE_UNKNOWN] = "unknown response", [VDP_RESPONSE_NO_RESPONSE] = "no response", }; const char * const vsi_states[] = { [VSI_UNASSOCIATED] = "VSI_UNASSOCIATED", [VSI_ASSOC_PROCESSING] = "VSI_ASSOC_PROCESSING", [VSI_ASSOCIATED] = "VSI_ASSOCIATED", [VSI_PREASSOC_PROCESSING] = "VSI_PREASSOC_PROCESSING", [VSI_PREASSOCIATED] = "VSI_PREASSOCIATED", [VSI_DEASSOC_PROCESSING] = "VSI_DEASSOC_PROCESSING", [VSI_EXIT] = "VSI_EXIT", }; int vdp_start_localchange_timer(struct vsi_profile *p); int vdp_remove_profile(struct vsi_profile *profile); int vdp_trigger(struct vsi_profile *profile); void vdp_trace_profile(struct vsi_profile *p) { char instance[VDP_UUID_STRLEN + 2]; struct mac_vlan *mac_vlan; vdp_uuid2str(p->instance, instance, sizeof(instance)); LLDPAD_DBG("profile:%p mode:%d response:%d state:%d (%s) no_nlmsg:%d" " txmit:%i" " mgrid:%d id:%d(%#x) version:%d %s format:%d entries:%d\n", p, p->mode, p->response, p->state, vsi_states[p->state], p->no_nlmsg, p->txmit, p->mgrid, p->id, p->id, p->version, instance, p->format, p->entries); LIST_FOREACH(mac_vlan, &p->macvid_head, entry) { char macbuf[MAC_ADDR_STRLEN + 1]; mac2str(mac_vlan->mac, macbuf, MAC_ADDR_STRLEN); LLDPAD_DBG("profile:%p mac:%s vlan:%d qos:%d pid:%d seq:%d\n", p, macbuf, mac_vlan->vlan, mac_vlan->qos, mac_vlan->req_pid, mac_vlan->req_seq); } } struct vsi_profile *vdp_alloc_profile() { struct vsi_profile *prof; prof = calloc(1, sizeof *prof); if (prof) LIST_INIT(&prof->macvid_head); return prof; } /* * vdp_remove_macvlan - remove all mac/vlan pairs in the profile * @profile: profile to remove * * Remove all allocated pairs on the profile. */ static void vdp_remove_macvlan(struct vsi_profile *profile) { struct mac_vlan *p; while ((p = LIST_FIRST(&profile->macvid_head))) { LIST_REMOVE(p, entry); free(p); } } void vdp_delete_profile(struct vsi_profile *prof) { vdp_remove_macvlan(prof); free(prof); } /* vdp_profile_equal - checks for equality of 2 profiles * @p1: profile 1 * @p2: profile 2 * * returns true if equal, false if not * * compares mgrid, id, version, instance 2 vsi profiles to find * out if they are equal. */ static bool vdp_profile_equal(struct vsi_profile *p1, struct vsi_profile *p2) { if (p1->mgrid != p2->mgrid) return false; if (p1->id != p2->id) return false; if (p1->version != p2->version) return false; if (memcmp(p1->instance, p2->instance, 16)) return false; return true; } /* * vdp_find_profile - Find a profile in the list of profiles already allocated * * Returns pointer to already allocated profile in list, 0 if not. */ struct vsi_profile *vdp_find_profile(struct vdp_data *vd, struct vsi_profile *thisone) { struct vsi_profile *p; LIST_FOREACH(p, &vd->profile_head, profile) { if (vdp_profile_equal(p, thisone)) return p; } return 0; } /* vdp_data - searches vdp_data in the list of modules for this port * @ifname: interface name to search for * * returns vdp_data on success, NULL on error * * searches the list of user_data for the VDP module user_data. */ struct vdp_data *vdp_data(char *ifname) { struct vdp_user_data *ud; struct vdp_data *vd = NULL; ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP02); if (ud) { LIST_FOREACH(vd, &ud->head, entry) { if (!strncmp(ifname, vd->ifname, IFNAMSIZ)) return vd; } } return NULL; } /* vdp_free_tlv - free tlv in vdp_data * @vd: vdp_data * * no return value * * frees up tlv in vdp_data. used in vdp_free_data. */ static void vdp_free_tlv(struct vdp_data *vd) { if (vd) { FREE_UNPKD_TLV(vd, vdp); } } /* vdp_free_data - frees up vdp data * @ud: user data structure * * no return value * * removes vd_structure from the user_data list. frees up tlv in vdp_data. * used in vdp_unregister. */ static void vdp_free_data(struct vdp_user_data *ud) { struct vdp_data *vd; if (ud) { while (!LIST_EMPTY(&ud->head)) { vd = LIST_FIRST(&ud->head); LIST_REMOVE(vd, entry); vdp_free_tlv(vd); free(vd); } } } /* vdp_response2str - map response to string * @response: response received * * no return value * * maps VDP response received for a profile to human readable string for * printing. */ const char *vdp_response2str(int response) { if ((response >= VDP_RESPONSE_SUCCESS) && (response <= VDP_RESPONSE_OUT_OF_SYNC)) return vsi_responses[response]; if (response == VDP_RESPONSE_NO_RESPONSE) return vsi_responses[VDP_RESPONSE_NO_RESPONSE]; return vsi_responses[VDP_RESPONSE_UNKNOWN]; } /* vdp_ack_profiles - clear ackReceived for all profiles with seqnr * @vd: vd for the interface * @seqnr: seqnr the ack has been received with * * no return value * * clear the ackReceived for all profiles which have been sent out with * the seqnr that we now have received the ecp ack for. */ void vdp_ack_profiles(struct vdp_data *vd, int seqnr) { struct vsi_profile *p; LIST_FOREACH(p, &vd->profile_head, profile) { if (p->seqnr == seqnr) { p->ackReceived = false; p->txmit = true; } } } /* vdp_vsis - find out number of VSIs for this interface * @ifname: interfac name * * returns the number of VSIs * * walk through the list of VSIs and return the count. */ int vdp_vsis(char *ifname) { struct vdp_data *vd; struct vsi_profile *p; int count = 0; vd = vdp_data(ifname); if (!vd) return 0; LIST_FOREACH(p, &vd->profile_head, profile) { count++; } return count; } /* vdp_vsis_pending - check for pending VSIs * @vd: vdp data for the interface * * returns the number of VSIs found * * walk through the list of VSIs and return the count. */ int vdp_vsis_pending(struct vdp_data *vd) { struct vsi_profile *p; int count = 0; LIST_FOREACH(p, &vd->profile_head, profile) { if (p->localChange && (p->txmit == false)) count++; } return count; } /* vdp_somethingChangedLocal - set flag if profile has changed * @profile: profile to set the flag for * @flag: set the flag to true or false * * no return value * * set the localChange flag with a mode to indicate a profile has changed. * used next time when a ecpdu with profiles is sent out. */ void vdp_somethingChangedLocal(struct vsi_profile *profile, bool flag) { LLDPAD_DBG("%s: setting profile->localChange to %s\n", __func__, (flag == true) ? "true" : "false"); profile->localChange = flag; if (flag == true) vdp_start_localchange_timer(profile); } /* vdp_keepaliveTimer_expired - checks for expired ack timer * @profile: profile to be checked * * returns true or false * * returns value of profile->keepaliveTimerExpired, true if ack timer has * expired, * false otherwise. */ static bool vdp_keepaliveTimer_expired(struct vsi_profile *profile) { return (profile->keepaliveTimer == 0); } /* vdp_ackTimer_expired - checks for expired ack timer * @profile: profile to be checked * * returns true or false * * returns value of profile->ackTimerExpired, true if ack timer has expired, * false otherwise. */ static bool vdp_ackTimer_expired(struct vsi_profile *profile) { return (profile->ackTimer == 0); } /* vdp_localchange_handler - triggers in case of vdp_ack or on vdp * localchange * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called from vdp_somethingchangedlocal or vdp_ack_profiles when a change is * pending. Calls the VDP station state machine. This detour is taken * to not having to call the vdp code from the ecp state machine. Instead, we * return to the event loop, giving other code a chance to do work. */ void vdp_localchange_handler(UNUSED void *eloop_data, void *user_ctx) { struct vsi_profile *p; p = (struct vsi_profile *) user_ctx; if ((p->ackReceived) || (p->localChange)) { LLDPAD_DBG("%s: p->localChange %i p->ackReceived %i\n", __func__, p->localChange, p->ackReceived); vdp_vsi_sm_station(p); } } /* * vdp_stop - cancel the VDP localchange timer * * returns 0 on success, -1 on error * * cancels the VPP localchange timer when a profile has been deleted. */ int vdp_stop_localchange_timer(struct vsi_profile *p) { return eloop_cancel_timeout(vdp_localchange_handler, NULL, (void *) p); } /* vdp_start_localchange_timer - starts the VDP localchange timer * @vd: vdp_data for the interface * * returns 0 on success, -1 on error * * starts the VPP localchange timer when a localchange has been signaled from * the VDP state machine. */ int vdp_start_localchange_timer(struct vsi_profile *p) { unsigned int usecs; usecs = VDP_LOCALCHANGE_TIMEOUT; return eloop_register_timeout(0, usecs, vdp_localchange_handler, NULL, (void *) p); } /* vdp_ack_timeout_handler - handles the ack timer expiry * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called when the VDP ack timer for a profile has expired. * Calls the VDP station state machine for the profile. */ void vdp_ack_timeout_handler(UNUSED void *eloop_data, void *user_ctx) { struct vsi_profile *p = (struct vsi_profile *) user_ctx; if (p->ackTimer > 0) p->ackTimer -= VDP_ACK_TIMER_DEFAULT; if (vdp_ackTimer_expired(p)) { LLDPAD_DBG("%s: profile %#02x vdp_ackTimer_expired %i" " p->ackReceived %i\n", __func__, p->instance[15], vdp_ackTimer_expired(p), p->ackReceived); vdp_vsi_sm_station(p); } } /* vdp_start_ack_timer - starts the VDP profile ack timer * @profile: vsi_profile * * returns 0 on success, -1 on error * * starts the VDP profile ack timer when a profile has been handed to ecp for * transmission. */ static int vdp_start_ackTimer(struct vsi_profile *profile) { unsigned int usecs; usecs = VDP_ACK_TIMER_DEFAULT; profile->ackTimer = VDP_ACK_TIMER_DEFAULT; LLDPAD_DBG("%s: %s starting ack timer for %#02x (%i)\n", __func__, profile->port->ifname, profile->instance[15], profile->ackTimer); return eloop_register_timeout(0, usecs, vdp_ack_timeout_handler, NULL, (void *)profile); } /* vdp_stop_ackTimer - stops the VDP profile ack timer * @vd: vdp_data for the interface * * returns the number of removed handlers * * stops the VDP tck imer. Used e.g. when the host interface goes down. */ static int vdp_stop_ackTimer(struct vsi_profile *profile) { LLDPAD_DBG("%s: %s stopping ack timer for %#02x (%i)\n", __func__, profile->port->ifname, profile->instance[15], profile->ackTimer); return eloop_cancel_timeout(vdp_ack_timeout_handler, NULL, (void *)profile); } /* vdp_keepalive_timeout_handler - handles the keepalive timer expiry * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called when the VDP keepalive timer for a profile has expired. * Calls the VDP station state machine for the profile. */ void vdp_keepalive_timeout_handler(UNUSED void *eloop_data, void *user_ctx) { struct vsi_profile *p = (struct vsi_profile *) user_ctx; if (p->keepaliveTimer > 0) p->keepaliveTimer -= VDP_KEEPALIVE_TIMER_DEFAULT; if (vdp_keepaliveTimer_expired(p)) { LLDPAD_DBG("%s: profile %#02x vdp_keepaliveTimer_expired %i" " p->ackReceived %i p->ackReceived %i\n", __func__, p->instance[15], vdp_keepaliveTimer_expired(p), p->ackReceived, p->ackReceived); vdp_vsi_sm_station(p); } } /* vdp_start_keepalive_timer - starts the VDP profile keepalive timer * @vd: vdp_data for the interface * * returns 0 on success, -1 on error * * starts the VDP profile keepalive timer when a profile has been handed to * ecp for transmission. */ static int vdp_start_keepaliveTimer(struct vsi_profile *profile) { unsigned int usecs; usecs = VDP_KEEPALIVE_TIMER_DEFAULT; profile->keepaliveTimer = VDP_KEEPALIVE_TIMER_DEFAULT; LLDPAD_DBG("%s: %s starting keepalive timer for %#02x (%i)\n", __func__, profile->port->ifname, profile->instance[15], profile->keepaliveTimer); return eloop_register_timeout(0, usecs, vdp_keepalive_timeout_handler, NULL, (void *) profile); } /* vdp_stop_keepalive_timer - stops the VDP profile keepalive timer * @vd: vdp_data for the interface * * returns the number of removed handlers * * stops the VDP tck imer. Used e.g. when the host interface goes down. */ static int vdp_stop_keepaliveTimer(struct vsi_profile *profile) { profile->keepaliveTimer = VDP_KEEPALIVE_TIMER_STOPPED; LLDPAD_DBG("%s: %s stopping keepalive timer for %#02x (%i)\n", __func__, profile->port->ifname, profile->instance[15], profile->keepaliveTimer); return eloop_cancel_timeout(vdp_keepalive_timeout_handler, NULL, (void *) profile); } static bool vdp_vsi_negative_response(struct vsi_profile *profile) { if ((profile->response > 0) && (profile->response < 255)) return true; else return false; } /* vdp_vsi_change_station_state - changes the VDP station sm state * @profile: profile to process * @newstate: new state for the sm * * no return value * * actually changes the state of the profile */ void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate) { switch(newstate) { case VSI_UNASSOCIATED: break; case VSI_ASSOC_PROCESSING: assert((profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED) || (profile->state == VSI_UNASSOCIATED)); break; case VSI_ASSOCIATED: assert((profile->state == VSI_ASSOC_PROCESSING) || (profile->state == VSI_ASSOCIATED)); break; case VSI_PREASSOC_PROCESSING: assert((profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED) || (profile->state == VSI_UNASSOCIATED)); break; case VSI_PREASSOCIATED: assert((profile->state == VSI_PREASSOC_PROCESSING) || (profile->state == VSI_PREASSOCIATED)); break; case VSI_DEASSOC_PROCESSING: assert((profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_UNASSOCIATED) || (profile->state == VSI_ASSOCIATED)); break; case VSI_EXIT: assert((profile->state == VSI_ASSOC_PROCESSING) || (profile->state == VSI_PREASSOC_PROCESSING) || (profile->state == VSI_DEASSOC_PROCESSING) || (profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED)); break; default: LLDPAD_ERR("ERROR: The VDP station State Machine is broken\n"); break; } LLDPAD_DBG("%s: %s state change %s -> %s\n", __func__, profile->port->ifname, vsi_states[profile->state], vsi_states[newstate]); profile->state = newstate; } /* vdp_vsi_set_station_state - sets the vdp sm station state * @profile: profile to process * * returns true or false * * switches the state machine to the next state depending on the input * variables. returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool vdp_vsi_set_station_state(struct vsi_profile *profile) { switch(profile->state) { case VSI_UNASSOCIATED: if ((profile->mode == VDP_MODE_PREASSOCIATE) || (profile->mode == VDP_MODE_PREASSOCIATE_WITH_RR)) { vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING); vdp_somethingChangedLocal(profile, true); return true; } else if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING); vdp_somethingChangedLocal(profile, true); return true; } else if (profile->mode == VDP_MODE_DEASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING); vdp_somethingChangedLocal(profile, true); return true; } return false; case VSI_ASSOC_PROCESSING: if (profile->ackReceived) { if (profile->response == 0) vdp_vsi_change_station_state(profile, VSI_ASSOCIATED); else vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) { vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } return false; case VSI_ASSOCIATED: if (profile->mode == VDP_MODE_PREASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_DEASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING); return true; } else if (vdp_vsi_negative_response(profile)) { vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } else if (vdp_keepaliveTimer_expired(profile)) { vdp_stop_keepaliveTimer(profile); vdp_somethingChangedLocal(profile, true); vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING); return true; } return false; case VSI_PREASSOC_PROCESSING: LLDPAD_DBG("%s: profile->ackReceived %i, vdp_ackTimer %i\n", __func__, profile->ackReceived, profile->ackTimer); if (profile->ackReceived) { if (profile->response == 0) vdp_vsi_change_station_state(profile, VSI_PREASSOCIATED); else vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) { vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } return false; case VSI_PREASSOCIATED: if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_DEASSOCIATE) { vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING); return true; } else if (vdp_keepaliveTimer_expired(profile)) { vdp_stop_keepaliveTimer(profile); vdp_somethingChangedLocal(profile, true); vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING); return true; } return false; case VSI_DEASSOC_PROCESSING: if ((profile->ackReceived) || vdp_ackTimer_expired(profile) || profile->remoteChange) { vdp_vsi_change_station_state(profile, VSI_EXIT); return true; } return false; case VSI_EXIT: return false; default: LLDPAD_ERR("%s: VSI state machine in invalid state %d\n", profile->port->ifname, profile->state); return false; } } /* vdp_vsi_sm_station - state machine for vdp station role * @profile: profile for which the state is processed * * no return value * * runs the state machine for the station role of VDP. */ void vdp_vsi_sm_station(struct vsi_profile *profile) { struct vdp_data *vd = vdp_data(profile->port->ifname); int bye = 0; vdp_vsi_set_station_state(profile); do { LLDPAD_DBG("%s: %s station for %#02x - %s\n", __func__, profile->port->ifname, profile->instance[15], vsi_states[profile->state]); switch(profile->state) { case VSI_UNASSOCIATED: break; case VSI_ASSOC_PROCESSING: vdp_stop_keepaliveTimer(profile); profile->response = VDP_RESPONSE_NO_RESPONSE; if (profile->localChange) { ecp_somethingChangedLocal(vd, true); profile->ackReceived = false; vdp_start_ackTimer(profile); } break; case VSI_ASSOCIATED: profile->ackReceived = false; vdp_somethingChangedLocal(profile, false); vdp_stop_ackTimer(profile); vdp_start_keepaliveTimer(profile); break; case VSI_PREASSOC_PROCESSING: vdp_stop_keepaliveTimer(profile); profile->response = VDP_RESPONSE_NO_RESPONSE; if (profile->localChange) { profile->ackReceived = false; ecp_somethingChangedLocal(vd, true); vdp_start_ackTimer(profile); } break; case VSI_PREASSOCIATED: profile->ackReceived = false; vdp_somethingChangedLocal(profile, false); vdp_stop_ackTimer(profile); vdp_start_keepaliveTimer(profile); break; case VSI_DEASSOC_PROCESSING: profile->ackReceived = false; vdp_stop_keepaliveTimer(profile); profile->response = VDP_RESPONSE_NO_RESPONSE; if (profile->localChange) { profile->ackReceived = false; ecp_somethingChangedLocal(vd, true); vdp_start_ackTimer(profile); } break; case VSI_EXIT: if (profile->no_nlmsg && !profile->ackReceived && vdp_ackTimer_expired(profile)) bye = 1; vdp_stop_ackTimer(profile); vdp_stop_keepaliveTimer(profile); vdp_stop_localchange_timer(profile); if (bye) vdp_remove_profile(profile); else vdp_trigger(profile); break; default: LLDPAD_ERR("%s: ERROR VSI state machine in invalid state %d\n", vd->ifname, profile->state); } } while (vdp_vsi_set_station_state(profile) == true); } /* vdp_advance_sm - advance state machine after update from switch * * no return value */ void vdp_advance_sm(struct vdp_data *vd) { struct vsi_profile *p; LIST_FOREACH(p, &vd->profile_head, profile) { LLDPAD_DBG("%s: %s station for %#02x - %s ackReceived %i\n", __func__, p->port->ifname, p->instance[15], vsi_states[p->state], p->ackReceived); if (p->ackReceived) { vdp_vsi_sm_station(p); p->ackReceived = false; } } } /* vdp_vsi_change_bridge_state - changes the VDP bridge sm state * @profile: profile to process * @newstate: new state for the sm * * no return value * * actually changes the state of the profile */ static void vdp_vsi_change_bridge_state(struct vsi_profile *profile, u8 newstate) { switch(newstate) { case VSI_UNASSOCIATED: break; case VSI_ASSOC_PROCESSING: assert((profile->state == VSI_UNASSOCIATED) || (profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED)); break; case VSI_ASSOCIATED: assert(profile->state == VSI_ASSOC_PROCESSING); break; case VSI_PREASSOC_PROCESSING: assert((profile->state == VSI_UNASSOCIATED) || (profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED)); break; case VSI_PREASSOCIATED: assert(profile->state == VSI_PREASSOC_PROCESSING); break; case VSI_DEASSOC_PROCESSING: assert((profile->state == VSI_UNASSOCIATED) || (profile->state == VSI_PREASSOCIATED) || (profile->state == VSI_ASSOCIATED)); break; case VSI_EXIT: assert((profile->state == VSI_DEASSOC_PROCESSING) || (profile->state == VSI_PREASSOC_PROCESSING) || (profile->state == VSI_ASSOC_PROCESSING)); break; default: LLDPAD_ERR("ERROR: The VDP bridge State Machine is broken\n"); break; } profile->state = newstate; } /* vdp_vsi_set_bridge_state - sets the vdp sm bridge state * @profile: profile to process * * returns true or false * * switches the state machine to the next state depending on the input * variables. returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool vdp_vsi_set_bridge_state(struct vsi_profile *profile) { switch(profile->state) { case VSI_UNASSOCIATED: if ((profile->mode == VDP_MODE_DEASSOCIATE)) /* || (INACTIVE)) */ { vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_PREASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); return true; } return false; case VSI_ASSOC_PROCESSING: /* TODO: handle error case if (!vsiError) || (vsiError && vsiState == Assoc) { */ if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_ASSOCIATED); return true; } return false; case VSI_ASSOCIATED: if (profile->mode == VDP_MODE_ASSOCIATE) /* || ( INACTIVE )*/ { vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_PREASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); return true; } return false; case VSI_PREASSOC_PROCESSING: if (profile->response != VDP_RESPONSE_SUCCESS) { vdp_vsi_change_bridge_state(profile, VSI_EXIT); return true; } vdp_vsi_change_bridge_state(profile, VSI_PREASSOCIATED); return false; case VSI_PREASSOCIATED: if (profile->mode == VDP_MODE_ASSOCIATE) { vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_DEASSOCIATE ) { vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING); return true; } else if (profile->mode == VDP_MODE_PREASSOCIATE ) { vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING); return true; } return false; case VSI_DEASSOC_PROCESSING: vdp_vsi_change_bridge_state(profile, VSI_EXIT); return false; case VSI_EXIT: return false; default: LLDPAD_ERR("%s: ERROR VSI state machine (bridge) in invalid state %d\n", profile->port->ifname, profile->state); return false; } } /* vdp_vsi_sm_bridge - state machine for vdp bridge role * @profile: profile for which the state is processed * * no return value * * runs the state machine for the bridge role of VDP. */ static void vdp_vsi_sm_bridge(struct vsi_profile *profile) { struct vdp_data *vd = vdp_data(profile->port->ifname); vdp_vsi_set_bridge_state(profile); do { LLDPAD_DBG("%s: %s bridge - %s\n", __func__, profile->port->ifname, vsi_states[profile->state]); switch(profile->state) { case VSI_UNASSOCIATED: break; case VSI_ASSOC_PROCESSING: /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); * if (vsiError) * txTLV(Assoc NACK) * else * txTLV(Assoc ACK) */ break; case VSI_ASSOCIATED: break; case VSI_PREASSOC_PROCESSING: /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate); * if (vsiError) * txTLV(PreAssoc NACK) * else * txTLV(PreAssoc ACK) */ /* for now, we always succeed */ profile->response = VDP_RESPONSE_SUCCESS; ecp_rx_send_ack_frame(vd); break; case VSI_PREASSOCIATED: LLDPAD_DBG("%s: %s\n", __func__, profile->port->ifname); break; case VSI_DEASSOC_PROCESSING: /* TODO: txTLV(DeAssoc ACK) */ break; case VSI_EXIT: vdp_remove_profile(profile); break; default: LLDPAD_ERR("%s: ERROR VSI state machine in invalid state %d\n", vd->ifname, profile->state); } } while (vdp_vsi_set_bridge_state(profile) == true); } /* * vdp_validate_tlv - validates vsi tlvs * @vdp: decoded vsi tlv * * Returns 0 on success, 1 on error * * checks the contents of an already decoded vsi tlv for inconsistencies */ static int vdp_validate_tlv(struct tlv_info_vdp *vdp, struct unpacked_tlv *tlv) { int pairs = (tlv->length - sizeof *vdp) / sizeof(struct mac_vlan_p); if (ntoh24(vdp->oui) != OUI_IEEE_8021Qbg) { LLDPAD_DBG("vdp->oui %#06x\n", ntoh24(vdp->oui)); goto out_err; } if (vdp->sub != LLDP_VDP_SUBTYPE) { LLDPAD_DBG("vdp->sub %#02x\n", vdp->sub); goto out_err; } if (vdp->mode > VDP_MODE_DEASSOCIATE) { LLDPAD_DBG("unknown mode %#02x in vsi tlv\n", vdp->mode); goto out_err; } if (vdp->response > VDP_RESPONSE_OUT_OF_SYNC) { LLDPAD_DBG("unknown response %#02x\n", vdp->response); goto out_err; } if (vdp->format != VDP_FILTER_INFO_FORMAT_MACVID) { LLDPAD_DBG("unknown format %#02x in vsi tlv\n", vdp->format); goto out_err; } if (ntohs(vdp->entries) < 1) { LLDPAD_DBG("invalid # of entries %#02x in vsi tlv\n", ntohs(vdp->entries)); goto out_err; } /* Check for number of entries of MAC,VLAN pairs */ if (ntohs(vdp->entries) != pairs) { LLDPAD_DBG("mismatching # of entries %#x/%#x in vsi tlv\n", ntohs(vdp->entries), pairs); goto out_err; } return 0; out_err: return 1; } /* * Create a VSI profile structure from switch response. */ static void make_profile(struct vsi_profile *new, struct tlv_info_vdp *vdp, struct unpacked_tlv *tlv) { int i; u8 *pos = tlv->info + sizeof *vdp; new->mode = vdp->mode; new->response = vdp->response; new->mgrid = vdp->mgrid; new->id = ntoh24(vdp->id); new->version = vdp->version; memcpy(&new->instance, &vdp->instance, sizeof new->instance); new->format = vdp->format; new->entries = ntohs(vdp->entries); LLDPAD_DBG("%s: MAC/VLAN filter info format %u, # of entries %u\n", __func__, new->format, new->entries); /* Add MAC,VLAN to list */ for (i = 0; i < new->entries; ++i) { struct mac_vlan *mac_vlan = calloc(1, sizeof(struct mac_vlan)); u16 vlan; char macbuf[MAC_ADDR_STRLEN + 1]; if (!mac_vlan) { new->entries = i; return; } memcpy(&mac_vlan->mac, pos, ETH_ALEN); pos += ETH_ALEN; mac2str(mac_vlan->mac, macbuf, MAC_ADDR_STRLEN); memcpy(&vlan, pos, 2); pos += 2; mac_vlan->vlan = ntohs(vlan); LLDPAD_DBG("%s: mac %s vlan %d\n", __func__, macbuf, mac_vlan->vlan); LIST_INSERT_HEAD(&new->macvid_head, mac_vlan, entry); } } /* * vdp_indicate - receive VSI TLVs from ECP * @port: the port on which the tlv was received * @tlv: the unpacked tlv to receive * * Returns 0 on success * * receives a vsi tlv and creates a profile. Take appropriate action * depending on the role of the (receive) port */ int vdp_indicate(struct vdp_data *vd, struct unpacked_tlv *tlv) { struct tlv_info_vdp vdp; struct vsi_profile *p, *profile; struct port *port = port_find_by_name(vd->ifname); LLDPAD_DBG("%s: indicating vdp of length %u (%zu) for %s\n", __func__, tlv->length, sizeof(struct tlv_info_vdp), vd->ifname); if (!port) { LLDPAD_ERR("%s: port not found for %s\n", __func__, vd->ifname); goto out_err; } memset(&vdp, 0, sizeof vdp); /* copy only vdp header w/o list of mac/vlan/groupid pairs */ memcpy(&vdp, tlv->info, sizeof vdp); if (vdp_validate_tlv(&vdp, tlv)) { LLDPAD_ERR("%s: invalid TLV received\n", __func__); goto out_err; } profile = vdp_alloc_profile(); if (!profile) { LLDPAD_ERR("%s: unable to allocate profile\n", __func__); goto out_err; } make_profile(profile, &vdp, tlv); profile->port = port; if (vd->role == VDP_ROLE_STATION) { /* do we have the profile already ? */ p = vdp_find_profile(vd, profile); if (p) { LLDPAD_DBG("%s: station profile found localChange %i " "ackReceived %i no_nlmsg:%d\n", __func__, p->localChange, p->ackReceived, p->no_nlmsg); if (profile->mode == VDP_MODE_DEASSOCIATE && (p->response == VDP_RESPONSE_NO_RESPONSE || p->response == VDP_RESPONSE_SUCCESS) && p->mode == VDP_MODE_PREASSOCIATE) { LLDPAD_DBG("%s: ignore dis-associate request " "in pre-association\n", __func__); vdp_delete_profile(profile); return 0; } p->ackReceived = true; p->keepaliveTimer = VDP_KEEPALIVE_TIMER_DEFAULT; if (profile->mode != p->mode) { p->mode = profile->mode; p->remoteChange = true; if (profile->mode == VDP_MODE_DEASSOCIATE) p->no_nlmsg = 0; } else p->remoteChange = false; p->response = profile->response; LLDPAD_DBG("%s: remoteChange %i no_nlmsg %d mode %d\n", __func__, p->remoteChange, p->no_nlmsg, p->mode); if (vdp_vsi_negative_response(p)) p->mode = VDP_MODE_DEASSOCIATE; LLDPAD_DBG("%s: profile response: %s (%i) " "for profile %#02x at state %s\n", __func__, vdp_response2str(p->response), p->response, p->instance[15], vsi_states[p->state]); } else { LLDPAD_DBG("%s: station profile not found\n", __func__); } vdp_delete_profile(profile); } if (vd->role == VDP_ROLE_BRIDGE) { /* do we have the profile already ? */ p = vdp_find_profile(vd, profile); if (p) { LLDPAD_DBG("%s: bridge profile found\n", __func__); vdp_delete_profile(profile); } else { LLDPAD_DBG("%s: bridge profile not found\n", __func__); /* put it in the list */ profile->state = VSI_UNASSOCIATED; LIST_INSERT_HEAD(&vd->profile_head, profile, profile); } vdp_vsi_sm_bridge(profile); } return 0; out_err: return 1; } /* * vdp_bld_vsi_tlv - build the VDP VSI TLV * @vd: vdp_data structure for this port * @profile: profile the vsi tlv is created from * * Returns 0 on success, ENOMEM otherwise * * creates a vdp structure from an existing profile */ static int vdp_bld_vsi_tlv(struct vdp_data *vd, struct vsi_profile *profile) { struct mac_vlan *mv; struct mac_vlan_p *mv_p; struct tlv_info_vdp *vdp; int rc = 0; struct unpacked_tlv *tlv = NULL; int size = sizeof(struct tlv_info_vdp) + profile->entries * sizeof(struct mac_vlan_p); vdp = malloc(size); if (!vdp) { LLDPAD_DBG("%s: unable to allocate memory for VDP TLV\n", __func__); rc = ENOMEM; goto out_err; } memset(vdp, 0, size); hton24(vdp->oui, OUI_IEEE_8021Qbg); vdp->sub = LLDP_VDP_SUBTYPE; vdp->mode = profile->mode; vdp->response = 0; vdp->mgrid = profile->mgrid; hton24(vdp->id, profile->id); vdp->version = profile->version; memcpy(&vdp->instance, &profile->instance, 16); vdp->format = VDP_FILTER_INFO_FORMAT_MACVID; vdp->entries = htons(profile->entries); mv_p = (struct mac_vlan_p *)(vdp + 1); LIST_FOREACH(mv, &profile->macvid_head, entry) { memcpy(mv_p->mac, mv->mac, MAC_ADDR_LEN); mv_p->vlan = htons(mv->vlan); mv_p++; } tlv = create_tlv(); if (!tlv) { rc = ENOMEM; goto out_free; } tlv->type = ORG_SPECIFIC_TLV; tlv->length = size; tlv->info = (u8 *)malloc(tlv->length); if(!tlv->info) { free(tlv); tlv = NULL; rc = ENOMEM; goto out_free; } FREE_UNPKD_TLV(vd, vdp); memcpy(tlv->info, vdp, tlv->length); vd->vdp = tlv; out_free: free(vdp); out_err: return rc; } /* vdp_bld_tlv - builds a tlv from a profile * @vd: vdp_data structure for this port * @profile: profile the vsi tlv is created from * * returns 0 on success, != 0 on error * * wrapper function around vdp_bld_vsi_tlv. adds some checks and calls * vdp_bld_vsi_tlv. */ static int vdp_bld_tlv(struct vdp_data *vd, struct vsi_profile *profile) { int rc = 0; if (!port_find_by_name(vd->ifname)) { rc = EEXIST; goto out_err; } if (vdp_bld_vsi_tlv(vd, profile)) { LLDPAD_ERR("%s: %s vdp_bld_vsi_tlv() failed\n", __func__, vd->ifname); rc = EINVAL; goto out_err; } out_err: return rc; } /* vdp_gettlv - get the tlv for a profile * @port: the port on which the tlv was received * @profile: profile the vsi tlv is created from * * returns 0 on success * * this is the interface function called from ecp_build_ECPDU. It returns the * packed tlv for a profile. */ struct packed_tlv *vdp_gettlv(struct vdp_data *vd, struct vsi_profile *profile) { int size; struct packed_tlv *ptlv = NULL; /* frees the unpacked_tlv in vdp_data * also done in vdp_bld_vsi_tlv */ vdp_free_tlv(vd); if (vdp_bld_tlv(vd, profile)) { LLDPAD_ERR("%s: %s vdp_bld_tlv failed\n", __func__, vd->ifname); goto out_err; } size = TLVSIZE(vd->vdp); if (!size) { LLDPAD_ERR("%s: size %i of unpacked_tlv not correct\n", __func__, size); goto out_err; } ptlv = create_ptlv(); if (!ptlv) goto out_err; ptlv->tlv = malloc(size); if (!ptlv->tlv) goto out_free; ptlv->size = 0; PACK_TLV_AFTER(vd->vdp, ptlv, size, out_free); return ptlv; out_free: ptlv = free_pkd_tlv(ptlv); out_err: LLDPAD_ERR("%s: %s failed\n", __func__, vd->ifname); return NULL; } /* vdp_macvlan_equal - checks for equality of 2 mac/vlan pairs * @mv1: mac/vlan pair 1 * @mv2: mac/vlan pair 2 * * returns true if equal, false if not * * compares mac address and vlan if they are equal. */ bool vdp_macvlan_equal(struct mac_vlan *mv1, struct mac_vlan *mv2) { if (memcmp(mv1->mac, mv2->mac, MAC_ADDR_LEN)) return false; if (mv1->vlan != mv2->vlan) return false; return true; } /* * Check if the current profile already has this entry. If so take over * PID and other fields. If not add this MAC,VLAN to our list. * * Returns 1 it the entry already exist, 0 if not. */ static int have_macvlan(struct vsi_profile *p1, struct mac_vlan *new) { struct mac_vlan *mv1; LIST_FOREACH(mv1, &p1->macvid_head, entry) if (vdp_macvlan_equal(mv1, new) == true) { mv1->req_pid = new->req_pid; mv1->req_seq = new->req_seq; mv1->qos = new->qos; return 1; } LIST_INSERT_HEAD(&p1->macvid_head, new, entry); p1->entries++; return 0; } /* vdp_takeover_macvlans - take over macvlan pairs from p2 into p1 * @p1: profile 1 * @p2: profile 2 * * returns number of mac/vlan pairs taken over * * loops over all mac/vlan pairs in profile 2 and looks for them in profile 1. * If the mac/vlan pair does not yet exist in profile 1, it adds the new pair to * the list in profile 1. */ void vdp_takeover_macvlans(struct vsi_profile *p1, struct vsi_profile *p2) { struct mac_vlan *mv2; int count = 0; LLDPAD_DBG("%s: taking over mac/vlan pairs\n", __func__); while ((mv2 = LIST_FIRST(&p2->macvid_head))) { LIST_REMOVE(mv2, entry); p2->entries--; if (have_macvlan(p1, mv2)) free(mv2); else count++; } LLDPAD_DBG("%s: %u mac/vlan pairs taken over\n", __func__, count); } /* vdp_add_profile - adds a profile to a per port list * @profile: profile to add * * returns the profile that has been found or added, NULL otherwise. * * main interface function which adds a profile to a list kept on a per-port * basis. Checks if the profile is already in the list, adds it if necessary. */ struct vsi_profile *vdp_add_profile(struct vdp_data *vd, struct vsi_profile *profile) { struct vsi_profile *p; LLDPAD_DBG("%s: adding vdp profile for %s\n", __func__, profile->port->ifname); vdp_trace_profile(profile); /* * Search this profile. If found check, * if the MAC/VLAN pair already exists. If not, add it. */ p = vdp_find_profile(vd, profile); if (p) { LLDPAD_DBG("%s: profile already exists\n", __func__); vdp_takeover_macvlans(p, profile); if (p->mode != profile->mode) { LLDPAD_DBG("%s: new mode %i\n", __func__, profile->mode); p->mode = profile->mode; p->response = VDP_RESPONSE_NO_RESPONSE; } profile = p; } else { profile->response = VDP_RESPONSE_NO_RESPONSE; LIST_INSERT_HEAD(&vd->profile_head, profile, profile); } vdp_somethingChangedLocal(profile, true); return profile; } /* vdp_remove_profile - remove a profile from a per port list * @profile: profile to remove * * returns 0 if removal was successful, -1 if removal failed * * function used in the state machines to remove a profile from a list kept on * a per-port basis. Checks if the profile is in the list, removes it if there. */ int vdp_remove_profile(struct vsi_profile *profile) { struct vsi_profile *p; struct vdp_data *vd; LLDPAD_DBG("%s: removing vdp profile on %s\n", __func__, profile->port->ifname); vdp_trace_profile(profile); vd = vdp_data(profile->port->ifname); if (!vd) { LLDPAD_ERR("%s: could not find vdp_data for %s\n", __func__, profile->port->ifname); return -1; } /* Check if profile exists. If yes, remove it. */ p = vdp_find_profile(vd, profile); if (p) { LIST_REMOVE(p, profile); vdp_delete_profile(p); return 0; } return -1; /* Not found */ } /* vdp_ifdown - tear down vdp structures for a interface * @ifname: name of the interface * * no return value * * interface function to lldpad. tears down vdp specific structures if * interface "ifname" goes down. */ void vdp_ifdown(char *ifname, UNUSED struct lldp_agent *agent) { struct vdp_data *vd; struct vsi_profile *p; LLDPAD_DBG("%s: called on interface %s\n", __func__, ifname); vd = vdp_data(ifname); if (!vd) goto out_err; if (ecp_deinit(ifname)) goto out_err; LIST_FOREACH(p, &vd->profile_head, profile) { if (p->ackTimer > 0) vdp_stop_ackTimer(p); if (p->keepaliveTimer > 0) vdp_stop_keepaliveTimer(p); } LLDPAD_INFO("%s: %s vdp data removed\n", __func__, ifname); return; out_err: LLDPAD_INFO("%s: %s vdp data remove failed\n", __func__, ifname); return; } /* vdp_ifup - build up vdp structures for a interface * @ifname: name of the interface * * no return value * * interface function to lldpad. builds up vdp specific structures if * interface "ifname" goes up. */ void vdp_ifup(char *ifname, struct lldp_agent *agent) { char *role; char config_path[16]; struct vdp_data *vd; struct vdp_user_data *ud; struct vsi_profile *p; int enabletx = false; LLDPAD_DBG("%s: %s agent:%d start VDP\n", __func__, ifname, agent->type); snprintf(config_path, sizeof(config_path), "%s.%s", VDP_PREFIX, ARG_TLVTXENABLE); if (get_config_setting(ifname, agent->type, config_path, (void *)&enabletx, CONFIG_TYPE_BOOL)) enabletx = false; if (enabletx == false) { LLDPAD_DBG("%s: %s not enabled for VDP\n", __func__, ifname); return; } vd = vdp_data(ifname); if (vd) { vd->enabletx = enabletx; LLDPAD_WARN("%s: %s vdp data already exists\n", __func__, ifname); goto out_start_again; } /* not found, alloc/init per-port module data */ vd = (struct vdp_data *) calloc(1, sizeof(struct vdp_data)); if (!vd) { LLDPAD_ERR("%s: %s malloc %zu failed\n", __func__, ifname, sizeof(*vd)); goto out_err; } strncpy(vd->ifname, ifname, IFNAMSIZ); vd->role = VDP_ROLE_STATION; vd->enabletx = enabletx; if (!get_cfg(ifname, NEAREST_BRIDGE, "vdp.role", (void *)&role, CONFIG_TYPE_STRING)) { if (!strcasecmp(role, VAL_BRIDGE)) { vd->role = VDP_ROLE_BRIDGE; } } LLDPAD_DBG("%s: configured for %s mode\n", ifname, (vd->role ==VDP_ROLE_BRIDGE) ? "bridge" : "station"); LIST_INIT(&vd->profile_head); ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP02); LIST_INSERT_HEAD(&ud->head, vd, entry); out_start_again: if (ecp_init(ifname)) { LLDPAD_ERR("%s: %s unable to init ecp\n", __func__, ifname); vdp_ifdown(ifname, agent); goto out_err; } vd->keepaliveTimer = VDP_KEEPALIVE_TIMER_DEFAULT; vd->ackTimer = VDP_ACK_TIMER_DEFAULT; LLDPAD_DBG("%s: %s starting vdp timer (%i)\n", __func__, vd->ifname, vd->nroftimers); LIST_FOREACH(p, &vd->profile_head, profile) { if (p->ackTimer > 0) { vdp_somethingChangedLocal(p, true); vdp_start_ackTimer(p); } if (p->keepaliveTimer > 0) vdp_start_keepaliveTimer(p); } LLDPAD_DBG("%s: %s agent:%d vdp added\n", __func__, ifname, agent->type); return; out_err: LLDPAD_ERR("%s: %s agent:%d vdp adding failed\n", __func__, ifname, agent->type); } static int vdp_client_cmd(UNUSED void *data, UNUSED struct sockaddr_un *from, UNUSED socklen_t fromlen, char *ibuf, int ilen, char *rbuf, int rlen) { return vdp_clif_cmd(ibuf, ilen, rbuf, rlen); } static const struct lldp_mod_ops vdp_ops = { .lldp_mod_register = vdp_register, .lldp_mod_unregister = vdp_unregister, .get_arg_handler = vdp_get_arg_handlers, .client_cmd = vdp_client_cmd }; /* vdp_register - register vdp module to lldpad * @none * * returns lldp_module struct on success, NULL on error * * allocates a module structure with vdp module information and returns it * to lldpad. */ struct lldp_module *vdp_register(void) { struct lldp_module *mod; struct vdp_user_data *ud; mod = malloc(sizeof(*mod)); if (!mod) { LLDPAD_ERR("%s: failed to start - vdp data\n", __func__); return NULL; } ud = malloc(sizeof(struct vdp_user_data)); if (!ud) { free(mod); LLDPAD_ERR("%s: failed to start - vdp user data\n", __func__); return NULL; } LIST_INIT(&ud->head); mod->id = LLDP_MOD_VDP02; mod->ops = &vdp_ops; mod->data = ud; LLDPAD_DBG("%s: done\n", __func__); return mod; } /* vdp_unregister - unregister vdp module from lldpad * @none * * no return value * * frees vdp module structure. */ void vdp_unregister(struct lldp_module *mod) { if (mod->data) { vdp_free_data((struct vdp_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s: done\n", __func__); } void vdp_update(char *ifname, u8 ccap) { struct vdp_data *vdp = vdp_data(ifname); if (vdp) { vdp->vdpbit_on = ccap & LLDP_EVB_CAPABILITY_PROTOCOL_VDP; LLDPAD_DBG("%s:%s vdpbit_on %d\n", __func__, ifname, vdp->vdpbit_on); } } /* * Handle a VSI request from buddy. */ int vdp_request(struct vdpnl_vsi *vsi) { struct vdp_data *vd; struct vsi_profile *profile, *p; struct port *port = port_find_by_name(vsi->ifname); struct mac_vlan *mac_vlan; int ret = 0; vd = vdp_data(vsi->ifname); if (!vd) { LLDPAD_ERR("%s: %s has not yet been configured\n", __func__, vsi->ifname); return -ENXIO; } if (!vd->vdpbit_on) { LLDPAD_ERR("%s: %s has VDP disabled\n", __func__, vsi->ifname); return -ENXIO; } if (!port) { LLDPAD_ERR("%s: %s can not find port\n", __func__, vsi->ifname); return -ENODEV; } /* If the link is down, reject request */ if (!port->portEnabled && vsi->request != VDP_MODE_DEASSOCIATE) { LLDPAD_WARN("%s: %s not enabled, unable to associate\n", __func__, vsi->ifname); return -ENXIO; } profile = vdp_alloc_profile(); if (!profile) return -ENOMEM; mac_vlan = calloc(1, sizeof(struct mac_vlan)); if (!mac_vlan) { ret = -ENOMEM; goto out_err; } profile->port = port; memcpy(&mac_vlan->mac, vsi->maclist->mac, sizeof mac_vlan->mac); mac_vlan->vlan = vsi->maclist->vlan; mac_vlan->qos = vsi->maclist->qos; mac_vlan->req_pid = vsi->req_pid; mac_vlan->req_seq = vsi->req_seq; LIST_INSERT_HEAD(&profile->macvid_head, mac_vlan, entry); profile->entries = 1; profile->mgrid = vsi->vsi_mgrid; profile->id = vsi->vsi_typeid; profile->version = vsi->vsi_typeversion; profile->mode = vsi->request; profile->response = vsi->response; memcpy(profile->instance, vsi->vsi_uuid, sizeof vsi->vsi_uuid); p = vdp_add_profile(vd, profile); p->no_nlmsg = 1; p->txmit = false; vdp_trace_profile(p); if (p != profile) goto out_err; return ret; out_err: vdp_delete_profile(profile); return ret; } /* * Query a VSI request from buddy and report its progress. Use the interface * name to determine the VSI profile list. Return one entry in parameter 'vsi' * use the structure members response and vsi_uuid. * Returns * 1 valid VSI data returned * 0 end of queue (no VSI data returned) * <0 errno */ int vdp_status(int number, struct vdpnl_vsi *vsi) { struct vdp_data *vd; struct vsi_profile *p; int i = 0, ret = 0; vd = vdp_data(vsi->ifname); if (!vd) { LLDPAD_ERR("%s: %s has not yet been configured\n", __func__, vsi->ifname); return -ENODEV; } /* Interate to queue element number */ LIST_FOREACH(p, &vd->profile_head, profile) { if (++i == number) { ret = 1; break; } } if (ret) { vdp_trace_profile(p); vsi->response = p->response; memcpy(vsi->vsi_uuid, p->instance, sizeof vsi->vsi_uuid); if (p->response != VDP_RESPONSE_NO_RESPONSE && p->state == VSI_EXIT) vdp_remove_profile(p); } LLDPAD_DBG("%s: entry:%d more:%d\n", __func__, number, ret); return ret; } /* * Copy MAC-VLAN list from profile to vdpnl structure. */ static void copy_maclist(struct vsi_profile *p, struct vdpnl_mac *macp) { struct mac_vlan *mv1; LIST_FOREACH(mv1, &p->macvid_head, entry) { macp->vlan = mv1->vlan; macp->qos = mv1->qos; memcpy(macp->mac, mv1->mac, sizeof macp->mac); ++macp; } } /* * Prepare data for a netlink message to originator of VSI. * Forward a notification from switch. */ int vdp_trigger(struct vsi_profile *profile) { struct vdpnl_vsi vsi; struct vdp_data *vd; struct mac_vlan *macp = 0; int rc = -EINVAL; struct vdpnl_mac maclist[profile->entries]; vsi.macsz = profile->entries; vsi.maclist = maclist; LLDPAD_DBG("%s: no_nlmsg:%d\n", __func__, profile->no_nlmsg); vdp_trace_profile(profile); if (profile->no_nlmsg) return 0; if (LIST_EMPTY(&profile->macvid_head)) return 0; macp = LIST_FIRST(&profile->macvid_head); if (!macp->req_pid) return 0; sleep(1); /* Delay message notification */ if (!profile->port || !profile->port->ifname) { LLDPAD_ERR("%s: no ifname found for profile %p:\n", __func__, profile); goto error_exit; } memcpy(vsi.ifname, profile->port->ifname, sizeof vsi.ifname); vd = vdp_data(vsi.ifname); if (!vd) { LLDPAD_ERR("%s: %s could not find vdp_data\n", __func__, vsi.ifname); goto error_exit; } vsi.ifindex = if_nametoindex(vsi.ifname); if (vsi.ifindex == 0) { LLDPAD_ERR("%s: %s could not find index for ifname\n", __func__, vsi.ifname); goto error_exit; } vsi.macsz = profile->entries; copy_maclist(profile, vsi.maclist); vsi.req_pid = macp->req_pid; vsi.req_seq = macp->req_seq; vsi.vsi_mgrid = profile->mgrid; vsi.vsi_typeid = profile->id; vsi.vsi_typeversion = profile->version; memcpy(vsi.vsi_uuid, profile->instance, sizeof vsi.vsi_uuid); vsi.request = VDP_MODE_DEASSOCIATE; rc = vdpnl_send(&vsi); error_exit: vdp_remove_profile(profile); return rc; } lldpad-0.9.46/lldp_vdp_clif.c000066400000000000000000000131511215142747300160260ustar00rootroot00000000000000/******************************************************************************* Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". *******************************************************************************/ #include #include #include #include #include #include "lldp_mod.h" #include "clif_msgs.h" #include "lldp.h" #include "lldp_vdp.h" #include "lldp_vdp_cmds.h" #include "lldp_vdp_clif.h" #include "lldp_mand_clif.h" static const char *mode_state(int mode) { switch (mode) { case VDP_MODE_PREASSOCIATE_WITH_RR: return "Preassociated with RR"; case VDP_MODE_DEASSOCIATE: return "Disassociated"; case VDP_MODE_ASSOCIATE: return "Associated"; case VDP_MODE_PREASSOCIATE: return "Preassociated"; default: return "unknown"; } } /* * Print a complete VDP TLV. Data string constructed in function * vdp_clif_profile(). */ static void vdp_show_tlv(UNUSED u16 len, char *info) { int rc, role, enabletx, vdpbit, mode, response, mgrid, id, idver; unsigned int x[16]; rc = sscanf(info, "%02x%02x%02x%02x%02x%02x%06x%02x", &role, &enabletx, &vdpbit, &mode, &response, &mgrid, &id, &idver); if (rc != 3 && rc != 8) return; printf("Role:%s\n", role ? VAL_BRIDGE : VAL_STATION); printf("\tEnabled:%s\n", enabletx ? VAL_YES : VAL_NO); printf("\tVDP Bit:%s\n", vdpbit ? VAL_YES : VAL_NO); if (rc == 3) /* No active VSI profile */ return; printf("\tMode:%d (%s)\n", mode, mode_state(mode)); printf("\tMgrid:%d\n", mgrid); printf("\tTypeid:%d\n", id); printf("\tTypeidversion:%d\n", idver); rc = sscanf(info + 20, "%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]); if (rc != 16) return; printf("\tUUID:%02x%02x%02x%02x-%02x%02x-%02x%02x" "-%02x%02x-%02x%02x%02x%02x%02x%02x\n", x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]); mode = 52; rc = sscanf(info + mode, "%02x%04x", &role, &vdpbit); if (rc != 2) return; printf("\tFilter Format:%d\n", role); printf("\tEntries:%d\n", vdpbit); mode += 6; while (--vdpbit >= 0) { rc = sscanf(info + mode, "%02x%02x%02x%02x%02x%02x%04x", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6]); if (rc != 7) return; printf("\t\tMAC:%02x:%02x:%02x:%02x:%02x:%02x\tVlanid:%d\n", x[0], x[1], x[2], x[3], x[4], x[5], x[6]); mode += 16; } } static struct type_name_info vdp_tlv_names[] = { { .type = ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE, .name = "VDP draft 0.2 protocol configuration", .key = "vdp", .print_info = vdp_show_tlv }, { .type = INVALID_TLVID } }; static int vdp_print_help() { struct type_name_info *tn = &vdp_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } static u32 vdp_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &vdp_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } static void vdp_cli_unregister(struct lldp_module *mod) { free(mod); } /* return 1: if it printed the TLV * 0: if it did not */ static int vdp_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &vdp_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } static const struct lldp_mod_ops vdp_ops_clif = { .lldp_mod_register = vdp_cli_register, .lldp_mod_unregister = vdp_cli_unregister, .print_tlv = vdp_print_tlv, .lookup_tlv_name = vdp_lookup_tlv_name, .print_help = vdp_print_help, }; struct lldp_module *vdp_cli_register(void) { struct lldp_module *mod; mod = malloc(sizeof(*mod)); if (!mod) { fprintf(stderr, "failed to malloc module data\n"); return NULL; } mod->id = (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE; mod->ops = &vdp_ops_clif; return mod; } lldpad-0.9.46/lldp_vdp_cmds.c000066400000000000000000000362721215142747300160500ustar00rootroot00000000000000/****************************************************************************** Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include #include #include #include #include "lldpad.h" #include "ctrl_iface.h" #include "lldp.h" #include "lldp_vdp.h" #include "lldp_mand_clif.h" #include "lldp_vdp_cmds.h" #include "lldp_qbg_utils.h" #include "lldp/ports.h" #include "lldp_tlv.h" #include "messages.h" #include "libconfig.h" #include "config.h" #include "clif_msgs.h" #include "lldpad_status.h" #include "lldp/states.h" static char *check_and_update(size_t *total, size_t *length, char *s, int c) { if (c < 0) return NULL; *total += c; if ((unsigned)c >= *length) return NULL; *length -= c; return s + c; } static char *print_mode(char *s, size_t length, struct vsi_profile *p) { int c; size_t total = 0; char *r = s; struct mac_vlan *mac_vlan; char instance[VDP_UUID_STRLEN + 2]; vdp_uuid2str(p->instance, instance, sizeof(instance)); c = snprintf(s, length, "%d,%d,%d,%d,%s,%d", p->state, p->mgrid, p->id, p->version, instance, p->format); s = check_and_update(&total, &length, s, c); if (!s) return r; LIST_FOREACH(mac_vlan, &p->macvid_head, entry) { char macbuf[MAC_ADDR_STRLEN + 1]; mac2str(mac_vlan->mac, macbuf, MAC_ADDR_STRLEN); c = snprintf(s, length, ",%s,%d", macbuf, mac_vlan->vlan); s = check_and_update(&total, &length, s, c); if (!s) return r; } return s; } static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { int value; char *s; char arg_path[VDP_BUF_SIZE]; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg); if (get_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)) value = false; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (value) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02zx%s%04zx%s", strlen(arg), arg, strlen(s), s); return cmd_success; } static int _set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, bool test) { int value, err; char arg_path[VDP_BUF_SIZE]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg); err = set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL); if (err) return cmd_failed; return cmd_success; } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, false); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_tlvtxenable(cmd, arg, argvalue, true); } static int get_arg_mode(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { struct vsi_profile *np; struct vdp_data *vd; char mode_str[VDP_BUF_SIZE], *t = mode_str; int filled = 0; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } vd = vdp_data(cmd->ifname); if (!vd) { LLDPAD_ERR("%s: vdp_data for %s not found !\n", __func__, cmd->ifname); return cmd_device_not_found; } memset(mode_str, 0, sizeof mode_str); LIST_FOREACH(np, &vd->profile_head, profile) { t = print_mode(t, sizeof(mode_str) - filled, np); filled = t - mode_str; } snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(mode_str), mode_str); return cmd_success; } static void str2instance(struct vsi_profile *profile, char *buffer) { unsigned int i, j = 0; for (i = 0; i <= strlen(buffer); i++) { if (buffer[i] == '-') continue; if (sscanf(&buffer[i], "%02hhx", &profile->instance[j]) == 1) { i++; j++; } } } static void vdp_fill_profile(struct vsi_profile *profile, char *buffer, int field) { LLDPAD_DBG("%s: parsed %s\n", __func__, buffer); switch(field) { case MODE: profile->mode = atoi(buffer); break; case MGRID: profile->mgrid = atoi(buffer); break; case TYPEID: profile->id = atoi(buffer); break; case TYPEIDVERSION: profile->version = atoi(buffer); break; case INSTANCEID: str2instance(profile, buffer); break; case FORMAT: profile->format = atoi(buffer); break; default: LLDPAD_ERR("Unknown field in buffer !\n"); break; } } static struct vsi_profile *vdp_parse_mode_line(char *argvalue) { int field; char *cmdstring, *parsed; struct vsi_profile *profile; profile = vdp_alloc_profile(); if (!profile) return NULL; cmdstring = strdup(argvalue); if (!cmdstring) goto out_free; field = 0; parsed = strtok(cmdstring, ","); while (parsed != NULL) { vdp_fill_profile(profile, parsed, field); field++; if (field > FORMAT) break; parsed = strtok(NULL, ","); } if ((field <= FORMAT) || (parsed == NULL)) goto out_free; parsed = strtok(NULL, ","); while (parsed != NULL) { struct mac_vlan *mac_vlan; mac_vlan = calloc(1, sizeof(struct mac_vlan)); if (mac_vlan == NULL) goto out_free; if (str2mac(parsed, &mac_vlan->mac[0], MAC_ADDR_LEN)) { free(mac_vlan); goto out_free; } parsed = strtok(NULL, ","); if (parsed == NULL) { free(mac_vlan); goto out_free; } mac_vlan->vlan = atoi(parsed); LIST_INSERT_HEAD(&profile->macvid_head, mac_vlan, entry); profile->entries++; parsed = strtok(NULL, ","); } free(cmdstring); return profile; out_free: free(cmdstring); vdp_delete_profile(profile); return NULL; } static int _set_arg_mode(struct cmd *cmd, char *argvalue, bool test) { struct vsi_profile *profile, *p; struct vdp_data *vd; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } profile = vdp_parse_mode_line(argvalue); if (profile == NULL) return cmd_failed; profile->port = port_find_by_name(cmd->ifname); if (!profile->port) { vdp_delete_profile(profile); return cmd_device_not_found; } vd = vdp_data(cmd->ifname); if (!vd) { vdp_delete_profile(profile); return cmd_device_not_found; } if (test) { vdp_delete_profile(profile); return cmd_success; } p = vdp_add_profile(vd, profile); if (profile != p) vdp_delete_profile(profile); return cmd_success; } static int set_arg_mode(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_mode(cmd, argvalue, false); } static int test_arg_mode(struct cmd *cmd, UNUSED char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_mode(cmd, argvalue, true); } static int get_arg_role(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { struct vdp_data *vd; if (cmd->cmd != cmd_gettlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: vd = vdp_data(cmd->ifname); if (!vd) { LLDPAD_ERR("%s: could not find vdp_data for %s\n", __FILE__, cmd->ifname); return cmd_device_not_found; } if (vd->role == VDP_ROLE_STATION) snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, (unsigned int) strlen(VAL_STATION), VAL_STATION); else if (vd->role == VDP_ROLE_BRIDGE) snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int) strlen(arg), arg, (unsigned int) strlen(VAL_BRIDGE), VAL_BRIDGE); else return cmd_failed; break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } return cmd_success; } static int _set_arg_role(struct cmd *cmd, char *arg, char *argvalue, bool test) { struct vdp_data *vd; char arg_path[VDP_BUF_SIZE]; if (cmd->cmd != cmd_settlv) return cmd_invalid; switch (cmd->tlvid) { case ((LLDP_MOD_VDP) << 8) | LLDP_VDP_SUBTYPE: break; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } vd = vdp_data(cmd->ifname); if (!vd) { LLDPAD_ERR("%s: could not find vdp_data for %s\n", __FILE__, cmd->ifname); return cmd_device_not_found; } if (!strcasecmp(argvalue, VAL_BRIDGE)) { if (!test) vd->role = VDP_ROLE_BRIDGE; } else if (!strcasecmp(argvalue, VAL_STATION)) { if (!test) vd->role = VDP_ROLE_STATION; } else { return cmd_invalid; } if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s.%s", VDP_PREFIX, arg); const char *p = &argvalue[0]; if (set_cfg(cmd->ifname, cmd->type, arg_path, &p, CONFIG_TYPE_STRING)) return cmd_failed; return cmd_success; } static int set_arg_role(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_role(cmd, arg, argvalue, false); } static int test_arg_role(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return _set_arg_role(cmd, arg, argvalue, true); } static struct arg_handlers arg_handlers[] = { { .arg = ARG_VDP_MODE, .arg_class = TLV_ARG, .handle_get = get_arg_mode, .handle_set = set_arg_mode, .handle_test = test_arg_mode }, { .arg = ARG_VDP_ROLE, .arg_class = TLV_ARG, .handle_get = get_arg_role, .handle_set = set_arg_role, .handle_test = test_arg_role }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable }, { .arg = 0 } }; struct arg_handlers *vdp_get_arg_handlers() { return &arg_handlers[0]; } /* * Interface to build information for lldptool -V vdp */ struct tlv_info_vdp_nopp { /* VSI information without profile data */ u8 oui[3]; /* OUI */ u8 sub; /* Subtype */ u8 role; /* Role: station or bridge */ u8 enabletx; u8 vdpbit_on; } __attribute__ ((__packed__)); /* * Flatten a profile stored as TLV and append it. Skip the first 4 bytes. * They contain the OUI already stored. * Returns the number of bytes added to the buffer. */ static int add_profile(unsigned char *pdu, size_t pdusz, struct vdp_data *vdp) { size_t size = 0; if (!vdp->vdp) return size; size = (unsigned)TLVSIZE(vdp->vdp) - 4; if (pdusz >= size) memcpy(pdu, vdp->vdp->info + 4, size); else { LLDPAD_ERR("%s: %s buffer size too small (need %d bytes)\n", __func__, vdp->ifname, TLVSIZE(vdp->vdp)); return -1; } return size; } /* * Create unpacked VDP tlv for VSI profile when active. */ static int make_vdp_tlv(unsigned char *pdu, size_t pdusz, struct vdp_data *vdp) { struct unpacked_tlv *tlv = (struct unpacked_tlv *)pdu; struct tlv_info_vdp_nopp *vdpno; size_t pduoff; int rc; tlv->info = (unsigned char *)(tlv + 1); vdpno = (struct tlv_info_vdp_nopp *)tlv->info; tlv->type = ORG_SPECIFIC_TLV; tlv->length = sizeof(struct tlv_info_vdp_nopp); hton24(vdpno->oui, LLDP_MOD_VDP); vdpno->sub = LLDP_VDP_SUBTYPE; vdpno->role = vdp->role; vdpno->enabletx = vdp->enabletx; vdpno->vdpbit_on = vdp->vdpbit_on; pduoff = sizeof(*tlv) + tlv->length; pdusz -= pduoff; rc = add_profile(pdu + pduoff, pdusz - pduoff, vdp); if (rc > 0) { tlv->length += rc; rc = 0; } return rc; } /* * Flatten a VDP TLV into a byte stream. */ static int vdp_clif_profile(char *ifname, char *rbuf, size_t rlen) { unsigned char pdu[VDP_BUF_SIZE]; /* Buffer for unpacked TLV */ int i, c, rstatus = cmd_success; size_t sum = 0; struct vdp_data *vd; struct unpacked_tlv *tlv = (struct unpacked_tlv *)pdu; struct packed_tlv *ptlv; LLDPAD_DBG("%s: %s rlen:%zu\n", __func__, ifname, rlen); vd = vdp_data(ifname); if (!vd) return cmd_device_not_found; if (make_vdp_tlv(pdu, sizeof pdu, vd)) return cmd_failed; /* Convert to packed TLV */ ptlv = pack_tlv(tlv); if (!ptlv) return cmd_failed; for (i = 0; i < TLVSIZE(tlv); ++i) { c = snprintf(rbuf, rlen, "%02x", ptlv->tlv[i]); rbuf = check_and_update(&sum, &rlen, rbuf, c); if (!rbuf) { rstatus = cmd_failed; break; } } free_pkd_tlv(ptlv); return rstatus; } /* * Module function to extract all VSI profile data on a given interface. It * is invoked via 'lldptool -t -i ethx -g ncb -V vdp' without any configuration * options. * This function does not support arguments and its values. They are handled * using the lldp_mand_cmds.c interfaces. */ int vdp_clif_cmd(char *ibuf, UNUSED int ilen, char *rbuf, int rlen) { struct cmd cmd; u8 len, version; int c, ioff; size_t roff = 0, outlen = rlen; char *here; int rstatus = cmd_invalid; /* Pull out the command elements of the command message */ hexstr2bin(ibuf + MSG_VER, (u8 *)&version, sizeof(u8)); version >>= 4; hexstr2bin(ibuf + CMD_CODE, (u8 *)&cmd.cmd, sizeof(cmd.cmd)); hexstr2bin(ibuf + CMD_OPS, (u8 *)&cmd.ops, sizeof(cmd.ops)); cmd.ops = ntohl(cmd.ops); hexstr2bin(ibuf + CMD_IF_LEN, &len, sizeof(len)); ioff = CMD_IF; if (len < sizeof(cmd.ifname)) memcpy(cmd.ifname, ibuf + CMD_IF, len); else return cmd_failed; cmd.ifname[len] = '\0'; ioff += len; memset(rbuf, 0, rlen); c = snprintf(rbuf, rlen, "%c%1x%02x%08x%02x%s", CMD_REQUEST, CLIF_MSG_VERSION, cmd.cmd, cmd.ops, (unsigned int)strlen(cmd.ifname), cmd.ifname); here = check_and_update(&roff, &outlen, rbuf, c); if (!here) return cmd_failed; if (version == CLIF_MSG_VERSION) { hexstr2bin(ibuf+ioff, &cmd.type, sizeof(cmd.type)); ioff += 2 * sizeof(cmd.type); } else /* Command valid only for nearest customer bridge */ goto out; if (cmd.cmd == cmd_gettlv) { hexstr2bin(ibuf+ioff, (u8 *)&cmd.tlvid, sizeof(cmd.tlvid)); cmd.tlvid = ntohl(cmd.tlvid); ioff += 2 * sizeof(cmd.tlvid); } else goto out; c = snprintf(here, outlen, "%08x", cmd.tlvid); here = check_and_update(&roff, &outlen, here, c); if (!here) return cmd_failed; rstatus = vdp_clif_profile(cmd.ifname, here, outlen); out: return rstatus; } lldpad-0.9.46/lldpad.c000066400000000000000000000251451215142747300144730ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "eloop.h" #include "lldpad.h" #include "event_iface.h" #include "messages.h" #include "version.h" #include "lldp/ports.h" #include "lldp/l2_packet.h" #include "lldp_mand.h" #include "lldp_basman.h" #include "lldp_dcbx.h" #include "lldp_med.h" #include "lldp_8023.h" #include "lldp_evb.h" #include "lldp_evb22.h" #include "lldp_ecp22.h" #include "lldp_vdp.h" #include "lldp_vdp22.h" #include "lldp_8021qaz.h" #include "config.h" #include "lldpad_shm.h" #include "lldp/agent.h" #include "lldp/l2_packet.h" #include "clif.h" /* * insert to head, so first one is last */ struct lldp_module *(*register_tlv_table[])(void) = { mand_register, basman_register, dcbx_register, med_register, ieee8023_register, evb_register, evb22_register, vdp_register, vdp22_register, ecp22_register, ieee8021qaz_register, NULL, }; char *cfg_file_name = NULL; bool daemonize = 0; int loglvl = LOG_WARNING; static const char *lldpad_version = "lldpad v" VERSION_STR "\n" "Copyright (c) 2007-2010, Intel Corporation\n" "\nPortions used and/or modified from: hostapd v 0.5.7\n" "Copyright (c) 2004-2007, Jouni Malinen and contributors"; static void init_modules(void) { struct lldp_module *module; struct lldp_module *premod = NULL; int i = 0; LIST_INIT(&lldp_head); for (i = 0; register_tlv_table[i]; i++) { module = register_tlv_table[i](); if (!module) continue; if (premod) LIST_INSERT_AFTER(premod, module, lldp); else LIST_INSERT_HEAD(&lldp_head, module, lldp); premod = module; } } void deinit_modules(void) { struct lldp_module *module; while (lldp_head.lh_first != NULL) { module = lldp_head.lh_first; LIST_REMOVE(lldp_head.lh_first, lldp); module->ops->lldp_mod_unregister(module); } } static void usage(void) { fprintf(stderr, "\n" "usage: lldpad [-hdkspv] [-f configfile]" "\n" "options:\n" " -h show this usage\n" " -f use configfile instead of default\n" " -d run daemon in the background\n" " -k terminate current running lldpad\n" " -s remove lldpad state records\n" " -p Do not create PID file\n" " -v show version\n" " -V set syslog level\n"); exit(1); } /* * send_event: Send message to attach clients. * @moduleid - module identification of sender or 0 for legacy format * @msg - string encoded message */ void send_event(int level, u32 moduleid, char *msg) { struct clif_data *cd = NULL; cd = (struct clif_data *) eloop_get_user_data(); if (cd) ctrl_iface_send(cd, level, moduleid, msg, strlen(msg)); } static void remove_all_adapters() { struct port *port, *p; port = porthead; while (port != NULL) { p = port; port = port->next; remove_port(p->ifname); } return; } void lldpad_reconfig(UNUSED int sig, UNUSED void *eloop_ctx, UNUSED void *signal_ctx) { LLDPAD_WARN("lldpad: SIGHUP received reinit..."); /* Send LLDP SHUTDOWN frames and deinit modules */ clean_lldp_agents(); deinit_modules(); remove_all_adapters(); destroy_cfg(); /* Reinit config file and modules */ init_cfg(); init_modules(); init_ports(); return; } struct { const char *path; int score; } oom_adjust[] = {{"/proc/self/oom_score_adj", -1000}, {"/proc/self/oom_adj", -17}, {NULL, 0}}; /* * lldp_oom_adjust: Set oom score for lldpad * * Note we have two interfaces depending on kernel version. */ void lldpad_oom_adjust(void) { int i; for (i = 0; oom_adjust[i].path; i++) { FILE *oom_file = fopen(oom_adjust[i].path, "r+"); int err; if (!oom_file) continue; err = fprintf(oom_file, "%d", oom_adjust[i].score); fclose(oom_file); if (err < 0) continue; return; } LLDPAD_DBG("lldpad: OOM adjust failed\n"); }; int main(int argc, char *argv[]) { int c; struct clif_data *clifd; int fd = -1; char buf[32]; int shm_remove = 0; int killme = 0; int print_v = 0; int pid_file = 1; pid_t pid; int cnt; for (;;) { c = getopt(argc, argv, "dhkvspf:V:"); if (c < 0) break; switch (c) { case 'f': if (cfg_file_name) { usage(); break; } cfg_file_name = strdup(optarg); break; case 'd': daemonize = 1; break; case 'k': killme = 1; break; case 's': shm_remove = 1; break; case 'p': pid_file = 0; break; case 'v': print_v = 1; break; case 'V': loglvl = atoi(optarg); if (loglvl > LOG_DEBUG) loglvl = LOG_DEBUG; if (loglvl < LOG_EMERG) loglvl = LOG_EMERG; break; case 'h': default: usage(); break; } } /* exit if invalid input in the command line */ if (optind < argc ) usage(); if (print_v) { printf("%s\n", lldpad_version); exit(0); } if (cfg_file_name == NULL) cfg_file_name = DEFAULT_CFG_FILE; if (shm_remove) { mark_lldpad_shm_for_removal(); exit(0); } if (killme) { pid = lldpad_shm_getpid(); if (pid < 0) { perror("lldpad_shm_getpid failed"); LLDPAD_WARN("lldpad_shm_getpid failed\n"); exit (1); } else if (pid == PID_NOT_SET) { if (!lldpad_shm_setpid(DONT_KILL_PID)) { perror("lldpad_shm_setpid failed"); LLDPAD_WARN("lldpad_shm_setpid failed\n"); exit (1); } else { exit(0); } } else if (pid == DONT_KILL_PID) { exit (0); } if (!kill(pid, 0) && !kill(pid, SIGINT)) { cnt = 0; while (!kill(pid, 0) && cnt++ < 1000) usleep(10000); if (cnt >= 1000) { LLDPAD_WARN("failed to kill lldpad %d\n", pid); exit (1); } } else { perror("lldpad kill failed"); LLDPAD_WARN("lldpad kill failed\n"); } if (!lldpad_shm_setpid(DONT_KILL_PID)) { perror("lldpad_shm_setpid failed after kill"); LLDPAD_WARN("lldpad_shm_setpid failed after kill"); exit (1); } exit (0); } if (pid_file) { fd = open(PID_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { LLDPAD_ERR("error opening lldpad lock file"); exit(1); } if (flock(fd, LOCK_EX | LOCK_NB) < 0) { if (errno == EWOULDBLOCK) { fprintf(stderr, "lldpad is already running\n"); if (read(fd, buf, sizeof(buf)) > 0) { fprintf(stderr, "pid of existing" "lldpad is %s\n", buf); } LLDPAD_ERR("lldpad already running"); } else { perror("error locking lldpad lock file"); LLDPAD_ERR("error locking lldpad lock file"); } exit(1); } } lldpad_oom_adjust(); /* initialize lldpad user data */ clifd = malloc(sizeof(struct clif_data)); if (clifd == NULL) { LLDPAD_ERR("failed to malloc user data\n"); exit(1); } /* initialize lldpad configuration file */ if (!init_cfg()) { LLDPAD_ERR("failed to initialize configuration file\n"); exit(1); } if (eloop_init(clifd)) { LLDPAD_ERR("failed to initialize event loop\n"); exit(1); } /* initialize the client interface socket before daemonize */ if (ctrl_iface_init(clifd) < 0) { LLDPAD_ERR("failed to register client interface\n"); exit(1); } if (daemonize && daemon(1, 0)) { LLDPAD_ERR("error daemonizing lldpad"); goto out; } if (pid_file) { if (lseek(fd, 0, SEEK_SET) < 0) { LLDPAD_ERR("error seeking lldpad lock file\n"); exit(1); } memset(buf, 0, sizeof(buf)); sprintf(buf, "%u\n", getpid()); if (write(fd, buf, sizeof(buf)) < 0) perror("error writing to lldpad lock file"); if (fsync(fd) < 0) perror("error syncing lldpad lock file"); close(fd); } pid = lldpad_shm_getpid(); if (pid < 0) { LLDPAD_ERR("error getting shm pid"); if (pid_file) unlink(PID_FILE); exit(1); } else if (pid == PID_NOT_SET) { if (!lldpad_shm_setpid(getpid())) { perror("lldpad_shm_setpid failed"); LLDPAD_ERR("lldpad_shm_setpid failed\n"); if (pid_file) unlink(PID_FILE); exit (1); } } else if (pid != DONT_KILL_PID) { if (!kill(pid, 0)) { LLDPAD_ERR("lldpad already running"); if (pid_file) unlink(PID_FILE); exit(1); } /* pid in shm no longer has a process, go ahead * and let this lldpad instance execute. */ if (!lldpad_shm_setpid(getpid())) { perror("lldpad_shm_setpid failed"); LLDPAD_ERR("error overwriting shm pid"); if (pid_file) unlink(PID_FILE); exit (1); } } openlog("lldpad", LOG_CONS | LOG_PID, LOG_DAEMON); setlogmask(LOG_UPTO(loglvl)); if (check_cfg_file()) exit(1); /* setup event netlink interface for user space processes. * This needs to be setup first to ensure it gets lldpads * pid as netlink address. */ if (event_iface_init_user_space() < 0) { LLDPAD_ERR("lldpad failed to start - failed to register user space event interface\n"); exit(1); } init_modules(); eloop_register_signal_terminate(eloop_terminate, NULL); eloop_register_signal_reconfig(lldpad_reconfig, NULL); /* setup LLDP agent */ if (!start_lldp_agents()) { LLDPAD_ERR("failed to initialize LLDP agent\n"); exit(1); } /* setup event RT netlink interface */ if (event_iface_init() < 0) { LLDPAD_ERR("failed to register event interface\n"); exit(1); } /* Find available interfaces and add adapters */ init_ports(); if (ctrl_iface_register(clifd) < 0) { if (!daemonize) fprintf(stderr, "failed to register control interface\n"); LLDPAD_ERR("lldpad failed to start - " "failed to register control interface\n"); goto out; } eloop_run(); clean_lldp_agents(); deinit_modules(); remove_all_adapters(); ctrl_iface_deinit(clifd); /* free's clifd */ event_iface_deinit(); stop_lldp_agents(); out: destroy_cfg(); closelog(); if (pid_file) unlink(PID_FILE); eloop_destroy(); if (eloop_terminated()) exit(0); exit(1); } lldpad-0.9.46/lldpad.init000066400000000000000000000140731215142747300152120ustar00rootroot00000000000000#!/bin/bash ################################################################################ # # Intel Data Center Bridging (DCB) Software # Copyright(c) 2007-2008 Intel Corporation. # # Based on: # Template LSB system startup script for example service/daemon FOO # Copyright (C) 1995--2005 Kurt Garloff, SUSE / Novell Inc. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # # The full GNU General Public License is included in this distribution in # the file called "COPYING". # # Contact Information: # open-lldp Mailing List # ################################################################################ # # lldpad This shell script takes care of starting and stopping # lldpad (including DCB capabilities exchange protocol) # # chkconfig: - 20 80 # description: Link Layer Discovery Protocol Agent Daemon # ### BEGIN INIT INFO # Provides: lldpad # Required-Start: network # Required-Stop: # Default-Start: 3 5 # Default-Stop: # Short Description: Link Layer Discovery Protocol Agent Daemon # Description: Link Layer Discovery Protocol Agent Daemon ### END INIT INFO LLDPAD=lldpad LLDPAD_BIN=/usr/sbin/$LLDPAD test -x $LLDPAD_BIN || { echo "$LLDPAD_BIN not installed"; if [ "$1" = "stop" ]; then exit 0; else exit 5; fi; } # Default: Assume sysvinit binaries exist start_daemon() { /sbin/start_daemon ${1+"$@"}; } killproc() { /sbin/killproc ${1+"$@"}; } pidofproc() { /sbin/pidofproc ${1+"$@"}; } checkproc() { /sbin/checkproc ${1+"$@"}; } if test -e /etc/rc.status; then # SUSE rc script library . /etc/rc.status else export LC_ALL=POSIX _cmd=$1 declare -a _SMSG if test "${_cmd}" = "status"; then _SMSG=(running dead dead unused unknown reserved) _RC_UNUSED=3 else _SMSG=(done failed failed missed failed skipped unused failed \ failed reserved) _RC_UNUSED=6 fi if test -e /lib/lsb/init-functions; then # LSB . /lib/lsb/init-functions echo_rc() { if test ${_RC_RV} = 0; then log_success_msg " [${_SMSG[${_RC_RV}]}] " else log_failure_msg " [${_SMSG[${_RC_RV}]}] " fi } # TODO: Add checking for lockfiles checkproc() { pidofproc ${1+"$@"} >/dev/null 2>&1; } elif test -e /etc/init.d/functions; then # RHAT . /etc/init.d/functions echo_rc() { #echo -n " [${_SMSG[${_RC_RV}]}] " if test ${_RC_RV} = 0; then success " [${_SMSG[${_RC_RV}]}] " else failure " [${_SMSG[${_RC_RV}]}] " fi } checkproc() { status ${1+"$@"}; return $?; } start_daemon() { daemon ${1+"$@"}; return $?; } else # emulate it echo_rc() { echo " [${_SMSG[${_RC_RV}]}] "; } fi rc_reset() { _RC_RV=0; } rc_failed() { if test -z "$1"; then _RC_RV=1; elif test "$1" != "0"; then _RC_RV=$1; fi return ${_RC_RV} } rc_check() { return rc_failed $? } rc_status() { rc_failed $? if test "$1" = "-r"; then _RC_RV=0; shift; fi if test "$1" = "-s"; then rc_failed 5; echo_rc; rc_failed 3; shift; fi if test "$1" = "-u"; then rc_failed ${_RC_UNUSED}; echo_rc; rc_failed 3; shift; fi if test "$1" = "-v"; then echo_rc; shift; fi if test "$1" = "-r"; then _RC_RV=0; shift; fi return ${_RC_RV} } rc_exit() { exit ${_RC_RV}; } rc_active() { if test -z "$RUNLEVEL"; then read RUNLEVEL REST < <(/sbin/runlevel); fi if test -e /etc/init.d/S[0-9][0-9]${1}; then return 0; fi return 1 } fi # Reset status of this service rc_reset # See how we were called. case "$1" in start) [ "$EUID" = "0" ] || exit 4 echo -n $"Starting $LLDPAD: " $LLDPAD_BIN -k start_daemon $LLDPAD_BIN -d $OPTIONS rc_status -v [ $? -eq 0 ] && touch /var/lock/subsys/lldpad ;; stop) [ "$EUID" = "0" ] || exit 4 echo -n $"Shutting down $LLDPAD: " killproc $LLDPAD rc_status -v [ $? -eq 0 ] && rm -f /var/lock/subsys/lldpad ;; status) echo -n "Checking for service $LLDPAD: " p=`dcbtool ping 2> /dev/null` RC=$? if [ "$RC" != "0" ]; then echo "stopped" if [ -f /var/lock/subsys/lldpad ]; then rc_failed 2 elif [ -z "$p" ]; then rc_failed 1 else rc_failed 3 fi else echo "running" fi ;; try-restart|condrestart) ## Do a restart only if the service was active before. ## Note: try-restart is now part of LSB (as of 1.9). ## RH has a similar command named condrestart. if test "$1" = "condrestart"; then echo "${attn} Use try-restart ${done}(LSB)${attn}"\ "rather than condrestart ${warn}(RH)${norm}" fi $0 restart ;; restart) $0 status if test $? = 0; then $0 stop $0 start else rc_reset # Not running is not a failure. fi # Remember status and be quiet rc_status ;; force-reload) ## Signal the daemon to reload its config. Most daemons ## do this on signal 1 (SIGHUP). ## If it does not support it, restart the service if it ## is running. #echo -n "Reload service $LLDPAD " ## if it supports it: #killproc -HUP $LLDPAD #touch /var/run/$LLDPAD.pid #rc_status -v ## Otherwise: $0 try-restart rc_status ;; reload) ## Like force-reload, but if daemon does not support ## signaling, do nothing (!) # If it supports signaling: #echo -n "Reload service $LLDPAD " #killproc -HUP $LLDPAD_BIN #touch /var/run/$LLDPAD.pid #rc_status -v ## Otherwise if it does not support reload: echo -n $"Reloading $LLDPAD is not supported: " rc_failed 3 rc_status -v ;; usage) echo $"Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" exit 0 ;; *) echo $"Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" exit 2 esac rc_exit lldpad-0.9.46/lldpad.pc.in000066400000000000000000000003141215142747300152470ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: lldpad Description: LLDP Agent with Data Center Bridging support Version: @VERSION@ Cflags: -I${includedir} Libs: lldpad-0.9.46/lldpad.service000066400000000000000000000002721215142747300157030ustar00rootroot00000000000000[Unit] Description=Link Layer Discovery Protocol Agent Daemon. After=syslog.target network.target [Service] Type=simple ExecStart=/usr/sbin/lldpad [Install] WantedBy=multi-user.target lldpad-0.9.46/lldpad.spec.in000066400000000000000000000035341215142747300156060ustar00rootroot00000000000000Name: lldpad Version: @VERSION@ Release: 1%{?dist} Summary: Intel LLDP Agent Group: System Environment/Daemons License: GPLv2 URL: http://e1000.sourceforge.net Source0: http://downloads.sourceforge.net/e1000/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) # BuildRequires: Requires(post): chkconfig Requires(preun): chkconfig initscripts Requires(postun): initscripts %description This package contains the Linux user space daemon and configuration tool for Intel LLDP Agent with Enhanced Ethernet support for the Data Center. %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel The %{name}-devel package contains header files for developing applications that use %{name}. %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %post /sbin/chkconfig --add lldpad %preun if [ $1 = 0 ]; then /sbin/service lldpad stop /sbin/chkconfig --del lldpad fi %postun if [ $1 = 1 ]; then /sbin/service lldpad condrestart fi %files %defattr(-,root,root,-) %doc COPYING %doc README %doc ChangeLog %{_sbindir}/* %dir /var/lib/lldpad %{_sysconfdir}/init.d/lldpad %{_sysconfdir}/bash_completion.d/lldpad %{_sysconfdir}/bash_completion.d/lldptool %{_mandir}/man8/* %{_libdir}/liblldp_clif.* %files devel %defattr(-,root,root,-) %doc COPYING %doc README %{_includedir}/* %{_libdir}/pkgconfig/*.pc %changelog * Mon Feb 22 2010 John Fastabend - 0.9.22 - initial generic lldp support * Thu Feb 26 2009 Chris Leech - 0.9.5-1 - initial RPM packaging lldpad-0.9.46/lldpad_shm.c000066400000000000000000000375231215142747300153450ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "dcb_protocol.h" #include "lldpad_shm.h" #include "lldp.h" void mark_lldpad_shm_for_removal() { int shmid; struct shmid_ds shminfo; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) return; if (shmctl(shmid, IPC_RMID, &shminfo) < 0) return; } /* return: 1 = success, 0 = failed */ void lldpad_shm_ver0_to_ver1(struct lldpad_shm_tbl_ver0 *shmold, int num_old_entries) { unsigned i; unsigned num_entries; struct lldpad_shm_entry new_ent[MAX_LLDPAD_SHM_ENTRIES]; num_entries = num_old_entries; if (num_entries > MAX_LLDPAD_SHM_ENTRIES) num_entries = MAX_LLDPAD_SHM_ENTRIES; shmold->num_entries = (num_entries & SHM_NUM_ENT_MASK) | (1 << SHM_VER_SHIFT); for (i = 0; i < num_entries; i++) { memcpy(new_ent[i].ifname, shmold->ent[i].ifname, sizeof(new_ent[i].ifname)); memcpy(&new_ent[i].chassisid[0], &shmold->ent[i].chassisid[0], SHM_CHASSISID_LEN); new_ent[i].chassisid_len = shmold->ent[i].chassisid_len; memcpy(&new_ent[i].portid[0], &shmold->ent[i].portid[0], SHM_PORTID_LEN); new_ent[i].portid_len = shmold->ent[i].portid_len; memcpy((void *)&new_ent[i].st, (void *)&shmold->ent[i].st, sizeof(dcbx_state)); new_ent[i].dcbx_mode = 0; } memcpy((void *)&shmold->ent[0], (void *)&new_ent[0], sizeof(struct lldpad_shm_entry) * num_entries); } /* return: 1 = success, 0 = failed */ int lldpad_shm_get_msap(const char *device_name, int type, char *info, size_t *len) { char *p; int shmid; struct lldpad_shm_tbl *shmaddr=NULL; pid_t rval = 0; unsigned i; unsigned num_entries; int version; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0 && errno == ENOENT) shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0x180); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) break; if (i == shmaddr->num_entries) goto done; if (type == CHASSIS_ID_TLV) { p = &shmaddr->ent[i].chassisid[0]; *len = shmaddr->ent[i].chassisid_len; } else if (type == PORT_ID_TLV) { p = &shmaddr->ent[i].portid[0]; *len = shmaddr->ent[i].portid_len; } else goto done; if (*len) { memcpy(info, p, *len); rval = 1; } done: shmdt(shmaddr); return rval; } /* return: 1 = success, 0 = failed */ int lldpad_shm_set_msap(const char *device_name, int type, char *info, size_t len) { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; pid_t rval = 0; unsigned i; int version; unsigned num_entries; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0 && errno == ENOENT) shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0x180); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) break; if (i < MAX_LLDPAD_SHM_ENTRIES) { if (type == CHASSIS_ID_TLV && len <= SHM_CHASSISID_LEN) { memset(&shmaddr->ent[i].chassisid[0], 0, SHM_CHASSISID_LEN); memcpy(&shmaddr->ent[i].chassisid[0], info, len); shmaddr->ent[i].chassisid_len = len; } else if (type == PORT_ID_TLV && len <= SHM_PORTID_LEN) { memset(&shmaddr->ent[i].portid[0], 0, SHM_PORTID_LEN); memcpy(&shmaddr->ent[i].portid[0], info, len); shmaddr->ent[i].portid_len = len; } else goto done; if (i == num_entries) { shmaddr->num_entries++; sprintf(shmaddr->ent[i].ifname, "%.*s", IFNAMSIZ, device_name); } rval = 1; } done: shmdt(shmaddr); return rval; } int lldpad_shm_get_dcbx(const char *device_name) { int shmid; struct lldpad_shm_tbl *shmaddr = NULL; pid_t rval = 0; /* zero is default DCBX auto mode */ unsigned i; unsigned num_entries; int version; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0 && errno == ENOENT) shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0x180); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) goto done; num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) { if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) { switch (shmaddr->ent[i].dcbx_mode) { case dcbx_subtype1: case dcbx_subtype2: rval = shmaddr->ent[i].dcbx_mode; break; default: ; } } } done: shmdt(shmaddr); return rval; } /* return: 1 = success, 0 = failed */ int lldpad_shm_set_dcbx(const char *device_name, int dcbx_mode) { int shmid; struct lldpad_shm_tbl *shmaddr = NULL; pid_t rval = 0; unsigned i; unsigned num_entries; int version; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0 && errno == ENOENT) shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0x180); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; if ((dcbx_mode != dcbx_subtype0) && (dcbx_mode != dcbx_subtype1) && (dcbx_mode != dcbx_subtype2)) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) { if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) { shmaddr->ent[i].dcbx_mode = dcbx_mode; rval = 1; break; } } /* make a new entry if no existing entry */ if ((i == num_entries) && (i < MAX_LLDPAD_SHM_ENTRIES)) { shmaddr->num_entries++; sprintf(shmaddr->ent[i].ifname, "%.*s", IFNAMSIZ, device_name); memset(&shmaddr->ent[i].chassisid[0], 0, SHM_CHASSISID_LEN); shmaddr->ent[i].chassisid_len = 0; memset(&shmaddr->ent[i].portid[0], 0, SHM_PORTID_LEN); shmaddr->ent[i].portid_len = 0; memset((void *)&shmaddr->ent[i].st, 0, sizeof(dcbx_state)); shmaddr->ent[i].dcbx_mode = dcbx_mode; rval = 1; } done: shmdt(shmaddr); return rval; } /* return: -1 = failed, >=0 = success * Should always be called first by lldpad. This is the * only routine which creates the shared memory segment. */ pid_t lldpad_shm_getpid() { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; pid_t rval = -1; int version; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0 && errno == ENOENT) shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0x180); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); rval = shmaddr->pid; shmdt(shmaddr); return rval; } /* return: 1 = success, 0 = failed */ int lldpad_shm_setpid(pid_t pid) { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; pid_t rval = 0; int version; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); shmaddr->pid = pid; shmdt(shmaddr); return 1; } /* return: 1 = success, 0 = failed */ int clear_dcbx_state() { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; unsigned i; int version; unsigned num_entries; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) return 0; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return 0; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* clear out dcbx_state for all entries */ for (i = 0; i < num_entries; i++) if (strlen(shmaddr->ent[i].ifname)) memset((void *)&shmaddr->ent[i].st, 0, sizeof(dcbx_state)); done: shmdt(shmaddr); return 1; } /* return: 1 = success, 0 = failed */ int set_dcbx_state(const char *device_name, dcbx_state *state) { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; unsigned i; int rval = 0; int version; unsigned num_entries; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) break; if (i < MAX_LLDPAD_SHM_ENTRIES) { if (i == num_entries) { shmaddr->num_entries++; sprintf(shmaddr->ent[i].ifname, "%.*s", IFNAMSIZ, device_name); } memcpy((void *)&shmaddr->ent[i].st, state, sizeof(dcbx_state)); rval = 1; } done: shmdt(shmaddr); return rval; } /* find and return a dcbx_state for the given device_name. * clear the dcbx_state entry after getting it - it's only valid * for the first read. * return: 1 = success, 0 = failed */ int get_dcbx_state(const char *device_name, dcbx_state *state) { int shmid; struct lldpad_shm_tbl *shmaddr=NULL; unsigned i; int rval = 0; int version; unsigned num_entries; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) return rval; shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); if ((long) shmaddr == -1) return rval; version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) lldpad_shm_ver0_to_ver1((struct lldpad_shm_tbl_ver0 *) shmaddr, shmaddr->num_entries & SHM_NUM_ENT_MASK); num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; /* check for invalid number of shm table entries */ if (num_entries > MAX_LLDPAD_SHM_ENTRIES) goto done; /* search for existing entry */ for (i = 0; i < num_entries; i++) if (strcmp(shmaddr->ent[i].ifname, device_name) == 0) { memcpy(state, (void *)&shmaddr->ent[i].st, sizeof(dcbx_state)); memset((void *)&shmaddr->ent[i].st, 0, sizeof(dcbx_state)); rval = 1; break; } done: shmdt(shmaddr); return rval; } #ifdef SHM_UTL /* compile utility to print out lldpad shared memory segment as follows: * gcc -o lldpad_shm -I. -Iinclude -DSHM_UTL lldpad_shm.c */ int print_lldpad_shm() { int shmid; struct lldpad_shm_tbl_ver0 *shmaddr_ver0 = NULL; struct lldpad_shm_tbl *shmaddr=NULL; unsigned i; int j; int rval = 0; int version; unsigned num_entries; unsigned max_entries; int ent_size; struct lldpad_shm_entry *entry_ptr = NULL; shmid = shmget(LLDPAD_SHM_KEY, LLDPAD_SHM_SIZE, 0); if (shmid < 0) { printf("failed to shmget\n"); return rval; } shmaddr = (struct lldpad_shm_tbl *)shmat(shmid, NULL, 0); shmaddr_ver0 = (struct lldpad_shm_tbl_ver0 *)shmaddr; if ((long) shmaddr == -1) { printf("failed to shmat\n"); return rval; } version = (shmaddr->num_entries & SHM_VER_MASK) >> SHM_VER_SHIFT; if (version == 0) { max_entries = MAX_LLDPAD_SHM_ENTRIES_VER0; ent_size = sizeof(struct lldpad_shm_entry_ver0); } else { max_entries = MAX_LLDPAD_SHM_ENTRIES; ent_size = sizeof(struct lldpad_shm_entry); } num_entries = shmaddr->num_entries & SHM_NUM_ENT_MASK; printf("pid = %d\n", shmaddr->pid); printf("version = %d\n", version); printf("num_entries = %d\n", num_entries); printf("max num_entries = %d\n", max_entries); /* check for invalid number of shm table entries */ if (num_entries > max_entries) goto done; for (i = 0; i < num_entries; i++) { if (version == 0) entry_ptr = (struct lldpad_shm_entry *)&shmaddr_ver0->ent[i]; else entry_ptr = &shmaddr->ent[i]; printf("ifname: %s\n", entry_ptr->ifname); printf("chassisid: "); for (j = 0; j < entry_ptr->chassisid_len; j++) printf("%02x", (unsigned char)entry_ptr->chassisid[j]); printf("\n"); printf("portid: "); for (j = 0; j < entry_ptr->portid_len; j++) printf("%02x", (unsigned char)entry_ptr->portid[j]); printf("\n"); printf("SeqNo: %d\n", entry_ptr->st.SeqNo); printf("AckNo: %d\n", entry_ptr->st.AckNo); printf("FCoEenable: %d\n", entry_ptr->st.FCoEenable); printf("iSCSIenable: %d\n", entry_ptr->st.iSCSIenable); if (version) { printf("DCBX mode: "); switch (entry_ptr->dcbx_mode) { case dcbx_subtype0: printf("Auto (IEEE)\n"); break; case dcbx_subtype1: printf("CIN\n"); break; case dcbx_subtype2: printf("CEE\n"); break; default: printf("unknown\n"); } } } rval = 1; done: shmdt(shmaddr); return rval; } static void usage(void) { fprintf(stderr, "\n" "usage: lldpad_shm [-r]" "\n" "options:\n" " -r dump lldpad shared memory in raw format\n"); exit(1); } main() { print_lldpad_shm(); } #endif lldpad-0.9.46/lldptool.c000066400000000000000000000476231215142747300150710ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2012 Intel Corporation. Substantially modified from: hostapd-0.5.7 Copyright (c) 2002-2007, Jouni Malinen and contributors This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "clif.h" #include "lldp_mand_clif.h" #include "lldp_basman_clif.h" #include "lldp_med_clif.h" #include "lldp_8023_clif.h" #include "lldp_dcbx_clif.h" #include "lldp_evb22_clif.h" #include "lldp_evb_clif.h" #include "lldp_vdp_clif.h" #include "lldp_8021qaz_clif.h" #include "lldp_orgspec_clif.h" #include "lldp_cisco_clif.h" #include "lldptool.h" #include "lldptool_cli.h" #include "lldp_mod.h" #include "lldp_mand.h" #include "lldp_basman.h" #include "lldp_med.h" #include "lldp_dcbx.h" #include "version.h" #include "lldpad.h" #include "lldp_util.h" #include "lldpad_status.h" static int show_raw; static const char *cli_version = "lldptool v" LLDPTOOL_VERSION "\n" "Copyright (c) 2007-2010, Intel Corporation\n" "\nSubstantially modified from: hostapd_cli v 0.5.7\n" "Copyright (c) 2004-2007, Jouni Malinen and contributors"; static const char *cli_license = "This program is free software. You can distribute it and/or modify it\n" "under the terms of the GNU General Public License version 2.\n" "\n"; /* "Alternatively, this software may be distributed under the terms of the\n" "BSD license. See README and COPYING for more details.\n"; */ static const char *cli_full_license = "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License version 2 as\n" "published by the Free Software Foundation.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" "\n" "Alternatively, this software may be distributed under the terms of the\n" "BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" "met:\n" "\n" "1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" "\n" "2. Redistributions in binary form must reproduce the above copyright\n" " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n" "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" " names of its contributors may be used to endorse or promote products\n" " derived from this software without specific prior written permission.\n" "\n" "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "\n"; static const char *commands_usage = "Usage:\n" " lldptool [options] [arg] general command line usage format\n" " lldptool go into interactive mode\n" " [options] [arg] general interactive command format\n"; static const char *commands_options = "Options:\n" " -i [ifname] network interface\n" " -V [tlvid] TLV identifier\n" " may be numeric or keyword (see below)\n" " -c used with get TLV command to specify\n" " that the list of configuration elements\n" " should be retrieved\n" " -d use to delete specified argument from\n" " the configuration. (Currently\n" " implemented for DCBX App TLV settings)\n" " -n \"neighbor\" option for command\n" " -r show raw message\n" " -R show only raw messages\n" " -g destination agent (may be one of):\n" " - nearestbridge (nb) (default)\n" " - nearestcustomerbridge (ncb)\n" " - nearestnontpmrbridge (nntpmrb)\n"; static const char *commands_help = "Commands:\n" " license show license information\n" " -h|help show command usage information\n" " -v|version show version\n" " -p|ping ping lldpad and query pid of lldpad\n" " -q|quit exit lldptool (interactive mode)\n" " -S|stats get LLDP statistics for ifname\n" " -t|get-tlv get TLVs from ifname\n" " -T|set-tlv set arg for tlvid to value\n" " -l|get-lldp get the LLDP parameters for ifname\n" " -L|set-lldp set the LLDP parameter for ifname\n"; static struct clif *clif_conn; static int cli_quit = 0; static int cli_attached = 0; /* * insert to head, so first one is last */ struct lldp_module *(*register_tlv_table[])(void) = { mand_cli_register, basman_cli_register, ieee8023_cli_register, med_cli_register, dcbx_cli_register, evb22_cli_register, evb_cli_register, vdp_cli_register, ieee8021qaz_cli_register, orgspec_cli_register, cisco_cli_register, NULL, }; static void init_modules(void) { struct lldp_module *module; struct lldp_module *premod = NULL; int i = 0; LIST_INIT(&lldp_cli_head); for (i = 0; register_tlv_table[i]; i++) { module = register_tlv_table[i](); if (premod) LIST_INSERT_AFTER(premod, module, lldp); else LIST_INSERT_HEAD(&lldp_head, module, lldp); premod = module; } } void deinit_modules(void) { struct lldp_module *module; while (lldp_head.lh_first != NULL) { module = lldp_head.lh_first; LIST_REMOVE(lldp_head.lh_first, lldp); module->ops->lldp_mod_unregister(module); } } static void usage(void) { fprintf(stderr, "%s\n", cli_version); fprintf(stderr, "\n%s\n%s\n%s\n", commands_usage, commands_options, commands_help); } /* assumes input is pointer to two hex digits */ /* returns -1 on error */ int hex2int(char *b) { int i; int n=0; int m; for (i=0,m=1; i<2; i++,m--) { if (isxdigit(*(b+i))) { if (*(b+i) <= '9') n |= (*(b+i) & 0x0f) << (4*m); else n |= ((*(b+i) & 0x0f) + 9) << (4*m); } else { return -1; } } return n; } void print_raw_message(char *msg, int print) { if (!print || !(print & SHOW_RAW)) return; if (!(print & SHOW_RAW_ONLY)) { switch (msg[MSG_TYPE]) { case EVENT_MSG: printf("event: "); break; case CMD_RESPONSE: printf("rsp: "); break; default: printf("cmd: "); break; } } printf("%s\n", msg); } int parse_print_message(char *msg, int print) { int status = 0; status = parse_response(msg); print_raw_message(msg, print); if (print & SHOW_RAW_ONLY) return status; if (msg[MSG_TYPE] == CMD_RESPONSE) print_response(msg, status); else if (msg[MSG_TYPE] == MOD_CMD && msg[MOD_MSG_TYPE] == EVENT_MSG) print_event_msg(&msg[MOD_MSG_TYPE]); return status; } static void cli_close_connection(void) { if (clif_conn == NULL) return; if (cli_attached) { clif_detach(clif_conn); cli_attached = 0; } clif_close(clif_conn); clif_conn = NULL; } static void cli_msg_cb(char *msg, UNUSED size_t len) { parse_print_message(msg, SHOW_OUTPUT | show_raw); } /* structure of the print argument bitmap: * SHOW_NO_OUTPUT (0x0) - don't print anything for the command * SHOW_OUTPUT (0x01) - print output for the command * SHOW_RAW (0x02) - print the raw clif command messages * SHOW_RAW_ONLY (0x04) - print only the raw clif command messages */ static int _clif_command(struct clif *clif, char *cmd, int print) { char buf[MAX_CLIF_MSGBUF]; size_t len; int ret; print_raw_message(cmd, print); if (clif_conn == NULL) { printf("Not connected to lldpad - command dropped.\n"); return -1; } len = sizeof(buf) - 1; ret = clif_request(clif, cmd, strlen(cmd), buf, &len, cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); return -2; } else if (ret < 0) { printf("'%s' command failed.\n", cmd); return -1; } if (print) { buf[len] = '\0'; ret = parse_print_message(buf, print); } return ret; } inline int clif_command(struct clif *clif, char *cmd, int raw) { return _clif_command(clif, cmd, SHOW_OUTPUT | raw); } static int cli_cmd_ping(struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, int raw) { return clif_command(clif, "P", raw); } static int cli_cmd_nop(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, UNUSED int raw) { return 0; } static int cli_cmd_help(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, UNUSED int raw) { struct lldp_module *np; printf("%s\n%s\n%s", commands_usage, commands_options, commands_help); printf("\nTLV identifiers:\n"); LIST_FOREACH(np, &lldp_head, lldp) if (np->ops->print_help) np->ops->print_help(); return 0; } static int cli_cmd_version(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, UNUSED int raw) { printf("%s\n", cli_version); return 0; } static int cli_cmd_license(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, UNUSED int raw) { printf("%s\n", cli_full_license); return 0; } static int cli_cmd_quit(UNUSED struct clif *clif, UNUSED int argc, UNUSED char *argv[], UNUSED struct cmd *command, UNUSED int raw) { cli_quit = 1; return 0; } struct cli_cmd { lldp_cmd cmdcode; const char *cmdstr; int (*handler)(struct clif *clif, int argc, char *argv[], struct cmd *cmd, int raw); }; static struct cli_cmd cli_commands[] = { { cmd_ping, "ping", cli_cmd_ping }, { cmd_help, "help", cli_cmd_help }, { cmd_license, "license", cli_cmd_license }, { cmd_version, "version", cli_cmd_version }, { cmd_quit, "quit", cli_cmd_quit }, { cmd_getstats, "stats", cli_cmd_getstats }, { cmd_gettlv, "gettlv", cli_cmd_gettlv }, { cmd_gettlv, "get-tlv", cli_cmd_gettlv }, { cmd_settlv, "settlv", cli_cmd_settlv }, { cmd_settlv, "set-tlv", cli_cmd_settlv }, { cmd_get_lldp, "getlldp", cli_cmd_getlldp }, { cmd_get_lldp, "get-lldp", cli_cmd_getlldp }, { cmd_set_lldp, "setlldp", cli_cmd_setlldp }, { cmd_set_lldp, "set-lldp", cli_cmd_setlldp }, { cmd_nop, NULL, cli_cmd_nop } }; u32 lookup_tlvid(char *tlvid_str) { struct lldp_module *np; u32 tlvid = INVALID_TLVID; LIST_FOREACH(np, &lldp_head, lldp) { if (np->ops->lookup_tlv_name) { tlvid = np->ops->lookup_tlv_name(tlvid_str); if (tlvid != INVALID_TLVID) break; } } return tlvid; } void print_args(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) printf("\tremaining arg %d = %s\n", i, argv[i]); } static struct option lldptool_opts[] = { {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'v'}, {"stats", 0, NULL, 'S'}, {"get-tlv", 0, NULL, 't'}, {"set-tlv", 0, NULL, 'T'}, {"get-lldp", 0, NULL, 'l'}, {"set-lldp", 0, NULL, 'L'}, {0, 0, 0, 0} }; static int request(struct clif *clif, int argc, char *argv[]) { struct cli_cmd *cmd, *match = NULL; struct cmd command; int count; int ret = 0; int newraw = 0; int numargs = 0; char **argptr = &argv[0]; char *end; int c; int option_index; memset((void *)&command, 0, sizeof(command)); command.cmd = cmd_nop; command.type = NEAREST_BRIDGE; command.module_id = LLDP_MOD_MAND; command.tlvid = INVALID_TLVID; opterr = 0; for (;;) { c = getopt_long(argc, argv, "Si:tTlLhcdnvrRpqV:g:", lldptool_opts, &option_index); if (c < 0) break; switch (c) { case '?': printf("missing argument for option %s\n\n", argv[optind-1]); usage(); return -1; case 'S': command.cmd = cmd_getstats; break; case 'i': strncpy(command.ifname, optarg, IFNAMSIZ); command.ifname[IFNAMSIZ] ='\0'; break; case 'g': if (!strcasecmp(optarg, "nearestbridge") || !strcasecmp(optarg, "nb")) command.type = NEAREST_BRIDGE; else if (!strcasecmp(optarg, "nearestcustomerbridge") || !strcasecmp(optarg, "ncb")) command.type = NEAREST_CUSTOMER_BRIDGE; else if (!strcasecmp(optarg, "nearestnontmprbridge") || !strcasecmp(optarg, "nntpmrb")) command.type = NEAREST_NONTPMR_BRIDGE; else { printf("Invalid agent specified !\n\n"); return -1; } break; case 'V': if (command.tlvid != INVALID_TLVID) { printf("\nInvalid command: multiple TLV " "identifiers: %s\n", optarg); return -1; } /* Currently tlvid unset lookup and verify parameter */ errno = 0; command.tlvid = strtoul(optarg, &end, 0); if (!command.tlvid || errno || *end != '\0' || end == optarg) { command.tlvid = lookup_tlvid(optarg); if (!strcasecmp("vdp", optarg)) command.module_id = command.tlvid; } if (command.tlvid == INVALID_TLVID) { printf("\nInvalid TLV identifier: %s\n", optarg); return -1; } break; case 'p': command.cmd = cmd_ping; break; case 'q': command.cmd = cmd_quit; break; case 't': command.cmd = cmd_gettlv; break; case 'T': command.cmd = cmd_settlv; break; case 'l': command.cmd = cmd_get_lldp; break; case 'L': command.cmd = cmd_set_lldp; break; case 'c': command.ops |= op_config; break; case 'd': command.ops |= op_delete; break; case 'n': command.ops |= op_neighbor; break; case 'h': command.cmd = cmd_help; break; case 'r': if (newraw) { usage(); return -1; } newraw = SHOW_RAW; break; case 'R': if (newraw) { usage(); return -1; } newraw = (SHOW_RAW | SHOW_RAW_ONLY); break; case 'v': command.cmd = cmd_version; break; default: usage(); ret = -1; } } /* * If -V vdp option is set together with -c option, use standard * module to retrieve data. */ if ((command.ops & op_config) && command.tlvid == command.module_id) command.module_id = LLDP_MOD_MAND; /* if no command was supplied via an option flag, then * the first remaining argument should be the command. */ count = 0; if (command.cmd == cmd_nop && optind < argc) { cmd = cli_commands; while (cmd->cmdcode != cmd_nop) { if (strncasecmp(cmd->cmdstr, argv[optind], strlen(argv[optind])) == 0) { match = cmd; command.cmd = match->cmdcode; count++; } cmd++; } } if (count > 1) { printf("Ambiguous command '%s'; possible commands:", argv[optind]); cmd = cli_commands; while (cmd->cmdstr) { if (strncasecmp(cmd->cmdstr, argv[optind], strlen(argv[optind])) == 0) printf(" %s", cmd->cmdstr); cmd++; } printf("\n"); ret = -1; } else { if (!match) { cmd = cli_commands; while (cmd->cmdcode != command.cmd) cmd++; match = cmd; } if (count == 0) numargs = argc-optind; else numargs = argc-optind-1; if (numargs) argptr = &argv[argc-numargs]; ret = match->handler(clif, numargs, argptr, &command, newraw); } return ret; } static void cli_recv_pending(struct clif *clif, int in_read) { int first = 1; if (clif == NULL) return; while (clif_pending(clif)) { char buf[256]; size_t len = sizeof(buf) - 1; if (clif_recv(clif, buf, &len) == 0) { buf[len] = '\0'; if (in_read && first) printf("\n"); first = 0; cli_msg_cb(buf, len); } else { printf("Could not read pending message.\n"); break; } } } static void cli_interactive() { const int max_args = 20; char *cmd, *argv[max_args], *pos; int argc; setlinebuf(stdout); printf("\nInteractive mode\n\n"); using_history(); stifle_history(1000); do { cli_recv_pending(clif_conn, 0); alarm(1); cmd = readline("> "); alarm(0); if (!cmd) break; if (*cmd) add_history(cmd); argc = 1; pos = cmd; for (;;) { while (*pos == ' ') pos++; if (*pos == '\0') break; argv[argc] = pos; argc++; if (argc == max_args) break; while (*pos != '\0' && *pos != ' ') pos++; if (*pos == ' ') *pos++ = '\0'; } if (argc) { optind = 0; request(clif_conn, argc, argv); } free(cmd); } while (!cli_quit); } static void cli_terminate(UNUSED int sig) { cli_close_connection(); exit(0); } static void cli_alarm(UNUSED int sig) { if (clif_conn && _clif_command(clif_conn, "P", SHOW_NO_OUTPUT)) { printf("Connection to lldpad lost - trying to reconnect\n"); cli_close_connection(); } if (!clif_conn) { clif_conn = clif_open(); if (clif_conn) { char attach_str[9] = ""; u32 mod_id = LLDP_MOD_MAND; bin2hexstr((u8*)&mod_id, 4, attach_str, 8); printf("Connection to lldpad re-established\n"); if (clif_attach(clif_conn, attach_str) == 0) cli_attached = 1; else printf("Warning: Failed to attach to lldpad.\n"); } } if (clif_conn) cli_recv_pending(clif_conn, 1); alarm(1); } int main(int argc, char *argv[]) { int interactive = 1; int warning_displayed = 0; int ret = 0; if (argc > 1) interactive = 0; if (interactive) printf("%s\n\n%s\n\n", cli_version, cli_license); for (;;) { clif_conn = clif_open(); if (clif_conn) { if (warning_displayed) printf("Connection established.\n"); break; } if (!interactive) { perror("Failed to connect to lldpad - clif_open"); return -1; } if (!warning_displayed) { printf("Could not connect to lldpad - re-trying\n"); warning_displayed = 1; } sleep(1); continue; } init_modules(); signal(SIGINT, cli_terminate); signal(SIGTERM, cli_terminate); signal(SIGALRM, cli_alarm); if (interactive) { char attach_str[9] = ""; u32 mod_id = LLDP_MOD_MAND; bin2hexstr((u8*)&mod_id, 4, attach_str, 8); if (clif_attach(clif_conn, attach_str) == 0) cli_attached = 1; else printf("Warning: Failed to attach to lldpad.\n"); cli_interactive(); } else { ret = request(clif_conn, argc, &argv[0]); ret = !!ret; } cli_close_connection(); deinit_modules(); return ret; } lldpad-0.9.46/lldptool_cmds.c000066400000000000000000000315171215142747300160720ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include "clif.h" #include "dcb_types.h" #include "lldptool.h" #include "lldp_mand_clif.h" #include "lldptool_cli.h" #include "lldpad.h" #include "lldp.h" #include "lldp_mod.h" #include "messages.h" #include "lldp_util.h" #include "lldpad_status.h" static char *print_status(cmd_status status); static int render_cmd(struct cmd *cmd, int argc, char **args, char **argvals) { int len; int i; len = sizeof(cmd->obuf); /* all command messages begin this way */ snprintf(cmd->obuf, len, "%c%08x%c%1x%02x%08x%02x%s%02x", MOD_CMD, cmd->module_id, CMD_REQUEST, CLIF_MSG_VERSION, cmd->cmd, cmd->ops, (unsigned int) strlen(cmd->ifname), cmd->ifname, cmd->type); /* if the command is a tlv command, add the tlvid to the message */ if (cmd->cmd == cmd_gettlv || cmd->cmd == cmd_settlv) snprintf(cmd->obuf+strlen(cmd->obuf), len-strlen(cmd->obuf), "%08x", cmd->tlvid); /* add any args and argvals to the command message */ for (i = 0; i < argc; i++) { if (args[i]) snprintf(cmd->obuf+strlen(cmd->obuf), len-strlen(cmd->obuf), "%02x%s", (unsigned int)strlen(args[i]), args[i]); if (argvals[i]) snprintf(cmd->obuf+strlen(cmd->obuf), len-strlen(cmd->obuf), "%04x%s", (unsigned int)strlen(argvals[i]), argvals[i]); } return strlen(cmd->obuf); } int parse_response(char *buf) { return hex2int(buf+CLIF_STAT_OFF); } void get_arg_value(char *str, char **arg, char **argval) { unsigned int i; for (i = 0; i < strlen(str); i++) if (!isprint(str[i])) return; for (i = 0; i < strlen(str); i++) if (str[i] == '=') break; if (i < strlen(str)) { str[i] = '\0'; *argval = &str[i+1]; } *arg = str; } int cli_cmd_getstats(struct clif *clif, int argc, UNUSED char *argv[], struct cmd *cmd, int raw) { char **args; char **argvals; args = calloc(argc, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(argc, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } render_cmd(cmd, argc, args, argvals); free(args); free(argvals); return clif_command(clif, cmd->obuf, raw); } int cli_cmd_gettlv(struct clif *clif, int argc, char *argv[], struct cmd *cmd, int raw) { int numargs = 0; char **args; char **argvals; int i; args = calloc(argc, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(argc, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } for (i = 0; i < argc; i++) get_arg_value(argv[i], &args[i], &argvals[i]); numargs = i; /* default is local tlv query */ if (!(cmd->ops & op_neighbor)) cmd->ops |= op_local; if (numargs) { /* Only commands with the config option should have * arguments. */ if (!(cmd->ops & op_config)) { printf("%s\n", print_status(cmd_invalid)); goto out; } /* Commands to get neighbor TLVs cannot have * arguments. */ if (cmd->ops & op_neighbor) { printf("%s\n", print_status(cmd_invalid)); goto out; } cmd->ops |= op_arg; } for (i = 0; i < numargs; i++) { if (argvals[i]) { printf("%s\n", print_status(cmd_invalid)); goto out; } } render_cmd(cmd, argc, args, argvals); free(args); free(argvals); return clif_command(clif, cmd->obuf, raw); out: free(args); free(argvals); return cmd_invalid; } int cli_cmd_settlv(struct clif *clif, int argc, char *argv[], struct cmd *cmd, int raw) { int numargs = 0; char **args; char **argvals; int i; args = calloc(argc, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(argc, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } for (i = 0; i < argc; i++) get_arg_value(argv[i], &args[i], &argvals[i]); numargs = i; for (i = 0; i < numargs; i++) { if (!argvals[i]) { printf("%s\n", print_status(cmd_invalid)); goto out; } } if (numargs) cmd->ops |= (op_arg | op_argval); render_cmd(cmd, argc, args, argvals); free(args); free(argvals); return clif_command(clif, cmd->obuf, raw); out: free(args); free(argvals); return cmd_invalid; } int cli_cmd_getlldp(struct clif *clif, int argc, char *argv[], struct cmd *cmd, int raw) { int numargs = 0; int numargvals = 0; char **args; char **argvals; int i; args = calloc(argc, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(argc, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } for (i = 0; i < argc; i++) get_arg_value(argv[i], &args[i], &argvals[i]); numargs = i; for (i = 0; i < numargs; i++) if (argvals[i]) numargvals++; if (!numargs || numargvals) { printf("%s\n", print_status(cmd_invalid)); goto out; } if (numargs) cmd->ops |= op_arg; render_cmd(cmd, argc, args, argvals); free(args); free(argvals); return clif_command(clif, cmd->obuf, raw); out: free(args); free(argvals); return cmd_invalid; } int cli_cmd_setlldp(struct clif *clif, int argc, char *argv[], struct cmd *cmd, int raw) { int numargs = 0; char **args; char **argvals; int i; args = calloc(argc, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(argc, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } for (i = 0; i < argc; i++) get_arg_value(argv[i], &args[i], &argvals[i]); numargs = i; for (i = 0; i < numargs; i++) { if (!argvals[i]) { printf("%s\n", print_status(cmd_invalid)); goto out; } } if (numargs) cmd->ops |= (op_arg | op_argval); render_cmd(cmd, argc, args, argvals); free(args); free(argvals); return clif_command(clif, cmd->obuf, raw); out: free(args); free(argvals); return cmd_invalid; } static char *print_status(cmd_status status) { char *str; switch(status) { case cmd_success: str = "Successful"; break; case cmd_failed: str = "Failed"; break; case cmd_device_not_found: str = "Device not found or inactive"; break; case cmd_agent_not_found: str = "Agent instance for device not found"; break; case cmd_invalid: str = "Invalid command"; break; case cmd_bad_params: str = "Invalid parameters"; break; case cmd_peer_not_present: str = "Peer feature not present"; break; case cmd_ctrl_vers_not_compatible: str = "Version not compatible"; break; case cmd_not_capable: str = "Device not capable"; break; case cmd_not_applicable: str = "Command not applicable"; break; case cmd_no_access: str = "Access denied"; break; default: str = "Unknown status"; break; } return str; } u32 get_tlvid(u16 tlv_type, char* ibuf) { u32 tlvid; if (tlv_type < 127) { tlvid = tlv_type; } else { hexstr2bin(ibuf, (u8 *)&tlvid, sizeof(tlvid)); tlvid = ntohl(tlvid); } return tlvid; } static int print_arg_value(char *ibuf) { int ioff = 0; char **args; char **argvals; int numargs; int i, offset; int ilen = strlen(ibuf); /* count args and argvalus */ offset = ioff; for (numargs = 0; (ilen - offset) > 2; numargs++) { offset += 2; if (ilen - offset > 0) { offset++; if (ilen - offset > 4) offset += 4; } } args = calloc(numargs, sizeof(char *)); if (!args) return cmd_failed; argvals = calloc(numargs, sizeof(char *)); if (!argvals) { free(args); return cmd_failed; } numargs = get_arg_val_list(ibuf, ilen, &ioff, args, argvals); for (i = 0; i < numargs; i++) { printf("%s", args[i]); printf("=%s\n", argvals[i]); } free(args); free(argvals); return ioff; } static void print_lldp(char *ibuf) { print_arg_value(ibuf); } static void print_tlvs(struct cmd *cmd, char *ibuf) { unsigned int ilen; u16 tlv_type; u16 tlv_len; u32 tlvid; int offset = 0; int printed; struct lldp_module *np; ilen = strlen(ibuf); if (cmd->ops & op_config) offset = print_arg_value(ibuf); ilen = strlen(ibuf + offset); while (ilen > 0) { tlv_len = 2*sizeof(u16); if (ilen < 2*sizeof(u16)) { printf("corrupted TLV ilen=%d, tlv_len=%d\n", ilen, tlv_len); break; } hexstr2bin(ibuf+offset, (u8 *)&tlv_type, sizeof(tlv_type)); tlv_type = ntohs(tlv_type); tlv_len = tlv_type; tlv_type >>= 9; tlv_len &= 0x01ff; offset += 2*sizeof(u16); ilen -= 2*sizeof(u16); if (ilen < (unsigned) 2*tlv_len) { printf("corrupted TLV ilen = %d, tlv_len=%d\n", ilen, tlv_len); break; } tlvid = get_tlvid(tlv_type, ibuf+offset); if (tlvid > INVALID_TLVID) offset += 8; printed = 0; LIST_FOREACH(np, &lldp_head, lldp) { if (np->ops->print_tlv) if (np->ops->print_tlv(tlvid, tlv_len, ibuf+offset)) { printed = 1; break; } } if (!printed) { if (tlvid < INVALID_TLVID) printf("Unidentified TLV\n\ttype:%d %*.*s\n", tlv_type, tlv_len*2, tlv_len*2, ibuf+offset); else printf("Unidentified Org Specific TLV\n\t" "OUI: 0x%06x, Subtype: %d, Info: %*.*s\n", tlvid >> 8, tlvid & 0x0ff, tlv_len*2-8, tlv_len*2-8, ibuf+offset); } if (tlvid > INVALID_TLVID) offset += (2*tlv_len - 8); else offset += 2*tlv_len; ilen -= 2*tlv_len; if (tlvid == END_OF_LLDPDU_TLV) break; } } static void print_port_stats(char *ibuf) { static char *stat_names[] = { "Total Frames Transmitted ", "Total Discarded Frames Received", "Total Error Frames Received ", "Total Frames Received ", "Total Discarded TLVs ", "Total Unrecognized TLVs ", "Total Ageouts ", "" }; int i; int offset = 0; u32 value; for(i = 0; strlen(stat_names[i]); i++) { hexstr2bin(ibuf+offset, (u8 *)&value, sizeof(value)); value = ntohl(value); printf("%s = %u\n", stat_names[i], value); offset += 8; } } void print_cmd_response(char *ibuf, int status) { struct cmd cmd; u8 len; int ioff; if (status != cmd_success) { printf("%s %s\n", print_status(status), ibuf + CMD_IF + CMD_IF_LEN); return; } hexstr2bin(ibuf+CMD_CODE, (u8 *)&cmd.cmd, sizeof(cmd.cmd)); hexstr2bin(ibuf+CMD_OPS, (u8 *)&cmd.ops, sizeof(cmd.ops)); cmd.ops = ntohl(cmd.ops); hexstr2bin(ibuf+CMD_IF_LEN, &len, sizeof(len)); ioff = CMD_IF; if (len < sizeof(cmd.ifname)) { memcpy(cmd.ifname, ibuf+CMD_IF, len); } else { printf("Response ifname too long: %*s\n", (int)len, cmd.ifname); return; } cmd.ifname[len] = '\0'; ioff += len; if (cmd.cmd == cmd_gettlv || cmd.cmd == cmd_settlv) { hexstr2bin(ibuf+ioff, (u8 *)&cmd.tlvid, sizeof(cmd.tlvid)); cmd.tlvid = ntohl(cmd.tlvid); ioff += 2*sizeof(cmd.tlvid); } switch (cmd.cmd) { case cmd_getstats: print_port_stats(ibuf + ioff); break; case cmd_gettlv: print_tlvs(&cmd, ibuf+ioff); break; case cmd_get_lldp: print_lldp(ibuf + ioff); break; case cmd_settlv: case cmd_set_lldp: printf("%s", ibuf+ioff); break; default: return; } } void print_response(char *buf, int status) { switch(buf[CLIF_RSP_OFF]) { case PING_CMD: if (status) printf("FAILED:%s\n", print_status(status)); else printf("%s\n", buf+CLIF_RSP_OFF+5); break; case ATTACH_CMD: case DETACH_CMD: case LEVEL_CMD: if (status) printf("FAILED:%s\n", print_status(status)); else printf("OK\n"); break; case CMD_REQUEST: print_cmd_response(buf+CLIF_RSP_OFF, status); break; default: printf("Unknown LLDP command response: %s\n", buf); break; } return; } void print_event_msg(char *buf) { int level; printf("\nEvent Message\n"); level = buf[EV_LEVEL_OFF] & 0x0f; printf("Level: \t"); switch (level) { case MSG_MSGDUMP: printf("DUMP\n"); break; case MSG_DEBUG: printf("DEBUG\n"); break; case MSG_INFO: printf("INFO\n"); break; case MSG_WARNING: printf("WARNING\n"); break; case MSG_ERROR: printf("ERROR\n"); break; case MSG_EVENT: printf("LLDP EVENT\n"); break; default: printf("Unknown event message: %d", level); return; } if (level != MSG_EVENT) { printf("Message: \t%s\n\n", buf+EV_GENMSG_OFF); return; } else { int cmd = atoi(&buf[EV_GENMSG_OFF]); switch (cmd) { case LLDP_RCHANGE: printf("Message: \tRemote Change\n"); break; default: printf("Message: \tUnknown Event Command\n"); break; } } } lldpad-0.9.46/log.c000066400000000000000000000035201215142747300140050ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include #include #include #include #include "messages.h" /* * Prepend each entry with a time stamp. */ static void showtime(void) { struct timeval tv; struct tm now; if (!gettimeofday(&tv, NULL)) { localtime_r(&tv.tv_sec, &now); printf("%02d:%02d:%02d.%06ld ", now.tm_hour, now.tm_min, now.tm_sec, tv.tv_usec); } } /* Helper macros for handling struct os_time */ void log_message(int level, const char *format, ...) { static int bypass_time; va_list va, vb; va_start(va, format); va_copy(vb, va); if (daemonize) vsyslog(level, format, vb); else if (loglvl >= level) { if (!bypass_time) showtime(); vprintf(format, vb); bypass_time = strchr(format, '\n') == 0; } va_end(va); } lldpad-0.9.46/parse_cli.l000066400000000000000000000463161215142747300152100ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ %option nounput %option noinput %{ #include #include #include #include #include #include "lldp_dcbx_cmds.h" #include "dcb_types.h" #include "parse_cli.h" #define BADARG "bad argument: " static int cmd=0; static int feature=0; static int subtype=0; static int error=0; static char *port_id = NULL; static char *badarg = NULL; static int got_args = 0; static int dcb_state = -1; static int dcbx_version = -1; static int advertise = 0x0f; static int enable = 0x0f; static int willing = 0x0f; static int up2tc_a[MAX_USER_PRIORITIES]; static int up2tc_idx = 0; static int pgpct_a[MAX_BANDWIDTH_GROUPS]; static int pgpct_idx = 0; static int pgid_a[MAX_USER_PRIORITIES]; static int pgid_idx = 0; static int uppct_a[MAX_USER_PRIORITIES]; static int uppct_idx = 0; static int strict_a[MAX_USER_PRIORITIES]; static int strict_idx = 0; static int pfcup_a[MAX_USER_PRIORITIES]; static int pfcup_idx = 0; static int appsubtype = -1; static char *appcfg_p = NULL; static int llsubtype = -1; static int llstatus = -1; static int desc_id = 0; static char *desc_str = NULL; static int fargs = 0; %} %option noyywrap %x dcb %x dcbx %x pg %x pfc %x app %x ll %x up2tc %x pgpct %x pgid %x uppct %x strict %x getport %x getfeature %x getdcbargs %x getdcbxargs %x getdcbxver %x getpfcargs %x getpgargs %x getpgdescargs %x getpgdesc %x getappargs %x getllargs %x getadvertise %x getenable %x getwilling %x getup2tc %x getpgpct %x getpgid %x getuppct %x getstrict %x getpfcup %x getappsubtype %x getappcfg %x getllsubtype %x getllstatus %x cmddone %% [ \\t]+ gc { if (!cmd) { cmd = CMD_GET_CONFIG; } else { if (!badarg) badarg = strdup(yytext); error = 1; } BEGIN(getport); } sc { if (!cmd) { cmd = CMD_SET_CONFIG; } else { if (!badarg) badarg = strdup(yytext); error = 1; } BEGIN(getport); } go { if (!cmd) { cmd = CMD_GET_OPER; } else { if (!badarg) badarg = strdup(yytext); error = 1; } BEGIN(getport); } gp { if (!cmd) { cmd = CMD_GET_PEER; } else { if (!badarg) badarg = strdup(yytext); error = 1; } BEGIN(getport); } [[:alnum:][:punct:][:cntrl:]]+ { if (!badarg) badarg = strdup(yytext); error = 1; } [ \\t] dcbx { feature = FEATURE_DCBX; /* 'dcbx' is a global feature which does not * require a port name. */ if (cmd == CMD_SET_CONFIG) { BEGIN(getdcbxargs); } else if (cmd != CMD_GET_PEER) { BEGIN(cmddone); } else { error = 1; if (!badarg) badarg = strdup("invalid command"); } } [a-zA-Z0-9_.:-]+ { port_id = strdup(yytext); BEGIN(getfeature); } [ \\t] dcb { feature = FEATURE_DCB; if (cmd == CMD_SET_CONFIG) BEGIN(getdcbargs); else BEGIN(cmddone); } pfc { feature = FEATURE_PFC; if (cmd == CMD_SET_CONFIG) BEGIN(getpfcargs); else BEGIN(cmddone); } pg { feature = FEATURE_PG; if (cmd == CMD_SET_CONFIG) BEGIN(getpgargs); else BEGIN(cmddone); } pgdesc { feature = FEATURE_PG_DESC; BEGIN(getpgdescargs); } app { feature = FEATURE_APP; BEGIN(getappsubtype); } ll { feature = FEATURE_LLINK; BEGIN(getllsubtype); } [a-zA-Z0-9]+ { if (!badarg) badarg = strdup(yytext); error = 1; } [ \\t] v: { BEGIN(getdcbxver); } [1256] { dcbx_version = atoi(yytext); BEGIN(cmddone); } cee { dcbx_version = dcbx_subtype2; BEGIN(cmddone); } cin { dcbx_version = dcbx_subtype1; BEGIN(cmddone); } force-cee { dcbx_version = dcbx_force_subtype2; BEGIN(cmddone); } force-cin { dcbx_version = dcbx_force_subtype1; BEGIN(cmddone); } [ \\t] on { dcb_state = 1; BEGIN(cmddone); } off { dcb_state = 0; BEGIN(cmddone); } [1-8] { desc_id = *yytext & 0x0f; if (cmd == CMD_SET_CONFIG) BEGIN(getpgdesc); else BEGIN(cmddone); } [ \\t]+ [[:alnum:][:punct:]]+[[:alnum:][:punct:] \\t]+[[:alnum:][:punct:]]+ { desc_str = strdup(yytext); BEGIN(cmddone); } [[:cntrl:]] { error = 1; if (!badarg) badarg = strdup(yytext); } a: { if (got_args & GOT_ADVERTISE) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_ADVERTISE; BEGIN(getadvertise); } } e: { if (got_args & GOT_ENABLE) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_ENABLE; BEGIN(getenable); } } w: { if (got_args & GOT_WILLING) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_WILLING; BEGIN(getwilling); } } [01] { advertise = atoi(yytext); switch (feature) { case FEATURE_PG: BEGIN(getpgargs); break; case FEATURE_PFC: BEGIN(getpfcargs); break; case FEATURE_APP: BEGIN(getappargs); break; case FEATURE_LLINK: BEGIN(getllargs); break; default: if (!badarg) badarg = strdup("invalid feature"); error = 1; } } [01] { enable = atoi(yytext); switch (feature) { case FEATURE_PG: BEGIN(getpgargs); break; case FEATURE_PFC: BEGIN(getpfcargs); break; case FEATURE_APP: BEGIN(getappargs); break; case FEATURE_LLINK: BEGIN(getllargs); break; default: if (!badarg) badarg = strdup("invalid feature"); error = 1; } } [01] { willing = atoi(yytext); switch (feature) { case FEATURE_PG: BEGIN(getpgargs); break; case FEATURE_PFC: BEGIN(getpfcargs); break; case FEATURE_APP: BEGIN(getappargs); break; case FEATURE_LLINK: BEGIN(getllargs); break; default: if (!badarg) badarg = strdup("invalid feature"); error = 1; } } [2-9[:alpha:][:punct:][:cntrl:] \\t] { error = 1; if (!badarg) badarg = strdup(yytext); } up2tc: { if (got_args & GOT_UP2TC) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_UP2TC; BEGIN(getup2tc); } } pgpct: { if (got_args & GOT_PGPCT) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_PGPCT; BEGIN(getpgpct); } } pgid: { if (got_args & GOT_PGID) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_PGID; BEGIN(getpgid); } } uppct: { if (got_args & GOT_UPPCT) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_UPPCT; BEGIN(getuppct); } } strict: { if (got_args & GOT_STRICT) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_STRICT; BEGIN(getstrict); } } pfcup: { if (got_args & GOT_PFCUP) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_PFCUP; BEGIN(getpfcup); } } :[0-9]+ { appsubtype = atoi(yytext+1); if (appsubtype >= DCB_MAX_APPTLV) { appsubtype = -1; if (!badarg) badarg = strdup("invalid subtype"); error = 1; } else if (cmd == CMD_SET_CONFIG) { BEGIN(getappargs); } else BEGIN(cmddone); } :[fF][cC][oO][eE][ \\t] { appsubtype = 0; if (cmd == CMD_SET_CONFIG) BEGIN(getappargs); else BEGIN(cmddone); } :[iI][sS][cC][sC][iI][ \\t] { appsubtype = 1; if (cmd == CMD_SET_CONFIG) BEGIN(getappargs); else BEGIN(cmddone); } :[fF][iI][pP][ \\t] { appsubtype = 2; if (cmd == CMD_SET_CONFIG) BEGIN(getappargs); else BEGIN(cmddone); } appcfg: { if (got_args & GOT_APPCFG) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_APPCFG; BEGIN(getappcfg); } } :0 { llsubtype = atoi(yytext+1); if (cmd == CMD_SET_CONFIG) BEGIN(getllargs); else BEGIN(cmddone); } :[fF][cC][oO][eE][ \\t] { llsubtype = 0; if (cmd == CMD_SET_CONFIG) BEGIN(getllargs); else BEGIN(cmddone); } status: { if (got_args & GOT_LLSTATUS) { if (!badarg) badarg = strdup("duplicate argument"); error = 1; } else { got_args |= GOT_LLSTATUS; BEGIN(getllstatus); } } [01] { llstatus = atoi(yytext); BEGIN(getllargs); } [[:alnum:][:punct:][:cntrl:]] { BEGIN(cmddone); yyless(0); } [0-7] { up2tc_a[up2tc_idx++] = atoi(yytext); if (up2tc_idx == 8) { BEGIN(getpgargs); } } [, \\t]+ [0-9]+ { pgpct_a[pgpct_idx] = atoi(yytext); if (pgpct_a[pgpct_idx] > 100) { if (!badarg) badarg = strdup("invalid percent value"); error = 1; } else { pgpct_idx++; if (pgpct_idx == 8) { BEGIN(getpgargs); } } } [0-9]+ { uppct_a[uppct_idx] = atoi(yytext); if (uppct_a[uppct_idx] > 100) { if (!badarg) badarg = strdup("invalid percent value"); error = 1; } else { uppct_idx++; if (uppct_idx == 8) { BEGIN(getpgargs); } } } [a-zA-Z0-9]+ { BEGIN(getpgargs); } [0-7fF] { pgid_a[pgid_idx++] = strtol(yytext, NULL, 16); if (pgid_idx == 8) { BEGIN(getpgargs); } } [01] { strict_a[strict_idx++] = atoi(yytext); if (strict_idx == 8) { BEGIN(getpgargs); } } [01] { pfcup_a[pfcup_idx++] = atoi(yytext); if (pfcup_idx == 8) { BEGIN(getpfcargs); } } [0-9a-fA-F]{2} { appcfg_p = strdup(yytext); BEGIN(getappargs); } . { error = 1; if (!badarg) badarg = strdup(yytext); } [^[:blank:]]+ { if (!badarg) badarg = strdup(yytext); error = 1; } <> { BEGIN(INITIAL); yyterminate(); } %% int get_port_len() { if (port_id) return strlen(port_id); else return 0; } int *get_up2tc() { return &up2tc_a[0]; } int *get_pgpct() { return &pgpct_a[0]; } int *get_pgid() { return &pgid_a[0]; } int *get_uppct() { return &uppct_a[0]; } int *get_strict() { return &strict_a[0]; } int *get_pfcup() { return &pfcup_a[0]; } char *get_appcfg() { return appcfg_p; } int get_enable() { return enable; } int get_dcb_param() { return dcb_state; } int get_dcbx_param() { return dcbx_version; } int get_advertise() { return advertise; } int get_willing() { return willing; } int get_cmd() { return cmd; } int get_fargs() { return fargs; } int get_feature() { return feature; } int get_subtype() { return subtype; } int get_desc_id() { return desc_id; } char *get_desc_str() { return desc_str; } void free_desc_str() { if (desc_str) { free(desc_str); desc_str = NULL; } } char *get_port() { return port_id; } int get_llstatus() { return llstatus; } void free_port() { if (port_id) { free(port_id); port_id = NULL; } } void free_appcfg() { if (appcfg_p) { free(appcfg_p); appcfg_p = NULL; } } char *get_parse_error() { return badarg; } void free_parse_error() { if (badarg) { free(badarg); badarg = NULL; } } void init_parse_state() { cmd=0; feature=0; error=0; free_parse_error(); free_port(); free_appcfg(); free_desc_str(); dcb_state = -1; dcbx_version = -1; advertise = 0xf; enable = 0xf; willing = 0xf; up2tc_idx = 0; pgpct_idx = 0; pgid_idx = 0; uppct_idx = 0; strict_idx = 0; pfcup_idx = 0; appsubtype = -1; llsubtype = -1; llstatus = -1; desc_id = 0; got_args = 0; fargs = 0; } int parse_dcb_cmd(char *buf) { int i; int pargs = 1; YY_BUFFER_STATE yyhnd; yyout = fopen("/dev/null", "a+"); yyhnd = yy_scan_string(buf); yylex(); yy_delete_buffer(yyhnd); if (error) { return error; } switch (cmd) { case CMD_GET_CONFIG: case CMD_GET_OPER: case CMD_GET_PEER: if (!feature) { if (!badarg) badarg = strdup("feature was not specified"); error = 1; } if (!port_id && feature != FEATURE_DCBX) { if (!badarg) badarg = strdup("port was not specified"); error = 1; } switch (feature) { case FEATURE_DCB: case FEATURE_DCBX: if (cmd == CMD_GET_PEER) { if (!badarg) badarg = strdup("invalid command"); error = 1; break; } /* else fall thru */ case FEATURE_PG: case FEATURE_PFC: subtype = 0; break; case FEATURE_APP: if (appsubtype >= 0) { subtype = appsubtype; } else { subtype = 0; if (!badarg) badarg = strdup("subtype was not " "specified"); error = 1; } break; case FEATURE_LLINK: if (llsubtype >= 0) { subtype = llsubtype; } else { subtype = 0; if (!badarg) badarg = strdup("subtype was not " "specified"); error = 1; } break; case FEATURE_PG_DESC: if (!desc_id) { desc_id = 0; if (!badarg) badarg = strdup("pgid was not " "specified"); error = 1; } if (cmd != CMD_GET_CONFIG) { if (!badarg) badarg = strdup("invalid command"); error = 1; } subtype = 0; break; default: if (!badarg) badarg = strdup("invalid feature"); error = 1; break; } break; case CMD_SET_CONFIG: if (!port_id && feature != FEATURE_DCBX) { if (!badarg) badarg = strdup("port was not specified"); error = 1; break; } /* make sure all command components were provided */ if (advertise == 0x0f && willing == 0x0f && enable == 0x0f) { pargs = 0; } switch (feature) { case FEATURE_DCB: fargs = (dcb_state > -1); if (fargs == 0) { if (!badarg) badarg = strdup("no dcb arg supplied"); error = 1; } break; case FEATURE_DCBX: fargs = (dcbx_version > -1); if (fargs == 0) { if (!badarg) badarg = strdup("no dcbx arg supplied"); error = 1; } break; case FEATURE_PG: /* check if pg attributes were included and flag error * if an incomplete set was entered. */ fargs = up2tc_idx + strict_idx + pgpct_idx + uppct_idx + pgid_idx; if (fargs == 0 && pargs == 0) { if (!badarg) badarg = strdup("no pg args supplied"); error = 1; break; } if (up2tc_idx == 0) { for (i = 0; i < MAX_USER_PRIORITIES; i++) if (fargs) up2tc_a[i] = -1; } else if (up2tc_idx < MAX_USER_PRIORITIES) { if (!badarg) badarg = strdup("incomplete up2tc " "argument"); error = 1; break; } if (pgid_idx == 0) { for (i = 0; i < MAX_USER_PRIORITIES; i++) if (fargs) pgid_a[i] = -1; } else if (pgid_idx < MAX_USER_PRIORITIES) { if (!badarg) badarg = strdup("incomplete pgid " "argument"); error = 1; break; } if (pgpct_idx == 0) { for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) if (fargs) pgpct_a[i] = -1; } else if (pgpct_idx < MAX_BANDWIDTH_GROUPS) { if (!badarg) badarg = strdup("incomplete pgpct " "argument"); error = 1; break; } if (uppct_idx == 0) { for (i = 0; i < MAX_USER_PRIORITIES; i++) if (fargs) uppct_a[i] = -1; } else if (uppct_idx < MAX_USER_PRIORITIES) { if (!badarg) badarg = strdup("incomplete uppct " "argument"); error = 1; break; } if (strict_idx == 0) { for (i=0; i= MAX_DESCRIPTION_LEN) { if (!badarg) badarg = strdup("bwg description " "length too long (max 99)"); error = 1; } break; default: if (!badarg) badarg = strdup("invalid feature"); error = 1; break; } break; default: badarg = strdup("invalid DCB command"); error = 1; } return error; } lldpad-0.9.46/qbg/000077500000000000000000000000001215142747300136315ustar00rootroot00000000000000lldpad-0.9.46/qbg/lldp_ecp22.c000066400000000000000000000670641215142747300157400ustar00rootroot00000000000000/****************************************************************************** Implementation of ECP according to 802.1Qbg (c) Copyright IBM Corp. 2010, 2012 Author(s): Jens Osterkamp Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include #include #include "eloop.h" #include "lldp_ecp22.h" #include "messages.h" #include "lldp_qbg_utils.h" #include "lldp/l2_packet.h" #include "lldp_tlv.h" #define ECP22_MAX_RETRIES_DEFAULT (3) /* Default # of max retries */ #define ECP22_ACK_TIMER_STOPPED (-1) /* * Defaults to 2ms wait time for acknowledgement packet reception. */ #define ECP22_ACK_TIMER_DEFAULT (8) static void ecp22_tx_run_sm(struct ecp22 *); static const char *const ecp22_rx_states[] = { /* Receive states verbatim */ "ECP22_RX_BEGIN", "ECP22_RX_WAIT", "ECP22_RX_WAIT2", "ECP22_RX_FIRST", "ECP22_RX_REC_ECPDU", "ECP22_RX_NEW_ECPDU", "ECP22_RX_SEND_ACK" }; static const char *const ecp22_tx_states[] = { /* Transmit states verbatim */ "ECP22_TX_BEGIN", "ECP22_TX_INIT", "ECP22_TX_TXMIT_ECPDU", "ECP22_TX_WAIT_FORREQ", "ECP22_TX_WAIT_ONDATA", "ECP22_TX_ERROR" }; /* * Increment sequence number. Do not return zero as sequence number. */ static unsigned short inc_seqno(unsigned short x) { ++x; if (!x) /* Wrapped */ ++x; return x; } /* * Find the ecp data associated with an interface. * Return pointer or NULL if not found. */ static struct ecp22 *find_ecpdata(char *ifname, struct ecp22_user_data *eud) { struct ecp22 *ecp = 0; if (eud) { LIST_FOREACH(ecp, &eud->head, node) if (!strncmp(ifname, ecp->ifname, IFNAMSIZ)) break; } return ecp; } /* * ecp22_txframe - transmit ecp frame * @ecp: pointer to currently used ecp data structure * * returns the number of characters sent on success, -1 on failure * * sends out the frame stored in the frame structure using l2_packet_send. */ static int ecp22_txframe(struct ecp22 *ecp, char *txt, unsigned char *dst, unsigned char *ack, size_t len) { hexdump_frame(ecp->ifname, txt, ack, len); return l2_packet_send(ecp->l2, dst, htons(ETH_P_ECP22), ack, len); } /* * Append some data at the end of the transmit data buffer. Make sure the * End TLV always fits into the buffer. */ static unsigned char end_tlv[2] = { 0x0, 0x0 }; /* END TLV */ static void ecp22_append(u8 *buffer, u32 *pos, void *data, u32 len) { if (*pos + len > ETH_FRAME_LEN - sizeof end_tlv) return; memcpy(buffer + *pos, data, len); *pos += len; } /* * Return a payload node to the freelist. */ void ecp22_putnode(struct ecp22_freelist *list, struct ecp22_payload_node *elm) { elm->ptlv = free_pkd_tlv(elm->ptlv); if (list->freecnt > ecp22_maxpayload) free(elm); else { ++list->freecnt; LIST_INSERT_HEAD(&list->head, elm, node); } } /* * ecp22_build_ecpdu - create an ecp protocol data unit * @ecp: pointer to currently used ecp data structure * * returns true on success, false on failure * * creates the frame header with the ports mac address, the ecp header with REQ * plus a packed TLVs created taken from the send queue. */ static bool ecp22_build_ecpdu(struct ecp22 *ecp) { struct l2_ethhdr eth; struct ecp22_hdr ecph; u32 fb_offset = 0; struct packed_tlv *ptlv; struct ecp22_payload_node *p = LIST_FIRST(&ecp->inuse.head); if (!p) return false; ecp->tx.ecpdu_received = true; /* Txmit buffer in use */ memcpy(eth.h_dest, p->mac, ETH_ALEN); l2_packet_get_own_src_addr(ecp->l2, eth.h_source); eth.h_proto = htons(ETH_P_ECP22); memset(ecp->tx.frame, 0, sizeof ecp->tx.frame); ecp22_append(ecp->tx.frame, &fb_offset, (void *)ð, sizeof eth); ecp22_hdr_set_version(&ecph, 1); ecp22_hdr_set_op(&ecph, ECP22_REQUEST); ecp22_hdr_set_subtype(&ecph, ECP22_VDP); ecph.ver_op_sub = htons(ecph.ver_op_sub); ecph.seqno = htons(ecp->tx.seqno); ecp22_append(ecp->tx.frame, &fb_offset, (void *)&ecph, sizeof ecph); ptlv = p->ptlv; ecp22_append(ecp->tx.frame, &fb_offset, ptlv->tlv, ptlv->size); ecp22_append(ecp->tx.frame, &fb_offset, end_tlv, sizeof end_tlv); ecp->tx.frame_len = MAX(fb_offset, (unsigned)ETH_ZLEN); LIST_REMOVE(p, node); ecp22_putnode(&ecp->isfree, p); LLDPAD_DBG("%s:%s seqno %#hx frame_len %#hx\n", __func__, ecp->ifname, ecp->tx.seqno, ecp->tx.frame_len); return true; } /* * Execute transmit state transmitECPDU. */ static void ecp22_es_waitforreq(struct ecp22 *ecp) { ecp->tx.retries = 0; ecp->tx.ack_received = false; ecp->tx.ecpdu_received = false; ecp->tx.seqno = inc_seqno(ecp->tx.seqno); LLDPAD_DBG("%s:%s seqno %#hx\n", __func__, ecp->ifname, ecp->tx.seqno); } /* * Execute transmit state countErrors. */ static void ecp22_es_counterror(struct ecp22 *ecp) { ++ecp->tx.errors; LLDPAD_DBG("%s:%s errors %lu\n", __func__, ecp->ifname, ecp->tx.errors); } /* * Execute transmit state initTransmit. */ static void ecp22_es_inittransmit(struct ecp22 *ecp) { ecp->tx.errors = 0; ecp->tx.seqno = 0; } /* * Return RTE value in milliseconds. */ static int rtevalue(unsigned char rte) { return (1 << rte) * 10; } /* * ecp22_ack_timeout_handler - handles the ack timer expiry * @eloop_data: data structure of event loop * @user_ctx: user context, vdp_data here * * no return value * * called when the ECP timer has expired. Calls the ECP station state machine. */ static void ecp22_ack_timeout_handler(UNUSED void *eloop_data, void *user_ctx) { struct ecp22 *ecp = (struct ecp22 *)user_ctx; LLDPAD_DBG("%s:%s retries:%d\n", __func__, ecp->ifname, ecp->tx.retries); ecp22_tx_run_sm(ecp); } /* * ecp22_tx_start_acktimer - starts the ECP ack timer * @ecp: pointer to currently used ecp data structure * * returns 0 on success, -1 on error * * starts the ack timer when a frame has been sent out. */ static void ecp22_tx_start_acktimer(struct ecp22 *ecp) { unsigned long ack_sec = rtevalue(ecp->max_rte) / 1000000; unsigned long ack_usec = rtevalue(ecp->max_rte) % 1000000; LLDPAD_DBG("%s:%s [%ld.%06ld]\n", __func__, ecp->ifname, ack_sec, ack_usec); eloop_register_timeout(ack_sec, ack_usec, ecp22_ack_timeout_handler, 0, (void *)ecp); } /* * ecp22_tx_change_state - changes the ecp tx sm state * @ecp: pointer to currently used ecp data structure * @newstate: new state for the sm * * no return value * * checks state transistion for consistency and finally changes the state of * the profile. */ static void ecp22_tx_change_state(struct ecp22 *ecp, unsigned char newstate) { switch (newstate) { case ECP22_TX_BEGIN: break; case ECP22_TX_INIT: assert(ecp->tx.state == ECP22_TX_BEGIN); break; case ECP22_TX_WAIT_FORREQ: assert(ecp->tx.state == ECP22_TX_INIT || ecp->tx.state == ECP22_TX_ERROR || ecp->tx.state == ECP22_TX_TXMIT_ECPDU); break; case ECP22_TX_WAIT_ONDATA: assert(ecp->tx.state == ECP22_TX_WAIT_FORREQ); break; case ECP22_TX_TXMIT_ECPDU: assert(ecp->tx.state == ECP22_TX_WAIT_ONDATA); break; case ECP22_TX_ERROR: assert(ecp->tx.state == ECP22_TX_TXMIT_ECPDU); break; default: LLDPAD_ERR("%s: ECP TX state machine invalid state %d\n", ecp->ifname, newstate); } LLDPAD_DBG("%s:%s state change %s -> %s\n", __func__, ecp->ifname, ecp22_tx_states[ecp->tx.state], ecp22_tx_states[newstate]); ecp->tx.state = newstate; } /* * Send the payload data. */ static int ecp22_es_txmit(struct ecp22 *ecp) { int rc = 0; ++ecp->tx.retries; ecp22_txframe(ecp, "ecp-out", ecp->tx.frame, ecp->tx.frame, ecp->tx.frame_len); ecp22_tx_start_acktimer(ecp); return rc; } /* * ecp22_set_tx_state - sets the ecp tx state machine state * @ecp: pointer to currently used ecp data structure * * returns true or false * * switches the state machine to the next state depending on the input * variables. returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool ecp22_set_tx_state(struct ecp22 *ecp) { struct port *port = port_find_by_name(ecp->ifname); if (!port) { LLDPAD_ERR("%s:%s port not found\n", __func__, ecp->ifname); return 0; } if ((port->portEnabled == false) && (port->prevPortEnabled == true)) { LLDPAD_ERR("%s:%s port was disabled\n", __func__, ecp->ifname); ecp22_tx_change_state(ecp, ECP22_TX_BEGIN); } port->prevPortEnabled = port->portEnabled; switch (ecp->tx.state) { case ECP22_TX_BEGIN: ecp22_tx_change_state(ecp, ECP22_TX_INIT); return true; case ECP22_TX_INIT: ecp22_tx_change_state(ecp, ECP22_TX_WAIT_FORREQ); return true; case ECP22_TX_WAIT_FORREQ: ecp22_tx_change_state(ecp, ECP22_TX_WAIT_ONDATA); return true; case ECP22_TX_WAIT_ONDATA: if (LIST_FIRST(&ecp->inuse.head)) { /* Data to send */ ecp22_build_ecpdu(ecp); ecp22_tx_change_state(ecp, ECP22_TX_TXMIT_ECPDU); return true; } return false; case ECP22_TX_TXMIT_ECPDU: if (ecp->tx.ack_received) { ecp22_tx_change_state(ecp, ECP22_TX_WAIT_FORREQ); return true; } if (ecp->tx.retries > ecp->max_retries) { ecp22_tx_change_state(ecp, ECP22_TX_ERROR); return true; } return false; case ECP22_TX_ERROR: ecp22_tx_change_state(ecp, ECP22_TX_WAIT_FORREQ); return true; default: LLDPAD_ERR("%s: ECP TX state machine in invalid state %d\n", ecp->ifname, ecp->tx.state); return false; } } /* * ecp22_tx_run_sm - state machine for ecp transmit * @ecp: pointer to currently used ecp data structure * * no return value */ static void ecp22_tx_run_sm(struct ecp22 *ecp) { ecp22_set_tx_state(ecp); do { LLDPAD_DBG("%s:%s state %s\n", __func__, ecp->ifname, ecp22_tx_states[ecp->tx.state]); switch (ecp->tx.state) { case ECP22_TX_BEGIN: break; case ECP22_TX_INIT: ecp22_es_inittransmit(ecp); break; case ECP22_TX_WAIT_FORREQ: ecp22_es_waitforreq(ecp); break; case ECP22_TX_WAIT_ONDATA: break; case ECP22_TX_TXMIT_ECPDU: ecp22_es_txmit(ecp); break; case ECP22_TX_ERROR: ecp22_es_counterror(ecp); break; } } while (ecp22_set_tx_state(ecp) == true); } /* * ecp22_rx_change_state - changes the ecp rx sm state * @ecp: pointer to currently used ecp data structure * @newstate: new state for the sm * * no return value * * checks state transistion for consistency and finally changes the state of * the ecp receive buffer. */ static void ecp22_rx_change_state(struct ecp22 *ecp, u8 newstate) { switch (newstate) { case ECP22_RX_BEGIN: break; case ECP22_RX_WAIT: assert(ecp->rx.state == ECP22_RX_BEGIN); break; case ECP22_RX_FIRST: assert(ecp->rx.state == ECP22_RX_WAIT); break; case ECP22_RX_REC_ECPDU: assert((ecp->rx.state == ECP22_RX_FIRST) || (ecp->rx.state == ECP22_RX_WAIT2)); break; case ECP22_RX_NEW_ECPDU: assert(ecp->rx.state == ECP22_RX_REC_ECPDU); break; case ECP22_RX_SEND_ACK: assert((ecp->rx.state == ECP22_RX_REC_ECPDU) || (ecp->rx.state == ECP22_RX_NEW_ECPDU)); break; case ECP22_RX_WAIT2: assert(ecp->rx.state == ECP22_RX_SEND_ACK); break; default: LLDPAD_ERR("%s:%s LLDP RX state machine invalid state %d\n", __func__, ecp->ifname, newstate); } LLDPAD_DBG("%s:%s state change %s -> %s\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state], ecp22_rx_states[newstate]); ecp->rx.state = newstate; } /* * Execute action in state sendack. Construct and send an acknowledgement * for the received ECP packet. */ static void ecp22_es_send_ack(struct ecp22 *ecp) { unsigned char ack_frame[ETH_HLEN + sizeof(struct ecp22_hdr)]; struct ethhdr *ethdst = (struct ethhdr *)ack_frame; struct ecp22_hdr *ecpdst = (struct ecp22_hdr *)&ack_frame[ETH_HLEN]; struct ethhdr *ethsrc = (struct ethhdr *)ecp->rx.frame; struct ecp22_hdr *ecpsrc = (struct ecp22_hdr *)&ecp->rx.frame[ETH_HLEN]; struct ecp22_hdr ack; LLDPAD_DBG("%s:%s state %s seqno %#hx\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state], ecp->rx.seqno); memcpy(ethdst->h_dest, nearest_customer_bridge, ETH_ALEN); l2_packet_get_own_src_addr(ecp->l2, (u8 *)ðdst->h_source); ethdst->h_proto = ethsrc->h_proto; /* Set ECP header */ ack.ver_op_sub = ntohs(ecpsrc->ver_op_sub); ecp22_hdr_set_op(&ack, ECP22_ACK); ecpdst->ver_op_sub = htons(ack.ver_op_sub); ecpdst->seqno = htons(ecp->rx.seqno); ecp22_txframe(ecp, "ecp-ack", ethsrc->h_source, ack_frame, sizeof ack_frame); } /* * Notify upper layer protocol function of ECP payload data just received. */ static void ecp22_to_ulp(unsigned short ulp, struct ecp22 *ecp) { size_t offset = ETH_HLEN + sizeof(struct ecp22_hdr); struct qbg22_imm to_ulp; to_ulp.data_type = ECP22_TO_ULP; to_ulp.u.c.len = ecp->rx.frame_len - offset; to_ulp.u.c.data = &ecp->rx.frame[offset]; if (ulp == ECP22_VDP) modules_notify(LLDP_MOD_VDP22, LLDP_MOD_ECP22, ecp->ifname, &to_ulp); else LLDPAD_INFO("%s:%s ECP subtype %d not yet implemented\n", __func__, ecp->ifname, ulp); } /* * Execute action in state newECPDU. * Notify upper layer protocol of new data. */ static void ecp22_es_new_ecpdu(struct ecp22 *ecp) { struct ecp22_hdr *hdr = (struct ecp22_hdr *)&ecp->rx.frame[ETH_HLEN]; struct ecp22_hdr ecphdr; unsigned short ulp; ecphdr.ver_op_sub = ntohs(hdr->ver_op_sub); ulp = ecp22_hdr_read_subtype(&ecphdr); LLDPAD_DBG("%s:%s state %s notify ULP %d seqno %#hx\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state], ulp, ecp->rx.seqno); ecp->rx.last_seqno = ecp->rx.seqno; ecp22_to_ulp(ulp, ecp); } /* * Execute action in state receiveECPDU. */ static void ecp22_es_rec_ecpdu(struct ecp22 *ecp) { struct ecp22_hdr *hdr = (struct ecp22_hdr *)&ecp->rx.frame[ETH_HLEN]; ecp->rx.seqno = ntohs(hdr->seqno); LLDPAD_DBG("%s:%s state %s seqno %#hx\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state], ecp->rx.seqno); } /* * Execute action in state receiveFirst. */ static void ecp22_es_first(struct ecp22 *ecp) { struct ecp22_hdr *hdr = (struct ecp22_hdr *)&ecp->rx.frame[ETH_HLEN]; LLDPAD_DBG("%s:%s state %s\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state]); ecp->rx.last_seqno = ntohs(hdr->seqno) - 1; } /* * Execute action in state receiveWait. */ static void ecp22_es_wait(struct ecp22 *ecp) { LLDPAD_DBG("%s:%s state %s\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state]); ecp->rx.ecpdu_received = false; } /* * ecp22_set_rx_state - sets the ecp receive state machine state * @ecp: pointer to currently used ecp data structure * * returns true or false * * switches the state machine to the next state depending on the input * variables. Returns true or false depending on wether the state machine * can be run again with the new state or can stop at the current state. */ static bool ecp22_set_rx_state(struct ecp22 *ecp) { struct port *port = port_find_by_name(ecp->ifname); if (!port) return false; LLDPAD_DBG("%s:%s state %s\n", __func__, ecp->ifname, ecp22_rx_states[ecp->rx.state]); if (port->portEnabled == false) ecp22_rx_change_state(ecp, ECP22_RX_BEGIN); switch (ecp->rx.state) { case ECP22_RX_BEGIN: ecp22_rx_change_state(ecp, ECP22_RX_WAIT); return false; case ECP22_RX_WAIT: if (ecp->rx.ecpdu_received) { ecp22_rx_change_state(ecp, ECP22_RX_FIRST); return true; } return false; case ECP22_RX_WAIT2: if (ecp->rx.ecpdu_received) { ecp22_rx_change_state(ecp, ECP22_RX_REC_ECPDU); return true; } return false; case ECP22_RX_FIRST: ecp22_rx_change_state(ecp, ECP22_RX_REC_ECPDU); return true; case ECP22_RX_REC_ECPDU: if (ecp->rx.seqno == ecp->rx.last_seqno) ecp22_rx_change_state(ecp, ECP22_RX_SEND_ACK); else ecp22_rx_change_state(ecp, ECP22_RX_NEW_ECPDU); return true; case ECP22_RX_NEW_ECPDU: ecp22_rx_change_state(ecp, ECP22_RX_SEND_ACK); return true; case ECP22_RX_SEND_ACK: ecp22_rx_change_state(ecp, ECP22_RX_WAIT2); return true; default: LLDPAD_DBG("%s:%s ECP RX state machine in invalid state %d\n", __func__, ecp->ifname, ecp->rx.state); return false; } } /* * ecp22_rx_run_sm - state machine for ecp receive protocol * @ecp: pointer to currently used ecp data structure * * no return value * * runs the state machine for ecp22 receive function. */ static void ecp22_rx_run_sm(struct ecp22 *ecp) { ecp22_set_rx_state(ecp); do { switch (ecp->rx.state) { case ECP22_RX_WAIT: case ECP22_RX_WAIT2: ecp22_es_wait(ecp); break; case ECP22_RX_FIRST: ecp22_es_first(ecp); break; case ECP22_RX_REC_ECPDU: ecp22_es_rec_ecpdu(ecp); break; case ECP22_RX_NEW_ECPDU: ecp22_es_new_ecpdu(ecp); break; case ECP22_RX_SEND_ACK: ecp22_es_send_ack(ecp); break; default: LLDPAD_DBG("%s:%s ECP RX state machine in invalid " "state %d\n", __func__, ecp->ifname, ecp->rx.state); } } while (ecp22_set_rx_state(ecp) == true); } /* * Received an aknowledgement frame. * Check if we have a transmit pending and the ack'ed packet number matches * the send packet. */ static void ecp22_recack_frame(struct ecp22 *ecp, unsigned short seqno) { LLDPAD_DBG("%s:%s txmit:%d seqno %#hx ack-seqno %#hx\n", __func__, ecp->ifname, ecp->tx.ecpdu_received, ecp->tx.seqno, seqno); if (ecp->tx.ecpdu_received) { if (ecp->tx.seqno == seqno) ecp->tx.ack_received = true; } } /* * ecp22_rx_receiveframe - receive am ecp frame * @ctx: rx callback context, struct ecp * in this case * @ifindex: index of interface * @buf: buffer which contains the frame just received * @len: size of buffer (frame) * * no return value * * creates a local copy of the buffer and checks the header. keeps some * statistics about ecp frames. Checks if it is a request or an ack frame and * branches to ecp rx or ecp tx state machine. */ static void ecp22_rx_receiveframe(void *ctx, int ifindex, const u8 *buf, size_t len) { struct ecp22 *ecp = (struct ecp22 *)ctx; struct port *port; struct ecp22_hdr *ecp_hdr, ecphdr; LLDPAD_DBG("%s:%s ifindex:%d len:%zd state:%s ecpdu_received:%d\n", __func__, ecp->ifname, ifindex, len, ecp22_rx_states[ecp->rx.state], ecp->rx.ecpdu_received); hexdump_frame(ecp->ifname, "frame-in", buf, len); port = port_find_by_name(ecp->ifname); if (!port || ecp->rx.ecpdu_received) /* Port not found or buffer not free */ return; memcpy(ecp->rx.frame, buf, len); ecp->rx.frame_len = len; ecp->stats.statsFramesInTotal++; ecp_hdr = (struct ecp22_hdr *)&ecp->rx.frame[ETH_HLEN]; ecphdr.ver_op_sub = ntohs(ecp_hdr->ver_op_sub); /* Check for correct subtype and version number */ if (ecp22_hdr_read_version(&ecphdr) != 1) { LLDPAD_ERR("%s:%s ERROR unknown version %#02hx seqno %#hx\n", __func__, ecp->ifname, ecphdr.ver_op_sub, ntohs(ecp_hdr->seqno)); return; } switch (ecp22_hdr_read_subtype(&ecphdr)) { default: LLDPAD_ERR("%s:%s ERROR unknown subtype %#02hx seqno %#hx\n", __func__, ecp->ifname, ecphdr.ver_op_sub, ntohs(ecp_hdr->seqno)); return; case ECP22_PECSP: case ECP22_VDP: /* Subtype ok, fall through intended */ break; } switch (ecp22_hdr_read_op(&ecphdr)) { case ECP22_REQUEST: LLDPAD_DBG("%s:%s received REQ frame seqno %#hx\n", __func__, ecp->ifname, ntohs(ecp_hdr->seqno)); ecp->rx.ecpdu_received = true; ecp22_rx_run_sm(ecp); break; case ECP22_ACK: LLDPAD_DBG("%s:%s received ACK frame seqno %#hx\n", __func__, ecp->ifname, ntohs(ecp_hdr->seqno)); ecp22_recack_frame(ecp, ntohs(ecp_hdr->seqno)); break; default: LLDPAD_ERR("%s:%s ERROR unknown mode %#02hx seqno %#hx\n", __func__, ecp->ifname, ecphdr.ver_op_sub, ntohs(ecp_hdr->seqno)); } } /* * ecp22_create - create data structure and initialize ecp protocol * @ifname: interface for which the ecp protocol is initialized * * returns NULL on error and an pointer to the ecp22 structure on success. * * finds the port to the interface name, sets up the receive handle for * incoming ecp frames and initializes the ecp rx and tx state machines. * To be called when a successful exchange of EVB TLVs has been * made and ECP protocols are supported by both sides. */ static struct ecp22 *ecp22_create(char *ifname, struct ecp22_user_data *eud) { struct ecp22 *ecp; ecp = calloc(1, sizeof *ecp); if (!ecp) { LLDPAD_ERR("%s:%s unable to allocate ecp protocol\n", __func__, ifname); return NULL; } strncpy(ecp->ifname, ifname, sizeof ecp->ifname); ecp->l2 = l2_packet_init(ecp->ifname, 0, ETH_P_ECP22, ecp22_rx_receiveframe, ecp, 1); if (!ecp->l2) { LLDPAD_ERR("%s:%s error open layer 2 ETH_P_ECP\n", __func__, ifname); free(ecp); return NULL; } LIST_INSERT_HEAD(&eud->head, ecp, node); LLDPAD_DBG("%s:%s create ecp data\n", __func__, ifname); return ecp; } /* * ecp22_start - build up ecp structures for an interface * @ifname: name of the interface */ void ecp22_start(char *ifname) { struct ecp22_user_data *eud; struct ecp22 *ecp; LLDPAD_DBG("%s:%s start ecp\n", __func__, ifname); eud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_ECP22); if (!eud) { LLDPAD_DBG("%s:%s no ECP module\n", __func__, ifname); return; } ecp = find_ecpdata(ifname, eud); if (!ecp) ecp = ecp22_create(ifname, eud); ecp->max_retries = ECP22_MAX_RETRIES_DEFAULT; ecp->max_rte = ECP22_ACK_TIMER_DEFAULT; LIST_INIT(&ecp->inuse.head); ecp->inuse.last = 0; LIST_INIT(&ecp->isfree.head); ecp->isfree.freecnt = 0; ecp->rx.state = ECP22_RX_BEGIN; ecp22_rx_run_sm(ecp); ecp->tx.state = ECP22_TX_BEGIN; ecp22_tx_run_sm(ecp); } /* * Remove the ecp_payload nodes */ static void ecp22_removelist(ecp22_list *ptr) { struct ecp22_payload_node *np; while ((np = LIST_FIRST(ptr))) { LIST_REMOVE(np, node); np->ptlv = free_pkd_tlv(np->ptlv); free(np); } } static void ecp22_remove(struct ecp22 *ecp) { LLDPAD_DBG("%s:%s remove ecp\n", __func__, ecp->ifname); ecp22_removelist(&ecp->inuse.head); ecp->inuse.last = 0; ecp22_removelist(&ecp->isfree.head); ecp->isfree.freecnt = 0; LIST_REMOVE(ecp, node); free(ecp); } /* * ecp22_stop - tear down ecp structures for a interface * @ifname: name of the interface * * no return value * */ void ecp22_stop(char *ifname) { struct ecp22_user_data *eud; struct ecp22 *ecp; LLDPAD_DBG("%s:%s stop ecp\n", __func__, ifname); eud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_ECP22); ecp = find_ecpdata(ifname, eud); if (ecp) ecp22_remove(ecp); } /* * Update data exchanged via EVB protocol. * Returns true when data update succeeded. */ static int data_from_evb(char *ifname, struct evb22_to_ecp22 *ptr) { struct ecp22_user_data *eud; struct ecp22 *ecp; eud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_ECP22); ecp = find_ecpdata(ifname, eud); if (ecp) { ecp->max_rte = ptr->max_rte; ecp->max_retries = ptr->max_retry; return 0; } return -ENOENT; } /* * Add ecp payload data at the end of the queue. */ static void ecp22_add_payload(struct ecp22 *ecp, struct ecp22_payload_node *elem) { if (LIST_EMPTY(&ecp->inuse.head)) LIST_INSERT_HEAD(&ecp->inuse.head, elem, node); else LIST_INSERT_AFTER(ecp->inuse.last, elem, node); ecp->inuse.last = elem; if (!ecp->tx.ecpdu_received) /* Transmit buffer free */ ecp22_tx_run_sm(ecp); } /* * Copy the payload data. */ static struct packed_tlv *copy_ptlv(struct packed_tlv *from) { struct packed_tlv *ptlv = create_ptlv(); if (!ptlv) return NULL; ptlv->size = from->size; ptlv->tlv = calloc(ptlv->size, sizeof(unsigned char)); if (!ptlv->tlv) { free_pkd_tlv(ptlv); return NULL; } memcpy(ptlv->tlv, from->tlv, from->size); return ptlv; } /* * Create a node for the ecp payload data. Get it from the free list if not * empty. Otherwise allocate from heap. */ static struct ecp22_payload_node *ecp22_getnode(struct ecp22_freelist *list) { struct ecp22_payload_node *elem = LIST_FIRST(&list->head); if (!elem) elem = calloc(1, sizeof *elem); else { LIST_REMOVE(elem, node); --list->freecnt; } return elem; } /* * Receive upper layer protocol data unit for transmit. * Returns error if the request could not be queued for transmision. */ static int ecp22_req2send(char *ifname, unsigned short subtype, unsigned const char *mac, struct packed_tlv *du) { struct ecp22_user_data *eud; struct ecp22 *ecp; struct ecp22_payload_node *payda; struct packed_tlv *ptlv = copy_ptlv(du); int rc = 0; LLDPAD_DBG("%s:%s subtype:%d\n", __func__, ifname, subtype); eud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_ECP22); ecp = find_ecpdata(ifname, eud); if (!ecp) { rc = -ENODEV; goto out; } if (!ptlv) { rc = -ENOMEM; goto out; } if (ptlv->size >= ECP22_MAXPAYLOAD_LEN) { rc = -E2BIG; goto out; } payda = ecp22_getnode(&ecp->isfree); if (!payda) { free_pkd_tlv(ptlv); rc = -ENOMEM; goto out; } payda->ptlv = ptlv; payda->subtype = subtype; memcpy(payda->mac, mac, sizeof payda->mac); ecp22_add_payload(ecp, payda); out: LLDPAD_DBG("%s:%s rc:%d\n", __func__, ifname, rc); return rc; } /* * Payload data from VDP module. * Returns true when data update succeeded. */ static int data_from_vdp(char *ifname, struct ecp22_to_ulp *ptr) { struct packed_tlv d; d.size = ptr->len; d.tlv = ptr->data; return ecp22_req2send(ifname, ECP22_VDP, nearest_customer_bridge, &d); } /* * Handle notifications from other modules. Check if sender-id and data type * indicator match. Return false when data could not be delivered. */ static int ecp22_notify(int sender_id, char *ifname, void *data) { struct qbg22_imm *qbg = (struct qbg22_imm *)data; LLDPAD_DBG("%s:%s sender-id:%#x data_type:%d\n", __func__, ifname, sender_id, qbg->data_type); if (sender_id == LLDP_MOD_EVB22 && qbg->data_type == EVB22_TO_ECP22) return data_from_evb(ifname, &qbg->u.a); if (sender_id == LLDP_MOD_VDP22 && qbg->data_type == VDP22_TO_ECP22) return data_from_vdp(ifname, &qbg->u.c); return 0; } static const struct lldp_mod_ops ecp22_ops = { .lldp_mod_register = ecp22_register, .lldp_mod_unregister = ecp22_unregister, .lldp_mod_notify = ecp22_notify }; /* * ecp22_register - register ecp module to lldpad * * returns lldp_module struct on success, NULL on error * * allocates a module structure with ecp module information and returns it * to lldpad. */ struct lldp_module *ecp22_register(void) { struct lldp_module *mod; struct ecp22_user_data *eud; mod = calloc(1, sizeof *mod); if (!mod) { LLDPAD_ERR("%s:can not allocate ecp module data\n", __func__); return NULL; } eud = calloc(1, sizeof(struct ecp22_user_data)); if (!eud) { free(mod); LLDPAD_ERR("%s:can not allocate ecp user data\n", __func__); return NULL; } LIST_INIT(&eud->head); mod->id = LLDP_MOD_ECP22; mod->ops = &ecp22_ops; mod->data = eud; LLDPAD_DBG("%s: done\n", __func__); return mod; } /* * ecp22_free_data - frees up ecp data chain */ static void ecp22_free_data(struct ecp22_user_data *ud) { struct ecp22 *ecp; if (ud) { while (!LIST_EMPTY(&ud->head)) { ecp = LIST_FIRST(&ud->head); ecp22_remove(ecp); } } } /* * ecp22_unregister - unregister ecp module from lldpad * * no return value * * frees ecp module structure and user data. */ void ecp22_unregister(struct lldp_module *mod) { if (mod->data) { ecp22_free_data((struct ecp22_user_data *)mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s: done\n", __func__); } lldpad-0.9.46/qbg/lldp_evb22.c000066400000000000000000000345571215142747300157460ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2012 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #define _GNU_SOURCE #include #include #include "lldp.h" #include "lldp_tlv.h" #include "lldp_evb22.h" #include "lldp_ecp22.h" #include "lldp_vdp22.h" #include "lldp_qbg_utils.h" #include "lldp_evb_cmds.h" #include "messages.h" #include "config.h" extern struct lldp_head lldp_head; struct evb22_data *evb22_data(char *ifname, enum agent_type type) { struct evb22_user_data *ud; struct evb22_data *ed = NULL; ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_EVB22); if (ud) { LIST_FOREACH(ed, &ud->head, entry) { if (!strncmp(ifname, ed->ifname, IFNAMSIZ) && (type == ed->agenttype)) break; } } return ed; } static void evb22_format_tlv(char *buf, size_t len, struct evb22_tlv *tlv) { int comma = 0; char bridge_txt[32], station_txt[32]; memset(bridge_txt, 0, sizeof bridge_txt); if (evb_ex_bgid(tlv->bridge_s)) { strcat(bridge_txt, "bgid"); comma = 1; } if (evb_ex_rrcap(tlv->bridge_s)) { if (comma) strcat(bridge_txt, ","); strcat(bridge_txt, "rrcap"); comma = 1; } if (evb_ex_rrctr(tlv->bridge_s)) { if (comma) strcat(bridge_txt, ","); strcat(bridge_txt, "rrctr"); } comma = 0; memset(station_txt, 0, sizeof station_txt); if (evb_ex_sgid(tlv->station_s)) { strcat(station_txt, "sgid"); comma = 1; } if (evb_ex_rrreq(tlv->station_s)) { if (comma) strcat(station_txt, ","); strcat(station_txt, "rrreq"); comma = 1; } if (evb_ex_rrstat(tlv->station_s)) { if (comma) strcat(station_txt, ","); strcat(station_txt, "rrstat"); } snprintf(buf, len, "bridge:%s(%#02x) station:%s(%#02x) " "retries:%d rte:%d mode:%d r/l:%d rwd:%d " "r/l:%d rka:%d", bridge_txt, tlv->bridge_s, station_txt, tlv->station_s, evb_ex_retries(tlv->r_rte), evb_ex_rte(tlv->r_rte), evb_ex_evbmode(tlv->evb_mode), evb_ex_rol(tlv->evb_mode), evb_ex_rwd(tlv->evb_mode), evb_ex_rol(tlv->rl_rka), evb_ex_rka(tlv->rl_rka)); } static void evb22_print_tlvinfo(char *ifname, struct evb22_tlv *tlv) { char buf[256]; evb22_format_tlv(buf, sizeof buf, tlv); LLDPAD_DBG("%s evb %s\n", ifname, buf); } static void evb22_dump_tlv(char *ifname, struct unpacked_tlv *tlv) { int i, left = 0; char buffer[256]; for (i = 0; i < tlv->length; i++) { int c; c = snprintf(buffer + left, sizeof buffer - left, "%02x ", tlv->info[i]); if (c < 0 || (c >= (int)sizeof buffer - left)) break; else left += c; } LLDPAD_DBG("%s:%s type %i length %i info %s\n", __func__, ifname, tlv->type, tlv->length, buffer); } static void common_tlv(struct evb22_data *ed) { struct evb22_tlv *recv = &ed->last; struct evb22_tlv *mine = &ed->policy; u8 val; /* Set retries and rte value */ val = evb_ex_retries(recv->r_rte); if (evb_ex_retries(mine->r_rte) > val) val = evb_ex_retries(mine->r_rte); ed->out.r_rte = evb_set_retries(val); val = evb_ex_rte(recv->r_rte); if (evb_ex_rte(mine->r_rte) > val) val = evb_ex_rte(mine->r_rte); ed->out.r_rte |= evb_set_rte(val); /* Set evbmode */ ed->out.evb_mode = evb_set_evbmode(evb_ex_evbmode(mine->evb_mode)); val = evb_ex_rwd(recv->evb_mode); if (evb_ex_rwd(mine->evb_mode) > val) val = evb_ex_rwd(mine->evb_mode); else ed->out.evb_mode |= evb_set_rol(1); ed->out.evb_mode |= evb_set_rwd(val); /* Set rka */ ed->out.rl_rka = 0; val = evb_ex_rka(recv->rl_rka); if (evb_ex_rka(mine->rl_rka) > val) val = evb_ex_rka(mine->rl_rka); else ed->out.rl_rka = evb_set_rol(1); ed->out.rl_rka |= evb_set_rka(val); } /* * Fill the EVB DU for LLDP transmition. Sender is bridge. */ static void bridge_tlv(struct evb22_data *ed) { struct evb22_tlv *recv = &ed->last; struct evb22_tlv *mine = &ed->policy; /* Copy my last station status */ ed->out.station_s = recv->station_s; /* Set bridge status */ ed->out.bridge_s = mine->bridge_s; if (evb_ex_rrreq(recv->station_s) && evb_ex_rrcap(mine->bridge_s)) ed->out.bridge_s |= evb_set_rrctr(1); common_tlv(ed); } /* * Fill the EVB DU for LLDP transmition. Sender is station. */ static void station_tlv(struct evb22_data *ed) { struct evb22_tlv *recv = &ed->last; struct evb22_tlv *mine = &ed->policy; u8 val; /* Copy my last bridge status */ ed->out.bridge_s = recv->bridge_s; /* * Set station status, 2nd byte of OUI is 0x80. If 0x00 * nothing received from bridge. */ if (recv->oui[1] == 0) val = EVB_RRSTAT_DONT; else if (evb_ex_rrctr(recv->bridge_s)) val = EVB_RRSTAT_YES; else val = EVB_RRSTAT_NO; ed->out.station_s = evb_maskoff_rrstat(mine->station_s) | evb_set_rrstat(val); common_tlv(ed); } /* * Checks values received in TLV and takes over some values. * Sets the new suggestion in member tie to be send out to switch. * * Also notify depending modules about the new values. */ static void evb22_update_tlv(struct evb22_data *ed) { struct qbg22_imm qbg; if (evb_ex_evbmode(ed->policy.evb_mode) == EVB_STATION) station_tlv(ed); else bridge_tlv(ed); qbg.data_type = EVB22_TO_ECP22; qbg.u.a.max_rte = evb_ex_rte(ed->out.r_rte); qbg.u.a.max_retry = evb_ex_retries(ed->out.r_rte); modules_notify(LLDP_MOD_ECP22, LLDP_MOD_EVB22, ed->ifname, &qbg); qbg.data_type = EVB22_TO_VDP22; qbg.u.b.max_rka = evb_ex_rka(ed->out.rl_rka); qbg.u.b.max_rwd = evb_ex_rwd(ed->out.evb_mode); /* Support group identifiers when advertised by both sides */ qbg.u.b.gpid = evb_ex_bgid(ed->out.bridge_s) && evb_ex_sgid(ed->out.station_s); modules_notify(LLDP_MOD_VDP22, LLDP_MOD_EVB22, ed->ifname, &qbg); } /* * Build the packed EVB TLV. * Returns a pointer to the packed tlv or 0 on failure. */ static struct packed_tlv *evb22_build_tlv(struct evb22_data *ed) { struct packed_tlv *ptlv = 0; u8 infobuf[sizeof(struct evb22_tlv)]; struct unpacked_tlv tlv = { .type = ORG_SPECIFIC_TLV, .length = sizeof(struct evb22_tlv), .info = infobuf }; evb22_update_tlv(ed); memcpy(tlv.info, &ed->out, tlv.length); ptlv = pack_tlv(&tlv); if (ptlv) { LLDPAD_DBG("%s:%s TLV about to be sent out:\n", __func__, ed->ifname); evb22_dump_tlv(ed->ifname, &tlv); } else LLDPAD_DBG("%s:%s failed to pack EVB TLV\n", __func__, ed->ifname); return ptlv; } /* * Function call to build and return module specific packed EVB TLV. * Returned packed_tlv is free'ed by caller of this function. */ static struct packed_tlv *evb22_gettlv(struct port *port, struct lldp_agent *agent) { struct evb22_data *ed; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb22_data(port->ifname, agent->type); if (!ed) { LLDPAD_ERR("%s:%s agent %d failed\n", __func__, port->ifname, agent->type); return 0; } return (ed->txmit) ? evb22_build_tlv(ed) : 0; } /* * evb_rchange: process received EVB TLV LLDPDU * * TLV not consumed on error */ static int evb22_rchange(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { struct evb22_data *ed; u8 oui_subtype[OUI_SUB_SIZE] = LLDP_MOD_EVB22_OUI; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb22_data(port->ifname, agent->type); if (!ed) return SUBTYPE_INVALID; if (tlv->type == TYPE_127) { /* check for length */ if (tlv->length < OUI_SUB_SIZE) return TLV_ERR; /* check for oui */ if (memcmp(tlv->info, &oui_subtype, OUI_SUB_SIZE)) return SUBTYPE_INVALID; /* disable rx if tx has been disabled by administrator */ if (!ed->txmit) { LLDPAD_WARN("%s:%s agent %d EVB Config disabled\n", __func__, ed->ifname, agent->type); return TLV_OK; } LLDPAD_DBG("%s:%s agent %d received tlv:\n", __func__, port->ifname, agent->type); evb22_dump_tlv(ed->ifname, tlv); memcpy(&ed->last, tlv->info, tlv->length); evb22_print_tlvinfo(ed->ifname, &ed->last); evb22_update_tlv(ed); somethingChangedLocal(ed->ifname, agent->type); LLDPAD_DBG("%s:%s agent %d new tlv:\n", __func__, port->ifname, agent->type); evb22_print_tlvinfo(ed->ifname, &ed->out); /* TODO vdp_update(port->ifname, ed->tie.ccap); */ } return TLV_OK; } /* * Stop all modules which depend on EVB capabilities. */ static void evb22_stop_modules(char *ifname) { LLDPAD_DBG("%s:%s STOP\n", __func__, ifname); ecp22_stop(ifname); vdp22_stop(ifname); } static void evb22_ifdown(char *ifname, struct lldp_agent *agent) { struct evb22_data *ed; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return; LLDPAD_DBG("%s:%s agent %d called\n", __func__, ifname, agent->type); ed = evb22_data(ifname, agent->type); if (!ed) { LLDPAD_DBG("%s:%s agent %d does not exist.\n", __func__, ifname, agent->type); return; } if (ed->vdp_start) evb22_stop_modules(ifname); LIST_REMOVE(ed, entry); free(ed); LLDPAD_INFO("%s:%s agent %d removed\n", __func__, ifname, agent->type); } /* * Fill up evb structure with reasonable info from the configuration file. */ static void evb22_init_tlv(struct evb22_data *ed, struct lldp_agent *agent) { u8 mode; memset(&ed->last, 0, sizeof ed->last); memset(&ed->out, 0, sizeof ed->out); memset(&ed->policy, 0, sizeof ed->policy); ed->txmit = evb22_conf_enabletx(ed->ifname, agent->type); if (!ed->txmit) LLDPAD_DBG("%s:%s agent %d EVB tx is currently disabled\n", __func__, ed->ifname, agent->type); hton24(ed->policy.oui, LLDP_MOD_EVB22); ed->policy.sub = LLDP_MOD_EVB22_SUBTYPE; hton24(ed->out.oui, LLDP_MOD_EVB22); ed->out.sub = LLDP_MOD_EVB22_SUBTYPE; mode = evb22_conf_evbmode(ed->ifname, agent->type); ed->policy.evb_mode = evb_set_rol(0) | evb_set_rwd(evb22_conf_rwd(ed->ifname, agent->type)) | evb_set_evbmode(mode); if (mode == EVB_STATION) { mode = evb22_conf_rrreq(ed->ifname, agent->type); ed->policy.station_s = evb_set_rrstat(EVB_RRSTAT_DONT) | evb_set_sgid(evb22_conf_gid(ed->ifname, agent->type)) | evb_set_rrreq(mode); ed->policy.bridge_s = 0; } else { mode = evb22_conf_rrcap(ed->ifname, agent->type); ed->policy.bridge_s = evb_set_rrcap(mode) | evb_set_bgid(evb22_conf_gid(ed->ifname, agent->type)); ed->policy.station_s = 0; } ed->policy.r_rte = evb_set_retries(evb22_conf_retries(ed->ifname, agent->type)) | evb_set_rte(evb22_conf_rte(ed->ifname, agent->type)); ed->policy.rl_rka = evb_set_rol(0) | evb_set_rka(evb22_conf_rka(ed->ifname, agent->type)); } static void evb22_ifup(char *ifname, struct lldp_agent *agent) { struct evb22_data *ed; struct evb22_user_data *ud; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return; LLDPAD_DBG("%s:%s agent %d called\n", __func__, ifname, agent->type); ed = evb22_data(ifname, agent->type); if (ed) { LLDPAD_DBG("%s:%s agent %d already exists\n", __func__, ifname, agent->type); return; } /* not found, alloc/init per-port tlv data */ ed = (struct evb22_data *) calloc(1, sizeof *ed); if (!ed) { LLDPAD_ERR("%s:%s agent %d malloc %zu failed\n", __func__, ifname, agent->type, sizeof *ed); return; } strncpy(ed->ifname, ifname, IFNAMSIZ); ed->agenttype = agent->type; evb22_init_tlv(ed, agent); ud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_EVB22); LIST_INSERT_HEAD(&ud->head, ed, entry); LLDPAD_DBG("%s:%s agent %d added\n", __func__, ifname, agent->type); } /* * Start all modules which depend on EVB capabilities: ECP, VDP, CDCP. */ static void evb22_start_modules(char *ifname) { LLDPAD_DBG("%s:%s START\n", __func__, ifname); ecp22_start(ifname); vdp22_start(ifname); } /* * Check for stable interfaces. When an interface goes up the carrier might * come and go during a start up time. Define a window during which the port * is considered unstable for EVB/VDP protocols. * * Use the dormantDelay counter of the port to determine a stable interface. */ static int evb22_timer(struct port *port, struct lldp_agent *agent) { struct evb22_data *ed; if (agent->type != NEAREST_CUSTOMER_BRIDGE) return 0; ed = evb22_data(port->ifname, agent->type); if (!ed) return 0; if (!ed->vdp_start && evb_ex_rrstat(ed->out.station_s) == EVB_RRSTAT_YES) { ed->vdp_start = true; evb22_start_modules(port->ifname); } return 0; } static u8 evb22_mibdelete(struct port *port, struct lldp_agent *agent) { struct evb22_data *ed; ed = evb22_data(port->ifname, agent->type); if (ed && (agent->type == ed->agenttype)) { memset(&ed->last, 0, sizeof ed->last); /* TODO vdp_update(port->ifname, 0); */ } return 0; } /* * Remove all interface/agent specific evb data. */ static void evb22_free_data(struct evb22_user_data *ud) { struct evb22_data *ed; if (ud) { while (!LIST_EMPTY(&ud->head)) { ed = LIST_FIRST(&ud->head); LIST_REMOVE(ed, entry); free(ed); } } } void evb22_unregister(struct lldp_module *mod) { if (mod->data) { evb22_free_data((struct evb22_user_data *) mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s:done\n", __func__); } static const struct lldp_mod_ops evb22_ops = { .lldp_mod_gettlv = evb22_gettlv, .lldp_mod_rchange = evb22_rchange, .lldp_mod_mibdelete = evb22_mibdelete, .timer = evb22_timer, .lldp_mod_ifdown = evb22_ifdown, .lldp_mod_ifup = evb22_ifup, .lldp_mod_register = evb22_register, .lldp_mod_unregister = evb22_unregister, .get_arg_handler = evb22_get_arg_handlers }; struct lldp_module *evb22_register(void) { struct lldp_module *mod; struct evb22_user_data *ud; mod = calloc(1, sizeof *mod); if (!mod) { LLDPAD_ERR("%s: failed to malloc module data\n", __func__); return NULL; } ud = calloc(1, sizeof *ud); if (!ud) { free(mod); LLDPAD_ERR("%s failed to malloc module user data\n", __func__); return NULL; } LIST_INIT(&ud->head); mod->id = LLDP_MOD_EVB22; mod->ops = &evb22_ops; mod->data = ud; LLDPAD_DBG("%s:done\n", __func__); return mod; } lldpad-0.9.46/qbg/lldp_evb22_clif.c000066400000000000000000000114751215142747300167350ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2010, 2012 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #include #include #include "lldp_tlv.h" #include "clif_msgs.h" #include "lldp_mod.h" #include "lldptool.h" #include "lldp.h" #include "lldp_evb22.h" #include "lldp_evb22_clif.h" static void show_tlv(char *buf, size_t len, struct evb22_tlv *tlv) { int comma = 0; char bridge_txt[32], station_txt[32]; memset(bridge_txt, 0, sizeof bridge_txt); if (evb_ex_bgid(tlv->bridge_s)) { strcat(bridge_txt, "bgid"); comma = 1; } if (evb_ex_rrcap(tlv->bridge_s)) { if (comma) strcat(bridge_txt, ","); strcat(bridge_txt, "rrcap"); comma = 1; } if (evb_ex_rrctr(tlv->bridge_s)) { if (comma) strcat(bridge_txt, ","); strcat(bridge_txt, "rrctr"); } comma = 0; memset(station_txt, 0, sizeof station_txt); if (evb_ex_sgid(tlv->station_s)) { strcat(station_txt, "sgid"); comma = 1; } if (evb_ex_rrreq(tlv->station_s)) { if (comma) strcat(station_txt, ","); strcat(station_txt, "rrreq"); comma = 1; } if (evb_ex_rrstat(tlv->station_s)) { if (comma) strcat(station_txt, ","); strcat(station_txt, "rrstat"); } snprintf(buf, len, "bridge:%s(%#02x)\n" "\tstation:%s(%#02x)\n" "\tretries:%d rte:%d\n" "\tmode:%s r/l:%d rwd:%d\n" "\tr/l:%d rka:%d\n", bridge_txt, tlv->bridge_s, station_txt, tlv->station_s, evb_ex_retries(tlv->r_rte), evb_ex_rte(tlv->r_rte), evb_ex_evbmode(tlv->evb_mode) == EVB_STATION ? "station" : "bridge", evb_ex_rol(tlv->evb_mode), evb_ex_rwd(tlv->evb_mode), evb_ex_rol(tlv->rl_rka), evb_ex_rka(tlv->rl_rka)); } static void evb22_print_cfg_tlv(u16 len, char *info) { struct evb22_tlv tlv; char buf[256]; if (len != 5) { printf("Bad evbcfg TLV: %s\n", info); return; } memset(&tlv, 0, sizeof tlv); memset(buf, 0, sizeof buf); hexstr2bin(&info[0], &tlv.bridge_s, sizeof tlv.bridge_s); hexstr2bin(&info[2], &tlv.station_s, sizeof tlv.station_s); hexstr2bin(&info[4], &tlv.r_rte, sizeof tlv.r_rte); hexstr2bin(&info[6], &tlv.evb_mode, sizeof tlv.evb_mode); hexstr2bin(&info[8], &tlv.rl_rka, sizeof tlv.rl_rka); show_tlv(buf, sizeof buf, &tlv); printf("%s", buf); } static struct type_name_info evb22_tlv_names[] = { { .type = TLVID(OUI_IEEE_8021Qbg22, LLDP_EVB22_SUBTYPE), .name = "EVB Configuration TLV", .key = "evb", .print_info = evb22_print_cfg_tlv }, { .type = INVALID_TLVID } }; static int evb22_print_help() { struct type_name_info *tn = &evb22_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tn->key && strlen(tn->key) && tn->name) { printf(" %s", tn->key); if (strlen(tn->key)+3 < 8) printf("\t"); printf("\t: %s\n", tn->name); } tn++; } return 0; } static void evb22_cli_unregister(struct lldp_module *mod) { free(mod); } /* return 1: if it printed the TLV * 0: if it did not */ static int evb22_print_tlv(u32 tlvid, u16 len, char *info) { struct type_name_info *tn = &evb22_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (tlvid == tn->type) { printf("%s\n", tn->name); if (tn->print_info) { printf("\t"); tn->print_info(len-4, info); } return 1; } tn++; } return 0; } static u32 evb22_lookup_tlv_name(char *tlvid_str) { struct type_name_info *tn = &evb22_tlv_names[0]; while (tn->type != INVALID_TLVID) { if (!strcasecmp(tn->key, tlvid_str)) return tn->type; tn++; } return INVALID_TLVID; } static const struct lldp_mod_ops evb22_ops_clif = { .lldp_mod_register = evb22_cli_register, .lldp_mod_unregister = evb22_cli_unregister, .print_tlv = evb22_print_tlv, .lookup_tlv_name = evb22_lookup_tlv_name, .print_help = evb22_print_help }; struct lldp_module *evb22_cli_register(void) { struct lldp_module *mod; mod = calloc(1, sizeof(*mod)); if (!mod) { fprintf(stderr, "%s failed to malloc module data\n", __func__); return NULL; } mod->id = LLDP_MOD_EVB22; mod->ops = &evb22_ops_clif; return mod; } lldpad-0.9.46/qbg/lldp_evb22_cmds.c000066400000000000000000000461301215142747300167420ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2012 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #define _GNU_SOURCE #include #include #include #include "lldp.h" #include "lldp_evb22.h" #include "lldp_tlv.h" #include "lldp_mand_clif.h" #include "config.h" #include "clif_msgs.h" #include "messages.h" /* * Defines for configuration file name tags. */ #define EVB_BUF_SIZE 256 #define EVB_CONF_MODE "evbmode" #define EVB_CONF_RRREQ "evbrrreq" #define EVB_CONF_RRCAP "evbrrcap" #define EVB_CONF_GPID "evbgpid" #define EVB_CONF_RETRIES "ecpretries" #define EVB_CONF_RTE "ecprte" #define EVB_CONF_RWD "vdprwd" #define EVB_CONF_RKA "vdprka" #define EVB_CONF_BRIDGE "bridge" #define EVB_CONF_STATION "station" /* * Read EVB specific data from the configuration file. */ static const char *evb22_conf_string(char *ifname, enum agent_type type, char *ext, int def) { char arg_path[EVB_BUF_SIZE]; const char *param = NULL; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID(OUI_IEEE_8021Qbg22, LLDP_EVB22_SUBTYPE), ext); if (get_cfg(ifname, type, arg_path, ¶m, CONFIG_TYPE_STRING)) LLDPAD_INFO("%s:%s agent %d loading EVB policy for %s" " failed, using default (%d)\n", __func__, ifname, type, ext, def); return param; } static int evb22_conf_int(char *ifname, enum agent_type type, char *ext, int def, int cfgtype) { char arg_path[EVB_BUF_SIZE]; int param; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, TLVID(OUI_IEEE_8021Qbg22, LLDP_EVB22_SUBTYPE), ext); if (get_cfg(ifname, type, arg_path, ¶m, cfgtype)) { LLDPAD_INFO("%s:%s agent %d loading EVB policy for %s" " failed, using default (%d)\n", __func__, ifname, type, ext, def); return def; } return param; } /* * Read EXP parameter. Defaults to 8 --> 10 * 2 ^ 8 = 2560us > 2ms. */ static int exponent(char *ifname, enum agent_type type, char *txt, int def) { int value; value = evb22_conf_int(ifname, type, txt, def, CONFIG_TYPE_INT); if (value > 31) { LLDPAD_DBG("%s:%s agent %d invalid %s %d\n", __func__, ifname, type, txt, value); value = def; } LLDPAD_DBG("%s:%s agent %d policy %s %d\n", __func__, ifname, type, txt, value); return value; } /* * Read retransmission exponent parameter. */ int evb22_conf_rte(char *ifname, enum agent_type type) { return exponent(ifname, type, EVB_CONF_RTE, 8); } /* * Read reinit keep alive parameter. Same as RTE. */ int evb22_conf_rka(char *ifname, enum agent_type type) { return exponent(ifname, type, EVB_CONF_RKA, 20); } /* * Read resource wait delay parameter. Same as RTE. */ int evb22_conf_rwd(char *ifname, enum agent_type type) { return exponent(ifname, type, EVB_CONF_RWD, 20); } /* * Read max retries parameter. Defaults to 3. */ int evb22_conf_retries(char *ifname, enum agent_type type) { int value; value = evb22_conf_int(ifname, type, EVB_CONF_RETRIES, 3, CONFIG_TYPE_INT); if (value > 7) { LLDPAD_DBG("%s:%s agent %d invalid %s %d\n", __func__, ifname, type, EVB_CONF_RETRIES, value); value = 3; } LLDPAD_DBG("%s:%s agent %d policy %s %d\n", __func__, ifname, type, EVB_CONF_RETRIES, value); return value; } /* * Read station group id parameter. Defaults to false. */ int evb22_conf_gid(char *ifname, enum agent_type type) { int value; value = evb22_conf_int(ifname, type, EVB_CONF_GPID, false, CONFIG_TYPE_BOOL); LLDPAD_DBG("%s:%s agent %d policy %s %s\n", __func__, ifname, type, EVB_CONF_GPID, value ? "true" : "false"); return value; } /* * Read reflective-relay bridge capability parameter. Defaults to false. */ int evb22_conf_rrcap(char *ifname, enum agent_type type) { int value; value = evb22_conf_int(ifname, type, EVB_CONF_RRCAP, false, CONFIG_TYPE_BOOL); LLDPAD_DBG("%s:%s agent %d policy %s %s\n", __func__, ifname, type, EVB_CONF_RRCAP, value ? "true" : "false"); return value; } /* * Read reflective-relay station request parameter. Defaults to false. */ int evb22_conf_rrreq(char *ifname, enum agent_type type) { int value; value = evb22_conf_int(ifname, type, EVB_CONF_RRREQ, false, CONFIG_TYPE_BOOL); LLDPAD_DBG("%s:%s agent %d policy %s %s\n", __func__, ifname, type, EVB_CONF_RRREQ, value ? "true" : "false"); return value; } /* * Read station/bridge role from configuration file. Defaults to station */ int evb22_conf_evbmode(char *ifname, enum agent_type type) { int mode = EVB_STATION; const char *value; value = evb22_conf_string(ifname, type, EVB_CONF_MODE, mode); if (value) { if (!strcasecmp(value, EVB_CONF_BRIDGE)) mode = EVB_BRIDGE; else if (strcasecmp(value, EVB_CONF_STATION)) { LLDPAD_ERR("%s:%s agent %d invalid evbmode %s\n", __func__, ifname, type, value); value = EVB_CONF_STATION; } } else value = EVB_CONF_STATION; LLDPAD_DBG("%s:%s agent %d policy %s %s(%#x)\n", __func__, ifname, type, EVB_CONF_MODE, value, mode); return mode; } /* * Read transmit status from configuration file. */ int evb22_conf_enabletx(char *ifname, enum agent_type type) { return is_tlv_txenabled(ifname, type, TLVID(OUI_IEEE_8021Qbg22, LLDP_EVB22_SUBTYPE)); } static int evb22_cmdok(struct cmd *cmd, cmd_status expected) { if (cmd->cmd != expected) return cmd_invalid; switch (cmd->tlvid) { case TLVID(OUI_IEEE_8021Qbg22, LLDP_EVB22_SUBTYPE): return cmd_success; case INVALID_TLVID: return cmd_invalid; default: return cmd_not_applicable; } } static int get_arg_evbmode(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { struct evb22_data *ed; char *s; cmd_status good_cmd = evb22_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (evb_ex_evbmode(ed->policy.evb_mode) == EVB_STATION) s = EVB_CONF_STATION; else s = EVB_CONF_BRIDGE; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int set2_arg_evbmode(struct cmd *cmd, char *arg, const char *argvalue, bool test) { char arg_path[EVB_BUF_SIZE]; struct evb22_data *ed; cmd_status good_cmd = evb22_cmdok(cmd, cmd_settlv); u8 mode; if (good_cmd != cmd_success) return good_cmd; if (strcasecmp(argvalue, EVB_CONF_BRIDGE) && strcasecmp(argvalue, EVB_CONF_STATION)) return cmd_bad_params; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &argvalue, CONFIG_TYPE_STRING)) { LLDPAD_ERR("%s: error saving EVB mode (%s)\n", ed->ifname, argvalue); return cmd_failed; } mode = strcasecmp(argvalue, EVB_CONF_BRIDGE) ? EVB_STATION : EVB_BRIDGE; ed->policy.evb_mode = evb_maskoff_evbmode(ed->policy.evb_mode) | evb_set_evbmode(mode); LLDPAD_INFO("%s: changed EVB mode (%s)\n", ed->ifname, argvalue); return cmd_success; } static int set_arg_evbmode(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return set2_arg_evbmode(cmd, arg, argvalue, false); } static int test_arg_evbmode(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return set2_arg_evbmode(cmd, arg, argvalue, true); } static int get_txmit(struct evb22_data *ed) { return ed->txmit; } static void set_txmit(struct evb22_data *ed, int value) { ed->txmit = value; } static int get_gpid(struct evb22_data *ed) { int mode = evb_ex_evbmode(ed->policy.evb_mode); if (mode == EVB_STATION && evb_ex_sgid(ed->policy.station_s)) return 1; if (mode == EVB_BRIDGE && evb_ex_bgid(ed->policy.bridge_s)) return 1; return 0; } static void set_gpid(struct evb22_data *ed, int value) { if (evb_ex_evbmode(ed->policy.evb_mode) == EVB_STATION) ed->policy.station_s = evb_maskoff_sgid(ed->policy.station_s) | evb_set_sgid(value); else ed->policy.bridge_s = evb_maskoff_bgid(ed->policy.bridge_s) | evb_set_bgid(value); } static void set_rrcap(struct evb22_data *ed, int value) { ed->policy.bridge_s = evb_maskoff_rrcap(ed->policy.bridge_s) | evb_set_rrcap(value); } static int get_rrcap(struct evb22_data *ed) { return evb_ex_rrcap(ed->policy.bridge_s); } static void set_rrreq(struct evb22_data *ed, int value) { ed->policy.station_s = evb_maskoff_rrreq(ed->policy.station_s) | evb_set_rrreq(value); } static int get_rrreq(struct evb22_data *ed) { return evb_ex_rrreq(ed->policy.station_s); } /* * Read a boolean value from the command line argument and apply the new * value to parameter. */ static int scan_bool(struct cmd *cmd, char *arg, char *argvalue, bool test, void (*fct)(struct evb22_data *, int)) { int value; char arg_path[EVB_BUF_SIZE]; struct evb22_data *ed; cmd_status good_cmd = evb22_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; if (!strcasecmp(argvalue, VAL_YES)) value = 1; else if (!strcasecmp(argvalue, VAL_NO)) value = 0; else return cmd_bad_params; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(cmd->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_BOOL)){ LLDPAD_ERR("%s: error saving EVB enabletx (%s)\n", ed->ifname, argvalue); return cmd_failed; } LLDPAD_INFO("%s: changed EVB %s (%s)\n", ed->ifname, arg, argvalue); (*fct)(ed, value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int show_bool(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len, int (*fct)(struct evb22_data *)) { struct evb22_data *ed; char *s; cmd_status good_cmd = evb22_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if ((*fct)(ed)) s = VAL_YES; else s = VAL_NO; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_bool(cmd, arg, argvalue, obuf, obuf_len, get_txmit); } static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, false, set_txmit); } static int test_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, true, 0); } static int get_arg_gpid(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_bool(cmd, arg, argvalue, obuf, obuf_len, get_gpid); } static int set_arg_gpid(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, false, set_gpid); } static int test_arg_gpid(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, true, 0); } static int get_arg_rrcap(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_bool(cmd, arg, argvalue, obuf, obuf_len, get_rrcap); } static int set_arg_rrcap(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, false, set_rrcap); } static int test_arg_rrcap(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, true, 0); } static int get_arg_rrreq(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_bool(cmd, arg, argvalue, obuf, obuf_len, get_rrreq); } static int set_arg_rrreq(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, false, set_rrreq); } static int test_arg_rrreq(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_bool(cmd, arg, argvalue, true, 0); } static void set_retries(struct evb22_data *ed, int value) { ed->policy.r_rte = evb_maskoff_retries(ed->policy.r_rte) | evb_set_retries(value); } static int get_retries(struct evb22_data *ed) { return evb_ex_retries(ed->policy.r_rte); } static void set_rte(struct evb22_data *ed, int value) { ed->policy.r_rte = evb_maskoff_rte(ed->policy.r_rte) | evb_set_rte(value); } static int get_rte(struct evb22_data *ed) { return evb_ex_rte(ed->policy.r_rte); } static void set_rwd(struct evb22_data *ed, int value) { ed->policy.evb_mode = evb_maskoff_rwd(ed->policy.evb_mode) | evb_set_rwd(value); } static int get_rwd(struct evb22_data *ed) { return evb_ex_rwd(ed->policy.evb_mode); } static void set_rka(struct evb22_data *ed, int value) { ed->policy.rl_rka = evb_maskoff_rka(ed->policy.rl_rka) | evb_set_rka(value); } static int get_rka(struct evb22_data *ed) { return evb_ex_rka(ed->policy.rl_rka); } static int scan_31bit(struct cmd *cmd, char *arg, const char *argvalue, bool test, void (*fct)(struct evb22_data *, int), int limit) { char arg_path[EVB_BUF_SIZE]; struct evb22_data *ed; int value; char *endp; cmd_status good_cmd = evb22_cmdok(cmd, cmd_settlv); if (good_cmd != cmd_success) return good_cmd; value = strtoul(argvalue, &endp, 0); if (*endp != '\0' || value > limit) return cmd_bad_params; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (test) return cmd_success; snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX, cmd->tlvid, arg); if (set_cfg(ed->ifname, cmd->type, arg_path, &value, CONFIG_TYPE_INT)){ LLDPAD_ERR("%s: error saving EVB %s (%d)\n", ed->ifname, arg, value); return cmd_failed; } LLDPAD_INFO("%s: changed EVB %s (%s)\n", ed->ifname, arg, argvalue); (*fct)(ed, value); somethingChangedLocal(cmd->ifname, cmd->type); return cmd_success; } static int show_31bit(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len, int (*fct)(struct evb22_data *)) { struct evb22_data *ed; char s[EVB_BUF_SIZE]; cmd_status good_cmd = evb22_cmdok(cmd, cmd_gettlv); if (good_cmd != cmd_success) return good_cmd; ed = evb22_data((char *) &cmd->ifname, cmd->type); if (!ed) return cmd_invalid; if (sprintf(s, "%i", (*fct)(ed)) <= 0) return cmd_invalid; snprintf(obuf, obuf_len, "%02x%s%04x%s", (unsigned int)strlen(arg), arg, (unsigned int)strlen(s), s); return cmd_success; } static int get_arg_retries(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_31bit(cmd, arg, argvalue, obuf, obuf_len, get_retries); } static int set_arg_retries(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, false, set_retries, 7); } static int test_arg_retries(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, true, 0, 7); } static int get_arg_rte(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_31bit(cmd, arg, argvalue, obuf, obuf_len, get_rte); } static int set_arg_rte(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, false, set_rte, 31); } static int test_arg_rte(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, true, 0, 31); } static int get_arg_rwd(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_31bit(cmd, arg, argvalue, obuf, obuf_len, get_rwd); } static int set_arg_rwd(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, false, set_rwd, 31); } static int test_arg_rwd(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, true, 0, 31); } static int get_arg_rka(struct cmd *cmd, char *arg, UNUSED char *argvalue, char *obuf, int obuf_len) { return show_31bit(cmd, arg, argvalue, obuf, obuf_len, get_rka); } static int set_arg_rka(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, false, set_rka, 31); } static int test_arg_rka(struct cmd *cmd, char *arg, char *argvalue, UNUSED char *obuf, UNUSED int obuf_len) { return scan_31bit(cmd, arg, argvalue, true, 0, 31); } static struct arg_handlers arg_handlers[] = { { .arg = EVB_CONF_RKA, .arg_class = TLV_ARG, .handle_get = get_arg_rka, .handle_set = set_arg_rka, .handle_test = test_arg_rka }, { .arg = EVB_CONF_RWD, .arg_class = TLV_ARG, .handle_get = get_arg_rwd, .handle_set = set_arg_rwd, .handle_test = test_arg_rwd }, { .arg = EVB_CONF_RTE, .arg_class = TLV_ARG, .handle_get = get_arg_rte, .handle_set = set_arg_rte, .handle_test = test_arg_rte }, { .arg = EVB_CONF_RETRIES, .arg_class = TLV_ARG, .handle_get = get_arg_retries, .handle_set = set_arg_retries, .handle_test = test_arg_retries }, { .arg = EVB_CONF_RRREQ, .arg_class = TLV_ARG, .handle_get = get_arg_rrreq, .handle_set = set_arg_rrreq, .handle_test = test_arg_rrreq }, { .arg = EVB_CONF_RRCAP, .arg_class = TLV_ARG, .handle_get = get_arg_rrcap, .handle_set = set_arg_rrcap, .handle_test = test_arg_rrcap }, { .arg = EVB_CONF_GPID, .arg_class = TLV_ARG, .handle_get = get_arg_gpid, .handle_set = set_arg_gpid, .handle_test = test_arg_gpid }, { .arg = EVB_CONF_MODE, .arg_class = TLV_ARG, .handle_get = get_arg_evbmode, .handle_set = set_arg_evbmode, .handle_test = test_arg_evbmode }, { .arg = ARG_TLVTXENABLE, .arg_class = TLV_ARG, .handle_get = get_arg_tlvtxenable, .handle_set = set_arg_tlvtxenable, .handle_test = test_arg_tlvtxenable }, { .arg = 0 } }; struct arg_handlers *evb22_get_arg_handlers() { return &arg_handlers[0]; } lldpad-0.9.46/qbg/lldp_vdp22.c000066400000000000000000000156071215142747300157560ustar00rootroot00000000000000/****************************************************************************** Implementation of EVB TLVs for LLDP (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ #define _GNU_SOURCE #include #include #include #include #include "messages.h" #include "config.h" #include "lldp_vdpnl.h" #include "lldp_qbg22.h" #include "lldp_vdp22.h" /* * VDP22 helper functions */ /* * Find the vdp data associated with an interface. * Return pointer or NULL if not found. */ static struct vdp22 *vdp22_findif(const char *ifname, struct vdp22_user_data *ud) { struct vdp22 *vdp = 0; if (ud) { LIST_FOREACH(vdp, &ud->head, entry) if (!strncmp(ifname, vdp->ifname, IFNAMSIZ)) break; } return vdp; } /* * Update data exchanged via ECP protocol. * Returns true when data update succeeded. */ static int data_from_ecp(char *ifname, struct ecp22_to_ulp *ptr) { struct vdp22_user_data *vud; struct vdp22 *vdp; vud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP22); vdp = vdp22_findif(ifname, vud); if (vdp) { memcpy(vdp->input, ptr->data, ptr->len); vdp->input_len = ptr->len; return 0; } return -ENOENT; } /* * Update data exchanged via EVB protocol. * Returns true when data update succeeded. */ static int data_from_evb(char *ifname, struct evb22_to_vdp22 *ptr) { struct vdp22_user_data *vud; struct vdp22 *vdp; vud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP22); vdp = vdp22_findif(ifname, vud); if (vdp) { vdp->max_rwd = ptr->max_rwd; vdp->max_rka = ptr->max_rka; vdp->gpid = ptr->gpid; return 0; } return -ENOENT; } /* * Handle notifications from other modules. Check if sender-id and data type * indicator match. Return false when data could not be delivered. */ static int vdp22_notify(int sender_id, char *ifname, void *data) { struct qbg22_imm *qbg = (struct qbg22_imm *)data; LLDPAD_DBG("%s:%s sender-id:%#x data_type:%d\n", __func__, ifname, sender_id, qbg->data_type); if (sender_id == LLDP_MOD_EVB22 && qbg->data_type == EVB22_TO_VDP22) return data_from_evb(ifname, &qbg->u.b); if (sender_id == LLDP_MOD_ECP22 && qbg->data_type == ECP22_TO_ULP) return data_from_ecp(ifname, &qbg->u.c); return 0; } /* * Remove a vdp22 element and delete the chain of active profiles. */ static void vdp22_free_elem(struct vdp22 *vdp) { while (!LIST_EMPTY(&vdp->prof22_head)) { struct vsi22_profile *prof = LIST_FIRST(&vdp->prof22_head); free(prof); } LIST_REMOVE(vdp, entry); free(vdp); } /* * Disable the interface for VDP protocol support. */ void vdp22_stop(char *ifname) { struct vdp22_user_data *vud; struct vdp22 *vdp; LLDPAD_DBG("%s:%s stop vdp\n", __func__, ifname); vud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP22); if (!vud) { LLDPAD_ERR("%s:%s no VDP22 module\n", __func__, ifname); return; } vdp = vdp22_findif(ifname, vud); if (!vdp) { LLDPAD_ERR("%s:%s no VDP22 data\n", __func__, ifname); return; } vdp22_free_elem(vdp); } /* * vdp22_create - create data structure and initialize vdp protocol * @ifname: interface for which the vdp protocol is initialized * * returns NULL on error and an pointer to the vdp22 structure on success. * * finds the port to the interface name, sets up the receive handle for * incoming vdp frames and initializes the vdp rx and tx state machines. * To be called when a successful exchange of EVB TLVs has been * made and ECP protocols are supported by both sides. */ static struct vdp22 *vdp22_create(const char *ifname, struct vdp22_user_data *eud) { struct vdp22 *vdp; vdp = calloc(1, sizeof *vdp); if (!vdp) { LLDPAD_ERR("%s:%s unable to allocate vdp protocol\n", __func__, ifname); return NULL; } strncpy(vdp->ifname, ifname, sizeof vdp->ifname); LIST_INIT(&vdp->prof22_head); LIST_INSERT_HEAD(&eud->head, vdp, entry); LLDPAD_DBG("%s:%s create vdp data\n", __func__, ifname); return vdp; } /* * Query the supported VDP protocol on an interface. */ static struct vdp22 *vdp22_getvdp(const char *ifname) { struct vdp22_user_data *vud; struct vdp22 *vdp = NULL; vud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP22); if (vud) vdp = vdp22_findif(ifname, vud); LLDPAD_DBG("%s:%s vdp %p\n", __func__, ifname, vdp); return vdp; } int vdp22_query(const char *ifname) { int rc = 0; if (vdp22_getvdp(ifname)) rc = 1; LLDPAD_DBG("%s:%s rc:%d\n", __func__, ifname, rc); return rc; } /* * Enable the interface for VDP protocol support. */ void vdp22_start(const char *ifname) { struct vdp22_user_data *vud; struct vdp22 *vdp; LLDPAD_DBG("%s:%s start vdp\n", __func__, ifname); vud = find_module_user_data_by_id(&lldp_head, LLDP_MOD_VDP22); if (!vud) { LLDPAD_ERR("%s:%s no VDP22 module\n", __func__, ifname); return; } vdp = vdp22_findif(ifname, vud); if (!vdp) vdp = vdp22_create(ifname, vud); } /* * Handle a VSI request from buddy. */ int vdp22_request(struct vdpnl_vsi *vsi) { int rc = -ENODEV; LLDPAD_DBG("%s:%s rc:%d\n", __func__, vsi->ifname, rc); return rc; } /* * Remove all interface/agent specific vdp data. */ static void vdp22_free_data(struct vdp22_user_data *ud) { if (ud) { while (!LIST_EMPTY(&ud->head)) { struct vdp22 *vd = LIST_FIRST(&ud->head); vdp22_free_elem(vd); } } } void vdp22_unregister(struct lldp_module *mod) { if (mod->data) { vdp22_free_data((struct vdp22_user_data *)mod->data); free(mod->data); } free(mod); LLDPAD_DBG("%s:done\n", __func__); } static const struct lldp_mod_ops vdp22_ops = { .lldp_mod_register = vdp22_register, .lldp_mod_unregister = vdp22_unregister, .lldp_mod_notify = vdp22_notify }; struct lldp_module *vdp22_register(void) { struct lldp_module *mod; struct vdp22_user_data *ud; mod = calloc(1, sizeof *mod); if (!mod) { LLDPAD_ERR("%s: failed to malloc module data\n", __func__); return NULL; } ud = calloc(1, sizeof *ud); if (!ud) { free(mod); LLDPAD_ERR("%s failed to malloc module user data\n", __func__); return NULL; } LIST_INIT(&ud->head); mod->id = LLDP_MOD_VDP22; mod->ops = &vdp22_ops; mod->data = ud; LLDPAD_DBG("%s:done\n", __func__); return mod; } lldpad-0.9.46/qbg/lldp_vdpnl.c000066400000000000000000000353701215142747300161430ustar00rootroot00000000000000/****************************************************************************** Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ /* * Contains netlink message parsing for VDP protocol from libvirtd or other * buddies. */ #include #include #include #include #include #include #include #include "messages.h" #include "lldp_vdp.h" #include "lldp_vdp22.h" #include "lldp_vdpnl.h" #include "lldp_qbg_utils.h" #include "lldp_rtnl.h" static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] = { [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac), .maxlen = sizeof(struct ifla_vf_mac)}, [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan), .maxlen = sizeof(struct ifla_vf_vlan)}, }; static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] = { [IFLA_PORT_VF] = { .type = NLA_U32 }, [IFLA_PORT_PROFILE] = { .type = NLA_STRING }, [IFLA_PORT_VSI_TYPE] = { .minlen = sizeof(struct ifla_port_vsi) }, [IFLA_PORT_INSTANCE_UUID] = { .minlen = PORT_UUID_MAX, .maxlen = PORT_UUID_MAX, }, [IFLA_PORT_HOST_UUID] = { .minlen = PORT_UUID_MAX, .maxlen = PORT_UUID_MAX, }, [IFLA_PORT_REQUEST] = { .type = NLA_U8 }, [IFLA_PORT_RESPONSE] = { .type = NLA_U16 }, }; /* * Retrieve name of interface and its index value from the netlink messaage * and store it in the data structure. * The GETLINK message may or may not contain the IFLA_IFNAME attribute. * Return 0 on success and errno on error. */ static int vdpnl_get(struct nlmsghdr *nlh, struct vdpnl_vsi *p) { struct nlattr *tb[IFLA_MAX + 1]; struct ifinfomsg *ifinfo; if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), (struct nlattr **)&tb, IFLA_MAX, NULL)) { LLDPAD_ERR("%s: error parsing GETLINK request\n", __func__); return -EINVAL; } ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); p->ifindex = ifinfo->ifi_index; if (tb[IFLA_IFNAME]) { memcpy(p->ifname, (char *)RTA_DATA(tb[IFLA_IFNAME]), sizeof p->ifname); } else if (!if_indextoname(p->ifindex, p->ifname)) { LLDPAD_ERR("%s: ifindex %d without interface name\n", __func__, p->ifindex); return -EINVAL; } LLDPAD_DBG("%s: IFLA_IFNAME:%s ifindex:%d\n", __func__, p->ifname, p->ifindex); return 0; } static void vdpnl_show(struct vdpnl_vsi *vsi) { char instance[VDP_UUID_STRLEN + 2]; struct vdpnl_mac *mac; int i; LLDPAD_DBG("%s: IFLA_IFNAME=%s index:%d\n", __func__, vsi->ifname, vsi->ifindex); for (i = 0, mac = vsi->maclist; i < vsi->macsz; ++i, ++mac) { LLDPAD_DBG("%s: IFLA_VF_MAC=%2x:%2x:%2x:%2x:%2x:%2x\n", __func__, mac->mac[0], mac->mac[1], mac->mac[2], mac->mac[3], mac->mac[4], mac->mac[5]); LLDPAD_DBG("%s: IFLA_VF_VLAN=%d\n", __func__, mac->vlan); } LLDPAD_DBG("%s: IFLA_PORT_VSI_TYPE=mgr_id:%d type_id:%ld " "typeid_version:%d\n", __func__, vsi->vsi_mgrid, vsi->vsi_typeid, vsi->vsi_typeversion); vdp_uuid2str(vsi->vsi_uuid, instance, sizeof(instance)); LLDPAD_DBG("%s: IFLA_PORT_INSTANCE_UUID=%s\n", __func__, instance); LLDPAD_DBG("%s: IFLA_PORT_REQUEST=%d\n", __func__, vsi->request); LLDPAD_DBG("%s: IFLA_PORT_RESPONSE=%d\n", __func__, vsi->response); } /* * Parse the IFLA_IFLA_VF_PORTIFLA_VF_PORTS block of the netlink message. * Return zero on success and errno else. */ static int vdpnl_vfports(struct nlattr *vfports, struct vdpnl_vsi *vsi) { char instance[VDP_UUID_STRLEN + 2]; struct nlattr *tb_vf_ports, *tb3[IFLA_PORT_MAX + 1]; int rem; if (!vfports) { LLDPAD_DBG("%s: FOUND NO IFLA_VF_PORTS\n", __func__); return -EINVAL; } nla_for_each_nested(tb_vf_ports, vfports, rem) { if (nla_type(tb_vf_ports) != IFLA_VF_PORT) { LLDPAD_DBG("%s: not a IFLA_VF_PORT skipping\n", __func__); continue; } if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports, ifla_port_policy)) { LLDPAD_ERR("%s: IFLA_PORT_MAX parsing failed\n", __func__); return -EINVAL; } if (tb3[IFLA_PORT_VF]) LLDPAD_DBG("%s: IFLA_PORT_VF=%d\n", __func__, *(uint32_t *) RTA_DATA(tb3[IFLA_PORT_VF])); if (tb3[IFLA_PORT_PROFILE]) LLDPAD_DBG("%s: IFLA_PORT_PROFILE=%s\n", __func__, (char *)RTA_DATA(tb3[IFLA_PORT_PROFILE])); if (tb3[IFLA_PORT_HOST_UUID]) { unsigned char *uuid; uuid = (unsigned char *) RTA_DATA(tb3[IFLA_PORT_HOST_UUID]); vdp_uuid2str(uuid, instance, sizeof(instance)); LLDPAD_DBG("%s: IFLA_PORT_HOST_UUID=%s\n", __func__, instance); } if (tb3[IFLA_PORT_VSI_TYPE]) { struct ifla_port_vsi *pvsi; int tid = 0; pvsi = (struct ifla_port_vsi *) RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); tid = pvsi->vsi_type_id[2] << 16 | pvsi->vsi_type_id[1] << 8 | pvsi->vsi_type_id[0]; vsi->vsi_mgrid = pvsi->vsi_mgr_id; vsi->vsi_typeversion = pvsi->vsi_type_version; vsi->vsi_typeid = tid; } if (tb3[IFLA_PORT_INSTANCE_UUID]) { unsigned char *uuid = (unsigned char *) RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); memcpy(vsi->vsi_uuid, uuid, sizeof vsi->vsi_uuid); } if (tb3[IFLA_PORT_REQUEST]) vsi->request = *(uint8_t *) RTA_DATA(tb3[IFLA_PORT_REQUEST]); if (tb3[IFLA_PORT_RESPONSE]) vsi->response = *(uint16_t *) RTA_DATA(tb3[IFLA_PORT_RESPONSE]); } return 0; } /* * Parse the IFLA_VFINFO_LIST block of the netlink message. * Return zero on success and errno else. */ static int vdpnl_vfinfolist(struct nlattr *vfinfolist, struct vdpnl_vsi *vsi) { struct nlattr *le1, *vf[IFLA_VF_MAX + 1]; int rem; if (!vfinfolist) { LLDPAD_ERR("%s: IFLA_VFINFO_LIST missing\n", __func__); return -EINVAL; } nla_for_each_nested(le1, vfinfolist, rem) { if (nla_type(le1) != IFLA_VF_INFO) { LLDPAD_ERR("%s: parsing of IFLA_VFINFO_LIST failed\n", __func__); return -EINVAL; } if (nla_parse_nested(vf, IFLA_VF_MAX, le1, ifla_vf_policy)) { LLDPAD_ERR("%s: parsing of IFLA_VF_INFO failed\n", __func__); return -EINVAL; } if (vf[IFLA_VF_MAC]) { struct ifla_vf_mac *mac = RTA_DATA(vf[IFLA_VF_MAC]); memcpy(vsi->maclist->mac, mac->mac, ETH_ALEN); } if (vf[IFLA_VF_VLAN]) { struct ifla_vf_vlan *vlan = RTA_DATA(vf[IFLA_VF_VLAN]); vsi->maclist->vlan = vlan->vlan; } } return 0; } /* * Convert the SETLINK message into internal data structure. */ static int vdpnl_set(struct nlmsghdr *nlh, struct vdpnl_vsi *vsi) { struct nlattr *tb[IFLA_MAX + 1]; struct ifinfomsg *ifinfo = (struct ifinfomsg *)NLMSG_DATA(nlh); int rc; if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), (struct nlattr **)&tb, IFLA_MAX, NULL)) { LLDPAD_ERR("%s: error parsing SETLINK request\n", __func__); return -EINVAL; } vsi->ifindex = ifinfo->ifi_index; if (tb[IFLA_IFNAME]) strncpy(vsi->ifname, (char *)RTA_DATA(tb[IFLA_IFNAME]), sizeof vsi->ifname); else { if (!if_indextoname(ifinfo->ifi_index, vsi->ifname)) { LLDPAD_ERR("%s: can not find name for interface %i\n", __func__, ifinfo->ifi_index); return -ENXIO; } } vsi->req_pid = nlh->nlmsg_pid; vsi->req_seq = nlh->nlmsg_seq; rc = vdpnl_vfinfolist(tb[IFLA_VFINFO_LIST], vsi); if (!rc) { rc = vdpnl_vfports(tb[IFLA_VF_PORTS], vsi); if (!rc) vdpnl_show(vsi); } return rc; } /* * Return the error code (can be zero) to the sender. Assume buffer is * large enough to hold the information. * Construct the netlink response on the input buffer. */ static int vdpnl_error(int err, struct nlmsghdr *from, size_t len) { struct nlmsgerr nlmsgerr; LLDPAD_DBG("%s: error %d\n", __func__, err); nlmsgerr.error = err; nlmsgerr.msg = *from; memset(from, 0, len); from->nlmsg_type = NLMSG_ERROR; from->nlmsg_seq = nlmsgerr.msg.nlmsg_seq; from->nlmsg_pid = nlmsgerr.msg.nlmsg_pid; from->nlmsg_flags = 0; from->nlmsg_len = NLMSG_SPACE(sizeof nlmsgerr); memcpy(NLMSG_DATA(from), &nlmsgerr, sizeof nlmsgerr); return from->nlmsg_len; } /* * Build the first part of the netlink reply message for status inquiry. * It contains the header and the ifinfo data structure. */ static void vdpnl_reply1(struct vdpnl_vsi *p, struct nlmsghdr *nlh, size_t len) { struct nlmsghdr to; struct ifinfomsg ifinfo; to.nlmsg_type = NLMSG_DONE; to.nlmsg_seq = nlh->nlmsg_seq; to.nlmsg_pid = nlh->nlmsg_pid; to.nlmsg_flags = 0; to.nlmsg_len = NLMSG_SPACE(sizeof ifinfo); memset(&ifinfo, 0, sizeof ifinfo); ifinfo.ifi_index = p->ifindex; memset(nlh, 0, len); memcpy(nlh, &to, sizeof to); memcpy(NLMSG_DATA(nlh), &ifinfo, sizeof ifinfo); } /* * Build the variable part of the netlink reply message for status inquiry. * It contains the UUID and the response field for the VSI profile. */ static void vdpnl_reply2(struct vdpnl_vsi *p, struct nlmsghdr *nlh) { char instance[VDP_UUID_STRLEN + 2]; mynla_put(nlh, IFLA_PORT_INSTANCE_UUID, sizeof p->vsi_uuid, p->vsi_uuid); vdp_uuid2str(p->vsi_uuid, instance, sizeof instance); LLDPAD_DBG("%s: IFLA_PORT_INSTANCE_UUID=%s\n", __func__, instance); mynla_put_u32(nlh, IFLA_PORT_VF, PORT_SELF_VF); LLDPAD_DBG("%s: IFLA_PORT_VF=%d\n", __func__, PORT_SELF_VF); if (p->response != VDP_RESPONSE_NO_RESPONSE) { mynla_put_u16(nlh, IFLA_PORT_RESPONSE, p->response); LLDPAD_DBG("%s: IFLA_PORT_RESPONSE=%d\n", __func__, p->response); } } /* * Extract the interface name and loop over all VSI profile entries. * Find UUID and response field for each active profile and construct a * netlink response message. * * Return message size. */ static int vdpnl_getlink(struct nlmsghdr *nlh, size_t len) { struct vdpnl_vsi p; int i = 0, rc; struct nlattr *vf_ports, *vf_port; memset(&p, 0, sizeof p); rc = vdpnl_get(nlh, &p); if (rc) return vdpnl_error(rc, nlh, len); vdpnl_reply1(&p, nlh, len); vf_ports = mynla_nest_start(nlh, IFLA_VF_PORTS); vf_port = mynla_nest_start(nlh, IFLA_VF_PORT); /* Iterate over all profiles */ do { rc = vdp_status(++i, &p); if (rc == 1) vdpnl_reply2(&p, nlh); if (rc == 0) { mynla_nest_end(nlh, vf_port); mynla_nest_end(nlh, vf_ports); } } while (rc == 1); if (rc < 0) return vdpnl_error(rc, nlh, len); LLDPAD_DBG("%s: message-size:%d\n", __func__, nlh->nlmsg_len); return nlh->nlmsg_len; } /* * Parse incoming command and create a data structure to store the VSI data. */ static int vdpnl_setlink(struct nlmsghdr *nlh, size_t len) { int rc = -ENOMEM; struct vdpnl_mac mac; struct vdpnl_vsi p; memset(&p, 0, sizeof p); p.macsz = 1; p.maclist = &mac; rc = vdpnl_set(nlh, &p); if (!rc) rc = vdp22_query(p.ifname) ? vdp22_request(&p) : vdp_request(&p); return vdpnl_error(rc, nlh, len); } /* * Process the netlink message. Parameters are the socket, the message and * its length in bytes. * The message buffer 'buf' is used for parsing the incoming message. * After parsing and decoding, the outgoing message is stored in 'buf'. * * Returns: * < 0: Errno number when message parsing failed. * == 0: Message ok and no response. * > 0: Message ok and response returned in buf parameter. Returns bytes * of response. */ int vdpnl_recv(unsigned char *buf, size_t buflen) { struct nlmsghdr *nlh = (struct nlmsghdr *)buf; LLDPAD_DBG("%s: buflen:%zd nlh.nl_pid:%d nlh_type:%d nlh_seq:%d " "nlh_len:%d\n", __func__, buflen, nlh->nlmsg_pid, nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_len); switch (nlh->nlmsg_type) { case RTM_SETLINK: return vdpnl_setlink(nlh, buflen); case RTM_GETLINK: return vdpnl_getlink(nlh, buflen); default: LLDPAD_ERR("%s: unknown type %d\n", __func__, nlh->nlmsg_type); } return -ENODEV; } /* * Add one entry in the list of MAC,VLAN pairs. */ static void add_pair(struct vdpnl_mac *mac, struct nlmsghdr *nlh) { struct nlattr *vfinfo; struct ifla_vf_mac ifla_vf_mac = { .vf = PORT_SELF_VF, .mac = { 0, } }; struct ifla_vf_vlan ifla_vf_vlan = { .vf = PORT_SELF_VF, .vlan = mac->vlan, .qos = mac->qos }; vfinfo = mynla_nest_start(nlh, IFLA_VF_INFO); memcpy(ifla_vf_mac.mac, mac->mac, sizeof mac->mac); mynla_put(nlh, IFLA_VF_MAC, sizeof ifla_vf_mac, &ifla_vf_mac); mynla_put(nlh, IFLA_VF_VLAN, sizeof ifla_vf_vlan, &ifla_vf_vlan); mynla_nest_end(nlh, vfinfo); } /* * Walk along the MAC,VLAN ID list and add each entry into the message. */ static void add_mac_vlan(struct vdpnl_vsi *vsi, struct nlmsghdr *nlh) { struct nlattr *vfinfolist; int i; vfinfolist = mynla_nest_start(nlh, IFLA_VFINFO_LIST); for (i = 0; i < vsi->macsz; ++i) add_pair(&vsi->maclist[i], nlh); mynla_nest_end(nlh, vfinfolist); } /* * Build an unsolicited netlink message to the VSI requestor. The originator * is the switch abondoning the VSI profile. * Assumes the messages fits into an 4KB buffer. * Returns the message size in bytes. */ int vdpnl_send(struct vdpnl_vsi *vsi) { unsigned char buf[MAX_PAYLOAD]; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; struct nlattr *vf_ports, *vf_port; struct ifinfomsg ifinfo; struct ifla_port_vsi portvsi; memset(buf, 0, sizeof buf); nlh->nlmsg_pid = getpid(); nlh->nlmsg_seq = vsi->req_seq; nlh->nlmsg_type = RTM_SETLINK; nlh->nlmsg_len = NLMSG_SPACE(sizeof ifinfo); memset(&ifinfo, 0, sizeof ifinfo); ifinfo.ifi_index = vsi->ifindex; memcpy(NLMSG_DATA(nlh), &ifinfo, sizeof ifinfo); mynla_put(nlh, IFLA_IFNAME, 1 + strlen(vsi->ifname), vsi->ifname); add_mac_vlan(vsi, nlh); portvsi.vsi_mgr_id = vsi->vsi_mgrid; portvsi.vsi_type_id[0] = vsi->vsi_typeid & 0xff; portvsi.vsi_type_id[1] = (vsi->vsi_typeid >> 8) & 0xff; portvsi.vsi_type_id[2] = (vsi->vsi_typeid >> 16) & 0xff; portvsi.vsi_type_version = vsi->vsi_typeversion; vf_ports = mynla_nest_start(nlh, IFLA_VF_PORTS); vf_port = mynla_nest_start(nlh, IFLA_VF_PORT); mynla_put(nlh, IFLA_PORT_VSI_TYPE, sizeof portvsi, &portvsi); mynla_put(nlh, IFLA_PORT_INSTANCE_UUID, PORT_UUID_MAX, vsi->vsi_uuid); mynla_put_u32(nlh, IFLA_PORT_VF, PORT_SELF_VF); mynla_put_u16(nlh, IFLA_PORT_REQUEST, vsi->request); mynla_nest_end(nlh, vf_port); mynla_nest_end(nlh, vf_ports); vdpnl_show(vsi); LLDPAD_DBG("%s: nlh.nl_pid:%d nlh_type:%d nlh_seq:%d nlh_len:%d\n", __func__, nlh->nlmsg_pid, nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_len); return event_trigger(nlh, vsi->req_pid); } lldpad-0.9.46/test/000077500000000000000000000000001215142747300140375ustar00rootroot00000000000000lldpad-0.9.46/test/nltest.c000066400000000000000000001153401215142747300155200ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include "nltest.h" #define MAX_ADDR_LEN 32 /* instead of including linux/netdevice.h */ #ifdef DCB_APP_IDTYPE_ETHTYPE #define DCB_APP_DRV_IF_SUPPORTED #endif static int hexdump; static void hexprint(char *b, int len) { int i; for (i = 0; i < len; i++) { if (i%16 == 0) printf("%s\t", i?"\n":""); printf("%02x ", (unsigned char)*(b + i)); } printf("\n\n"); } #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) static int init_socket(void) { int sd; int rcv_size = 8 * 1024; struct sockaddr_nl snl; sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sd < 0) return sd; if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { close(sd); return -EIO; } memset((void *)&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); /* snl.nl_groups = RTMGRP_LINK; */ if (bind(sd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) { close(sd); return -EIO; } return sd; } static struct nlmsghdr *start_msg(__u16 msg_type, __u8 arg) { struct nlmsghdr *nlh; struct dcbmsg *d; struct ifinfomsg *ifi; nlh = (struct nlmsghdr *)malloc(MAX_MSG_SIZE); if (NULL==nlh) return NULL; memset((void *)nlh, 0, MAX_MSG_SIZE); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = 0; nlh->nlmsg_pid = getpid(); switch (msg_type) { case RTM_GETDCB: case RTM_SETDCB: nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct dcbmsg)); d = NLMSG_DATA(nlh); d->cmd = arg; d->dcb_family = AF_UNSPEC; d->dcb_pad = 0; break; case RTM_GETLINK: nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); ifi = NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = arg; ifi->ifi_change = 0xffffffff; break; default: free(nlh); return NULL; break; } return nlh; } int addattr_l(struct nlmsghdr *n, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > MAX_MSG_SIZE) { fprintf(stderr, "addattr_l: message exceeded bound of %d\n", MAX_MSG_SIZE); return -1; } rta = NLMSG_TAIL(n); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return 0; } struct rtattr *addattr_nest(struct nlmsghdr *n, int type) { struct rtattr *nest = NLMSG_TAIL(n); addattr_l(n, type, NULL, 0); return nest; } int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; return n->nlmsg_len; } int parse_rtattr(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])) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta, len); } if (len) fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); return 0; } #define parse_rtattr_nested(tb, max, rta) \ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) static struct rtattr *add_rta(struct nlmsghdr *nlh, __u16 rta_type, void *attr, __u16 rta_len) { struct rtattr *rta; rta = (struct rtattr *)((char *)nlh + nlh->nlmsg_len); rta->rta_type = rta_type; rta->rta_len = rta_len + NLA_HDRLEN; if (attr) memcpy(NLA_DATA(rta), attr, rta_len); nlh->nlmsg_len += NLMSG_ALIGN(rta->rta_len); return rta; } static int send_msg(struct nlmsghdr *nlh) { struct sockaddr_nl nladdr; void *buf = (void *)nlh; int r, len = nlh->nlmsg_len; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; do { if (hexdump) { printf("SENT A MESSAGE: %d\n", len); hexprint((char *)nlh, nlh->nlmsg_len); } r = sendto(nl_sd, buf, len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); } while (r < 0 && errno == EINTR); if (r < 0) { printf("SEND FAILED: %d\n", r); return 1; } else return 0; } static struct nlmsghdr *get_msg(void) { struct nlmsghdr *nlh; int len; nlh = (struct nlmsghdr *)malloc(MAX_MSG_SIZE); if (NULL==nlh) return NULL; memset(nlh, 0, MAX_MSG_SIZE); len = recv(nl_sd, (void *)nlh, MAX_MSG_SIZE, 0); if (len < 0) { printf("RECEIVE FAILED with %d", errno); free(nlh); return NULL; } if (!(NLMSG_OK(nlh, (unsigned int)len))) { printf("RECEIVE FAILED, message too long\n"); free(nlh); return NULL; } if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(nlh); printf("RECEIVE FAILED with msg error %i (pid %d): %s\n", err->error, nlh->nlmsg_pid, strerror(err->error * -1)); if (hexdump && len > 0) hexprint((char *)nlh, len); free(nlh); return NULL; } if (hexdump) { printf("RECEIVED A MESSAGE: %d\n", len); hexprint((char *)nlh, nlh->nlmsg_len); } return nlh; } static int recv_msg(int cmd, int attr) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; int rval; nlh = get_msg(); if (NULL == nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if ((d->cmd != cmd) || (rta->rta_type != attr)) { printf("Bad netlink message attribute."); return -EIO; } rval = *(__u8 *)NLA_DATA(rta); free(nlh); return rval; } static int set_state(char *ifname, __u8 state) { struct nlmsghdr *nlh; nlh = start_msg(RTM_SETDCB, DCB_CMD_SSTATE); if (NULL == nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_STATE, (void *)&state, sizeof(__u8)); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_SSTATE, DCB_ATTR_STATE)); } static int get_state(char *ifname, __u8 *state) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; nlh = start_msg(RTM_GETDCB, DCB_CMD_GSTATE); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); if (send_msg(nlh)) return -EIO; free(nlh); nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GSTATE) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta->rta_type != DCB_ATTR_STATE) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } *state = *(__u8 *)NLA_DATA(rta); return 0; } static int get_pfc_cfg(char *ifname, __u8 *pfc) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent, *rta_child; int i; nlh = start_msg(RTM_GETDCB, DCB_CMD_PFC_GCFG); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_PFC_CFG, NULL, 0); rta_child = add_rta(nlh, DCB_PFC_UP_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_PFC_GCFG) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta_parent->rta_type != DCB_ATTR_PFC_CFG) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); for (i = 0; rta_parent > rta_child; i++) { if (i == 8) { printf("pfc array out of range\n"); break; } pfc[rta_child->rta_type - DCB_PFC_UP_ATTR_0] = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } if (rta_parent != rta_child) printf("rta pointers are off\n"); return 0; } static int get_pfc_state(char *ifname, __u8 *state) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; nlh = start_msg(RTM_GETDCB, DCB_CMD_PFC_GSTATE); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); if (send_msg(nlh)) return -EIO; free(nlh); nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_PFC_GSTATE) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta->rta_type != DCB_ATTR_PFC_STATE) { return -EIO; } *state = *(__u8 *)NLA_DATA(rta); return 0; } static int get_pg(char *ifname, struct tc_config *tc, __u8 *bwg, int cmd) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *class_parent, *param_parent, *rta_child; __u8 *p = (__u8 *)tc; int i, j; nlh = start_msg(RTM_GETDCB, cmd); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); class_parent = add_rta(nlh, DCB_ATTR_PG_CFG, NULL, 0); param_parent = add_rta(nlh, DCB_PG_ATTR_TC_ALL, NULL, 0); rta_child = add_rta(nlh, DCB_TC_ATTR_PARAM_ALL, NULL, 0); param_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); class_parent->rta_len += NLMSG_ALIGN(param_parent->rta_len); rta_child = add_rta(nlh, DCB_PG_ATTR_BW_ID_ALL, NULL, 0); class_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); class_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != cmd) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (class_parent->rta_type != DCB_ATTR_PG_CFG) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } param_parent = NLA_DATA(class_parent); class_parent = (struct rtattr *)((char *)class_parent + NLMSG_ALIGN(class_parent->rta_len)); for (i = 0; class_parent > param_parent; i++) { if (param_parent->rta_type >= DCB_PG_ATTR_TC_0 && param_parent->rta_type < DCB_PG_ATTR_TC_MAX) { rta_child = NLA_DATA(param_parent); param_parent = (struct rtattr *)((char *)param_parent + NLMSG_ALIGN(param_parent->rta_len)); for (j = 0; param_parent > rta_child; j++) { if (j == DCB_TC_ATTR_PARAM_MAX - DCB_TC_ATTR_PARAM_UNDEFINED + 1) { printf("parameter array out of " "range: %d\n", j); break; } *p = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); p++; } if (param_parent != rta_child) { printf("param_parent and rta_child pointers " "are off\n"); } } else if (param_parent->rta_type >= DCB_PG_ATTR_BW_ID_0 && param_parent->rta_type < DCB_PG_ATTR_BW_ID_MAX) { j = param_parent->rta_type - DCB_PG_ATTR_BW_ID_0; bwg[j] = *(__u8 *)NLA_DATA(param_parent); param_parent = (struct rtattr *)((char *)param_parent + NLMSG_ALIGN(param_parent->rta_len)); } else printf("unknown param_parent type = %d\n", param_parent->rta_type); } if (class_parent != param_parent) printf("class_parent and param_parent pointers are off\n"); return 0; } static int get_perm_hwaddr(char *ifname, __u8 *buf_perm, __u8 *buf_san) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta; nlh = start_msg(RTM_GETDCB, DCB_CMD_GPERM_HWADDR); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_PERM_HWADDR, NULL, 0); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) { printf("get msg failed\n"); return -EIO; } d = (struct dcbmsg *)NLMSG_DATA(nlh); rta = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GPERM_HWADDR) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta->rta_type != DCB_ATTR_PERM_HWADDR) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } memcpy(buf_perm, NLA_DATA(rta), ETH_ALEN); memcpy(buf_san, NLA_DATA(rta + ETH_ALEN*sizeof(__u8)), ETH_ALEN); return 0; } static int get_cap(char *ifname, __u8 *cap) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent, *rta_child; int i; nlh = start_msg(RTM_GETDCB, DCB_CMD_GCAP); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_CAP, NULL, 0); rta_child = add_rta(nlh, DCB_CAP_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GCAP) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta_parent->rta_type != DCB_ATTR_CAP) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); for (i = 0; rta_parent > rta_child; i++) { if (i == 8) { printf("cap array out of range\n"); break; } cap[rta_child->rta_type] = *(__u8 *)NLA_DATA(rta_child); switch (rta_child->rta_type) { case DCB_CAP_ATTR_ALL: break; case DCB_CAP_ATTR_PG: printf("pg: "); break; case DCB_CAP_ATTR_PFC: printf("pfc: "); break; case DCB_CAP_ATTR_UP2TC: printf("up2tc: "); break; case DCB_CAP_ATTR_PG_TCS: printf("pg tcs: "); break; case DCB_CAP_ATTR_PFC_TCS: printf("pfc tcs: "); break; case DCB_CAP_ATTR_GSP: printf("gsp: "); break; case DCB_CAP_ATTR_BCN: printf("bcn: "); break; case DCB_CAP_ATTR_DCBX: printf("dcbx: "); break; default: printf("unknown type: "); break; } printf("%02x\n", cap[rta_child->rta_type]); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } if (rta_parent != rta_child) printf("rta pointers are off\n"); return 0; } /* returns: 0 on success * 1 on failure */ static int set_numtcs(char *ifname, int tcid, __u8 numtcs) { struct nlmsghdr *nlh; struct rtattr *rta_parent, *rta_child; printf("set_numtcs_cfg: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_SNUMTCS); if (NULL == nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_NUMTCS, NULL, 0); rta_child = add_rta(nlh, tcid, &numtcs, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_SNUMTCS, DCB_ATTR_NUMTCS)); } static int get_numtcs(char *ifname, int tcid, __u8 *numtcs) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent, *rta_child; int found; int i; nlh = start_msg(RTM_GETDCB, DCB_CMD_GNUMTCS); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_NUMTCS, NULL, 0); rta_child = add_rta(nlh, DCB_NUMTCS_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GNUMTCS) { printf("Hmm, this is not the message we were expecting.\n"); return -EIO; } if (rta_parent->rta_type != DCB_ATTR_NUMTCS) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); return -EIO; } rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); found = 0; for (i = 0; rta_parent > rta_child; i++) { if (!found && rta_child->rta_type == tcid) { *numtcs = *(__u8 *)NLA_DATA(rta_child); found = 1; } rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } if (rta_parent != rta_child) printf("rta pointers are off\n"); if (found) return 0; else return -1; } /* static int set_hw_all(char *ifname) { struct nlmsghdr *nlh; int status = 1; nlh = start_msg(CMD, DCB_CMD_SET_ALL); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); add_rta(nlh, DCB_ATTR_SET_ALL, (void *)&status, sizeof(__u8)); return send_msg(nlh); }*/ static int get_bcn(char *ifname, bcn_cfg *bcn_data) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent, *rta_child; int i, j; unsigned int temp_int; nlh = start_msg(RTM_GETDCB, DCB_CMD_BCN_GCFG); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_BCN, NULL, 0); rta_child = add_rta(nlh, DCB_BCN_ATTR_ALL, NULL, 0); rta_parent->rta_len += NLMSG_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) { printf("error getting BCN cfg.\n"); return -EIO; } d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_BCN_GCFG) { printf("Hmm, this is not the message we were expecting.\n"); free(nlh); return -EIO; } if (rta_parent->rta_type != DCB_ATTR_BCN) { /* Do we really want to code up an attribute parser?? */ printf("A full libnetlink (with rtnl and attribute support) " "would sure be nice.\n"); free(nlh); return -EIO; } rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); for (i = 0; rta_parent > rta_child; i++) { if (i == DCB_BCN_ATTR_RP_ALL - DCB_BCN_ATTR_RP_0) { printf("bcn param out of range\n"); break; } bcn_data->up_settings[rta_child->rta_type - DCB_BCN_ATTR_RP_0].rp_admin = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); } for (i = 0; i < BCN_ADDR_OPTION_LEN/4; i++) { /*2 bytes for BCNA data */ temp_int = *(__u32 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); for (j = 0; j < 4; j++) { bcn_data->bcna[j+i*4] = (__u8)((temp_int & (0xFF << (j*8))) >> (j*8)); } } memcpy((void *)&bcn_data->rp_alpha, (__u32 *)NLA_DATA(rta_child), sizeof(__u32)); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); memcpy((void *)&bcn_data->rp_beta, (__u32 *)NLA_DATA(rta_child), sizeof(__u32)); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); memcpy((void *)&bcn_data->rp_gd, (__u32 *)NLA_DATA(rta_child), sizeof(__u32)); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); memcpy((void *)&bcn_data->rp_gi, (__u32 *)NLA_DATA(rta_child), sizeof(__u32)); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_tmax = *(__u32 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_td = *(__u16 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_rmin = *(__u16 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_w = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_rd = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_ru = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_wrtt = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); bcn_data->rp_ri = *(__u32 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); if (rta_parent != rta_child) printf("rta pointers are off\n"); return 0; } /* returns: 0 on success * 1 on failure */ static int set_bcn_cfg(char *ifname, bcn_cfg *bcn_data) { struct nlmsghdr *nlh; struct rtattr *rta_parent, *rta_child; int i; int temp_int; printf("set_bcn_cfg: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_BCN_SCFG); if (NULL == nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_BCN, NULL, 0); for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) { rta_child = add_rta(nlh, i, (void *)&bcn_data->up_settings[i - DCB_BCN_ATTR_RP_0].rp_admin, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); } temp_int = 0; for (i = 0; i < BCN_ADDR_OPTION_LEN/2; i++) temp_int |= bcn_data->bcna[i]<<(i*8); rta_child = add_rta(nlh, DCB_BCN_ATTR_BCNA_0, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = 0; for (i = BCN_ADDR_OPTION_LEN/2; i < BCN_ADDR_OPTION_LEN; i++) temp_int |= bcn_data->bcna[i]<<((i- BCN_ADDR_OPTION_LEN/2)*8); rta_child = add_rta(nlh, DCB_BCN_ATTR_BCNA_1, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_ALPHA, (void *)&bcn_data->rp_alpha, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_BETA, (void *)&bcn_data->rp_beta, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_GD, (void *)&bcn_data->rp_gd, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_GI, (void *)&bcn_data->rp_gi, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_TMAX, (void *)&bcn_data->rp_tmax, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_td; rta_child = add_rta(nlh, DCB_BCN_ATTR_TD, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_rmin; rta_child = add_rta(nlh, DCB_BCN_ATTR_RMIN, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_w; rta_child = add_rta(nlh, DCB_BCN_ATTR_W, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_rd; rta_child = add_rta(nlh, DCB_BCN_ATTR_RD, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_ru; rta_child = add_rta(nlh, DCB_BCN_ATTR_RU, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); temp_int = (int)bcn_data->rp_wrtt; rta_child = add_rta(nlh, DCB_BCN_ATTR_WRTT, (void *)&temp_int, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_BCN_ATTR_RI, (void *)&bcn_data->rp_ri, sizeof(__u32)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_BCN_SCFG, DCB_ATTR_BCN)); } static int set_hw_bcn(char *device_name, bcn_cfg *bcn_data, __u8 oper_mode) { int i; bcn_cfg bcn_store; bcn_cfg *bcn_temp; oper_mode = 1; { for (i = 0; i <= 8; i++) { bcn_data->up_settings[i].rp_admin = 1; } bcn_data->rp_alpha = 0.5; bcn_data->rp_beta = 0.1; bcn_data->rp_gd = 0.00026; /* Based on other default parameters */ bcn_data->rp_gi = 0.53; /* Based on other default parameters */ bcn_data->rp_tmax = 100; bcn_data->rp_td = 100; bcn_data->rp_rmin = 100; bcn_data->rp_w = 9; bcn_data->rp_rd = 1; bcn_data->rp_ru = 1; bcn_data->rp_ri = 5001; bcn_data->rp_wrtt = 9; } if (!oper_mode) /* oper mode is false */ { //get_bcn(DEF_CFG_STORE, &bcn_store); bcn_temp = &bcn_store; } else { bcn_temp = bcn_data; } for (i = 0; i < 8; i++) { if (bcn_temp->up_settings[i].cp_admin) bcn_temp->up_settings[i].cp_admin = 1; else bcn_temp->up_settings[i].cp_admin = 0; } return set_bcn_cfg(device_name, bcn_temp); } #ifdef DCB_APP_DRV_IF_SUPPORTED static int get_app_cfg(char *ifname, appgroup_attribs *app_data) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *rta_parent, *rta_child; int rval = 0; __u8 idtype; __u16 id; nlh = start_msg(RTM_GETDCB, DCB_CMD_GAPP); if (NULL==nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_APP, NULL, 0); rta_child = add_rta(nlh, DCB_APP_ATTR_IDTYPE, (void *)&app_data->dcb_app_idtype, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_APP_ATTR_ID, (void *)&app_data->dcb_app_id, sizeof(__u16)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; nlh = get_msg(); if (!nlh) return -EIO; d = (struct dcbmsg *)NLMSG_DATA(nlh); rta_parent = (struct rtattr *)(((char *)d) + NLMSG_ALIGN(sizeof(struct dcbmsg))); if (d->cmd != DCB_CMD_GAPP) { printf("Hmm, this is not the message we were expecting.\n"); rval = -EIO; goto get_error; } if (rta_parent->rta_type != DCB_ATTR_APP) { printf("A full libnetlink (with genl and attribute support) " "would sure be nice.\n"); rval = -EIO; goto get_error; } rta_child = NLA_DATA(rta_parent); rta_parent = (struct rtattr *)((char *)rta_parent + NLMSG_ALIGN(rta_parent->rta_len)); idtype = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); if (idtype != app_data->dcb_app_idtype) { rval = -EIO; goto get_error; } id = *(__u16 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); if (id != app_data->dcb_app_id) { rval = -EIO; goto get_error; } app_data->dcb_app_priority = *(__u8 *)NLA_DATA(rta_child); rta_child = (struct rtattr *)((char *)rta_child + NLMSG_ALIGN(rta_child->rta_len)); if (rta_parent != rta_child) printf("rta pointers are off\n"); get_error: free(nlh); return rval; } int set_hw_app0(char *ifname, appgroup_attribs *app_data) { struct nlmsghdr *nlh; struct rtattr *rta_parent, *rta_child; printf("set_hw_app0: %s\n", ifname); nlh = start_msg(RTM_SETDCB, DCB_CMD_SAPP); if (NULL == nlh) return -EIO; add_rta(nlh, DCB_ATTR_IFNAME, (void *)ifname, strlen(ifname) + 1); rta_parent = add_rta(nlh, DCB_ATTR_APP, NULL, 0); rta_child = add_rta(nlh, DCB_APP_ATTR_IDTYPE, (void *)&app_data->dcb_app_idtype, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_APP_ATTR_ID, (void *)&app_data->dcb_app_id, sizeof(__u16)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); rta_child = add_rta(nlh, DCB_APP_ATTR_PRIORITY, (void *)&app_data->dcb_app_priority, sizeof(__u8)); rta_parent->rta_len += NLA_ALIGN(rta_child->rta_len); if (send_msg(nlh)) return -EIO; return(recv_msg(DCB_CMD_SAPP, DCB_ATTR_APP)); } #endif /* DCB_APP_DRV_IF_SUPPORTED */ void print_app(struct rtattr *app_attr) { struct dcb_app *data; data = RTA_DATA(app_attr); printf("selector %i protocol %i priority %i\n", data->selector, data->protocol, data->priority); } void print_pfc(struct ieee_pfc *pfc) { int i; printf("PFC:\n"); printf("\t cap %2x en %2x\n", pfc->pfc_cap, pfc->pfc_en); printf("\t mbc %2x delay %i\n", pfc->mbc, pfc->delay); printf("\t requests: "); for (i = 0; i < 8; i++) printf("%lli ", pfc->requests[i]); printf("\n"); printf("\t requests: "); for (i = 0; i < 8; i++) printf("%lli ", pfc->indications[i]); printf("\n"); } void print_ets(struct ieee_ets *ets) { int i; printf("ETS:\n"); printf("\tcap %2x cbs %2x\n", ets->ets_cap, ets->cbs); printf("\tets tc_tx_bw: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_tx_bw[i]); printf("\n"); printf("\tets tc_rx_bw: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_rx_bw[i]); printf("\n"); printf("\tets tc_tsa: "); for (i = 0; i < 8; i++) printf("%i ", ets->tc_tsa[i]); printf("\n"); printf("\tets prio_tc: "); for (i = 0; i < 8; i++) printf("%i ", ets->prio_tc[i]); printf("\n"); } int set_ieee(char *ifname, struct ieee_ets *ets_data, struct ieee_pfc *pfc_data, struct dcb_app *app_data) { struct nlmsghdr *nlh; struct rtattr *ieee, *apptbl; nlh = start_msg(RTM_SETDCB, DCB_CMD_IEEE_SET); if (NULL == nlh) return -EIO; addattr_l(nlh, DCB_ATTR_IFNAME, ifname, strlen(ifname) + 1); ieee = addattr_nest(nlh, DCB_ATTR_IEEE); if (ets_data) addattr_l(nlh, DCB_ATTR_IEEE_ETS, ets_data, sizeof(*ets_data)); if (pfc_data) addattr_l(nlh, DCB_ATTR_IEEE_PFC, pfc_data, sizeof(*pfc_data)); if (app_data) { apptbl = addattr_nest(nlh, DCB_ATTR_IEEE_APP_TABLE); addattr_l(nlh, DCB_ATTR_IEEE_APP, app_data, sizeof(*app_data)); #if 1 app_data->protocol++; addattr_l(nlh, DCB_ATTR_IEEE_APP, app_data, sizeof(*app_data)); #endif addattr_nest_end(nlh, apptbl); } addattr_nest_end(nlh, ieee); if (send_msg(nlh)) return -EIO; return recv_msg(DCB_CMD_IEEE_SET, DCB_ATTR_IEEE); } #define DCB_RTA(r) \ ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct dcbmsg)))) int get_ieee(char *ifname) { struct nlmsghdr *nlh; struct dcbmsg *d; struct rtattr *dcb, *ieee[DCB_ATTR_IEEE_MAX+1]; struct rtattr *tb[DCB_ATTR_MAX + 1]; int len; nlh = start_msg(RTM_GETDCB, DCB_CMD_IEEE_GET); if (NULL == nlh) { printf("start_msg failed\n"); return -EIO; } addattr_l(nlh, DCB_ATTR_IFNAME, ifname, strlen(ifname) + 1); if (send_msg(nlh)) { printf("send failure\n"); return -EIO; } /* Receive 802.1Qaz parameters */ memset(nlh, 0, MAX_MSG_SIZE); len = recv(nl_sd, (void *)nlh, MAX_MSG_SIZE, 0); if (len < 0) { perror("ieee_get"); return -EIO; } if (nlh->nlmsg_type != RTM_GETDCB) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(nlh); if (nlh->nlmsg_type == NLMSG_ERROR) { printf("NLMSG_ERROR: err(%i): %s\n", err->error, strerror(err->error * -1)); } return -1; } d = NLMSG_DATA(nlh); len -= NLMSG_LENGTH(sizeof(*d)); if (len < 0) { printf("Broken message\n"); return -1; } parse_rtattr(tb, DCB_ATTR_MAX, DCB_RTA(d), len); if (!tb[DCB_ATTR_IEEE]) { printf("Missing DCB_ATTR_IEEE attribute!\n"); return -1; } if (tb[DCB_ATTR_IFNAME]) { printf("\tifname %s\n", (char *)RTA_DATA(tb[DCB_ATTR_IFNAME])); } else { printf("Missing DCB_ATTR_IFNAME attribute!\n"); return -1; } dcb = tb[DCB_ATTR_IEEE]; parse_rtattr_nested(ieee, DCB_ATTR_IEEE_MAX, dcb); if (ieee[DCB_ATTR_IEEE_ETS]) { struct ieee_ets *ets = RTA_DATA(ieee[DCB_ATTR_IEEE_ETS]); print_ets(ets); } if (ieee[DCB_ATTR_IEEE_PFC]) { struct ieee_pfc *pfc = RTA_DATA(ieee[DCB_ATTR_IEEE_PFC]); print_pfc(pfc); } if (ieee[DCB_ATTR_IEEE_APP_TABLE]) { struct rtattr *i, *app_list = ieee[DCB_ATTR_IEEE_APP_TABLE]; int rem = RTA_PAYLOAD(app_list); printf("APP:\n"); for (i = RTA_DATA(app_list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) print_app(i); } return 0; } int main(int argc, char *argv[]) { struct tc_config tc[8]; int i, err = 0; int newstate = -1; int read_only = 0; __u8 state; __u8 pfc[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; __u8 bwg[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; __u8 mac[ETH_ALEN], san_mac[ETH_ALEN]; __u8 cap[DCB_CAP_ATTR_MAX+1]; __u8 numtcs; bcn_cfg bcn_set_data, bcn_data; #ifdef DCB_APP_DRV_IF_SUPPORTED appgroup_attribs app_data = {DCB_APP_IDTYPE_ETHTYPE, 0x8906, 0x08}; #endif /* DCB_APP_DRV_IF_SUPPORTED */ int ifindex; int optind = 1; char *ifname; printf("Calling RTNETLINK interface.\n"); if (argc < 2) { fprintf(stderr, "usage: %s [-v] [on|off|ro]\n", argv[0]); exit(1); } if (argc > 2) { if (!strcmp(argv[1], "-v")) { hexdump = 1; optind++; } if (argc > optind + 1) { if (!strcmp(argv[optind + 1], "on")) newstate = 1; if (!strcmp(argv[optind + 1], "off")) newstate = 0; if (!strcmp(argv[optind + 1], "ro")) read_only = 1; } } ifname = argv[optind]; ifindex = if_nametoindex(ifname); if (ifindex == 0) { printf("no ifindex for %s\n", ifname); exit(1); } if ((nl_sd = init_socket()) < 0) { fprintf(stderr, "error creating netlink socket\n"); return nl_sd; } #ifdef DO_GETLINK_QUERY printf("DOING A GETLINK COMMAND\n"); nlh = start_msg(RTM_GETLINK, ifindex); if (nlh == NULL) exit(1); if (send_msg(nlh)) exit(1); free(nlh); nlh = get_msg(); #endif printf("GETTING DCB STATE\n"); err = get_state(ifname, &state); if (err) { fprintf(stderr, "Error getting DCB state\n"); goto err_main; } printf("DCB State = %d\n", state); if (newstate >= 0) { printf("\nSETTING DCB STATE TO: %d\n", newstate); err = set_state(ifname, newstate); if (err) goto err_main; err = get_state(ifname, &state); if (err) { fprintf(stderr, "Error getting DCB state\n"); goto err_main; } printf("New DCB State = %d\n", state); } printf("\nGETTING PFC CONFIGURATION\n"); for (i=0; i<8; i++) pfc[i] = 0x0f; get_pfc_cfg(ifname, pfc); printf("PFC config:\n"); for (i=0; i<8; i++) printf("%x ", pfc[i]); printf("\n"); get_pfc_state(ifname, &state); if (err) { fprintf(stderr, "Error getting PFC status\n"); goto err_main; } printf("PFC State = %d\n", state); printf("\nGETTING PG TX CONFIGURATION\n"); get_pg(ifname, tc, bwg, DCB_CMD_PGTX_GCFG); for (i = 0; i < 8; i++) { printf("%d: pr=%d\tbwgid=%d\tbw%%=%d\tu2t=%d\tlk%%=%d\n", i, tc[i].prio_type, tc[i].bwg_id, tc[i].bwg_percent, tc[i].up_to_tc_bitmap, bwg[i]); tc[i].prio_type = 3; tc[i].bwg_id = i; tc[i].bwg_percent = 100; tc[i].up_to_tc_bitmap = i; bwg[i] = 12 + (i & 1); } printf("\nGETTING PG RX CONFIGURATION\n"); memset(bwg, 0, sizeof(bwg)); memset(&tc[0], 0, sizeof(tc)); get_pg(ifname, tc, bwg, DCB_CMD_PGRX_GCFG); for (i = 0; i < 8; i++) { printf("%d: pr=%d\tbwgid=%d\tbw%%=%d\tu2t=%d\tlk%%=%d\n", i, tc[i].prio_type, tc[i].bwg_id, tc[i].bwg_percent, tc[i].up_to_tc_bitmap, bwg[i]); } printf("\nGETTING PERMANENT MAC: "); get_perm_hwaddr(ifname, mac, san_mac); for (i = 0; i < 5; i++) printf("%02x:", mac[i]); printf("%02x\n", mac[i]); printf("\nGETTING SAN MAC: "); for (i = 0; i < 5; i++) printf("%02x:", san_mac[i]); printf("%02x\n", san_mac[i]); printf("\nGETTING DCB CAPABILITIES\n"); get_cap(ifname, &cap[0]); printf("\nGET NUMBER OF PG TCS\n"); if (!get_numtcs(ifname, DCB_NUMTCS_ATTR_PG, &numtcs)) printf("num = %d\n", numtcs); else printf("not found\n"); printf("\nGET NUMBER OF PFC TCS\n"); if (!get_numtcs(ifname, DCB_NUMTCS_ATTR_PFC, &numtcs)) printf("num = %d\n", numtcs); else printf("not found\n"); if (!read_only) { printf("\nTEST SET NUMBER OF PG TCS\n"); if (!set_numtcs(ifname, DCB_NUMTCS_ATTR_PG, numtcs)) printf("set passed\n"); else printf("error\n"); printf("\nTEST SET NUMBER OF PFC TCS\n"); if (!set_numtcs(ifname, DCB_NUMTCS_ATTR_PFC, numtcs)) printf("set passed\n"); else printf("error\n\n"); /* printf("set_pfc_cfg = %d\n", set_pfc_cfg(ifname, pfc)); */ /* printf("set_rx_pg = %d\n", set_pg(ifname, tc, bwg, DCB_CMD_PGRX_SCFG));*/ /* printf("set_hw_all = %d\n", set_hw_all(ifname)); */ err = set_hw_bcn(ifname, &bcn_set_data, 1); printf("set_bcn_cfg result is %d.\n", err); /*set_hw_all(ifname);*/ } printf("\nGETTING BCN:\n"); if (!get_bcn(ifname, &bcn_data)) { for (i = 0; i < 8; i++) { printf("BCN RP %d: %d\n", i, bcn_data.up_settings[i].rp_admin); } printf("\nBCN RP ALPHA: %f\n", bcn_data.rp_alpha); printf("BCN RP BETA : %f\n", bcn_data.rp_beta); printf("BCN RP GD : %f\n", bcn_data.rp_gd); printf("BCN RP GI : %f\n", bcn_data.rp_gi); printf("BCN RP TMAX : %d\n", bcn_data.rp_tmax); printf("BCN RP RI : %d\n", bcn_data.rp_ri); printf("BCN RP TD : %d\n", bcn_data.rp_td); printf("BCN RP RMIN : %d\n", bcn_data.rp_rmin); printf("BCN RP W : %d\n", bcn_data.rp_w); printf("BCN RP RD : %d\n", bcn_data.rp_rd); printf("BCN RP RU : %d\n", bcn_data.rp_ru); printf("BCN RP WRTT : %d\n", bcn_data.rp_wrtt); } else printf("not found\n"); #ifdef DCB_APP_DRV_IF_SUPPORTED if (!read_only) { printf("\nSETTING APP:\n"); if (set_hw_app0(ifname, &app_data)) { printf("Fail to set app data.\n"); goto err_main; } } printf("\nGETTING APP:\n"); if (!get_app_cfg(ifname, &app_data)) { printf("APP ID TYPE: "); if (app_data.dcb_app_idtype) printf(" \t DCB_APP_IDTYPE_ETHTYPE.\n"); else printf(" \t DCB_APP_IDTYPE_PORTNUM.\n"); printf(" APP ID: 0x%0x.\n", app_data.dcb_app_id); printf(" APP PRIORITY: 0x%0x.\n", app_data.dcb_app_priority); } else { printf("GETTING APP FAILED!.\n"); } #endif /* DCB_APP_DRV_IF_SUPPORTED */ if (!read_only) { struct ieee_ets ets = { .willing = 0, .ets_cap = 0x1, .cbs = 0, .tc_tx_bw = {25, 25, 25, 25, 0, 0, 0, 0}, .tc_rx_bw = {0, 0, 0, 0, 25, 25, 25, 25}, .tc_tsa = {1, 2, 3, 4, 1, 2, 3, 4}, .prio_tc = {1, 2, 3, 4, 1, 2, 3, 4} }; struct ieee_pfc pfc = { .pfc_cap = 0xf1, .pfc_en = 0, .mbc = 0, .delay = 0x32 }; struct dcb_app app = { .selector = 0, .priority = 4, .protocol = 0x8906 }; printf("\nSETTING ETS:\n"); set_ieee(ifname, &ets, &pfc, &app); } get_ieee(ifname); err_main: close(nl_sd); return err; } lldpad-0.9.46/test/nltest.h000066400000000000000000000062521215142747300155260ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #ifndef _NLUTIL_H #define _NLUTIL_H #include #include #include #include #include #include "linux/if.h" #include "linux/netlink.h" #include "linux/rtnetlink.h" #include "linux/dcbnl.h" typedef __u8 u8; typedef __u16 u16; typedef __u32 u32; typedef __u64 u64; #define BCN_ADDR_OPTION_LEN 8 /* 8 hex digits */ #define ETH_ALEN 6 enum dcb_pfc_type { pfc_disabled = 0, pfc_enabled_tx, pfc_enabled_rx, pfc_enabled_full }; enum strict_prio_type { prio_none = 0, prio_group, prio_link }; struct tc_config { __u8 bwg_id; __u8 up_to_tc_bitmap; __u8 prio_type; __u8 bwg_percent; }; typedef struct bcn_cfg { __u8 bcna[8];/* CM-Tag BCNA field */ struct { char cp_admin; /* CP admin mode */ char rp_admin; /* RP admin mode */ char rp_oper; /* RP Operational mode */ char rem_tag_oper; /* Remove CM tag Operational mode */ }up_settings[8]; /* Index is user priority */ float rp_alpha; /* RP max decrease factor */ float rp_beta; /* RP max increase factor */ float rp_gd; /* RP decrement coefficient */ float rp_gi; /* RP increment coefficient */ int rp_tmax; /* RP max time to backoff after BCN0 */ int cp_sf; /* CP sampling interval fixed - Not used by driver */ int rp_c; /* RP link capacity */ int rp_ri; /* RP initial rate */ __u16 rp_td; /* RP drift interval */ __u16 rp_rmin; /* RP default rate after 1st BCN0 */ __u8 rp_w; /* RP derivate rate */ __u8 rp_rd; /* RP drift factor */ __u8 rp_ru; /* RP rate unit */ __u8 rp_wrtt; /* RP RTT moving average weight */ } bcn_cfg; typedef struct appgroup_attribs { __u8 dcb_app_idtype; __u16 dcb_app_id; __u8 dcb_app_priority; } appgroup_attribs; #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) #define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN)) #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) int nl_sd; int dbg = 1; /* Maximum size of response requested or message sent */ #define MAX_MSG_SIZE 1024 extern unsigned if_nametoindex(const char *ifname); #endif /* _NLUTIL_H */ lldpad-0.9.46/test/qbg22sim.1000066400000000000000000000214171215142747300155540ustar00rootroot00000000000000.PU .TH qbg22sim 1 "LLDPAD" "Revision: 0.4" .SH NAME qbg22sim \- EVB, ECP and VDP Protocol Test Program for LLDPAD .SH SYNOPSIS .ll +8 .br .B qbg22sim [\-d duration] [\-t timeout] [\-T timeout] [\-v] device [file] .br .ll -8 .SH DESCRIPTION .B Qbg22sim sends IEEE 802.1ad LLDP protocol messages which contain an IEEE802.1Qbg EVB data unit TLV. It also sends IEEE 802.1Qbg ECP protocol messages which contain an arbitrary data payload in TLV format. It can automaticly send IEEE 802.1Qbg VDP protocol acknowledgements with a given response code. Only the IEEE 802.1Qbg Darft 2.2 (ratified on July 5th, 2012) is supported. .PP The mandatory device parameter specifies a network interface name. This interface is opened in raw mode and listens to LLDP messages (ethertype 0x88cc) and ECP messages (ethertype 0x8890) targeted to multicast addresses for nearest bridge, nearest customer bridge and nearest non two port MAC relay bridge. The purpose of the low level test program is to test .BR lldpad (8) EVB, ECP and VDP module interaction by sending EVB/ECP TLV data. The data is read from one or more files, the response received can be displayed on the screen and optionally checked against an expected value. If no input file is specified .B qbg22sim only listen but do not send any data units. A dash stands for standard input. The input files are preprocessed by .BR cpp (1). All .BR cpp (1) features can be used, including comments, defines, include files. .SS Event Handling .B qbg22sim use the select(2) system call to wait for a message and a timer event. The timer is a periodic timer and initially loaded with the values specified with the options -t and -T. The default value is one second and zero nano seconds. Each time the timer expires .B qbg22sim checks for messages to send. If the elapsed run time of the program is larger or equal to the time specified in the first field of the configuration file, those lines are transmitted. Note that only one transmission per timer invocation is capable to check optional replies for an expected TLV. If more than one line have the same elapsed time, then only the last line sent can have expected reply TLVs (see below). .SS Input File Layout The input file is read line by line. Each line specifies a complete entry, long lines can be split by a trailing backslash newline combination. Each line is divided by blanks or tabs into several fields. .IP 1 The first field specifies the elapsed time in seconds when the remaining data on the line will be sent. .IP 2 The second field specifies the destination MAC address. If the second field on one of the keywords .I ecp or .I vdp this entry specifies a command line. Command lines alter the behavior of .B qbg22sim for the named protocol. .RS In this case the third and forth field specify parameter and value. At the time specified, the parameter for the procotol is changed and applied until the program terminates or the parameter is changed again. For the .I ecp protocol the valid parameters are .IP ack The forth field specifies the time to wait in seconds until ECP protocol acknowledgement is sent. A value of 0 stands for no delay at all. A value larger then 20 seconds disables acknowledgements. .IP seqno The forth field specifies the next sequence number to use when a VDP protocol message triggers an ECP acknowledgement. .PP For the .I vdp protocol the valid parameters are .IP ack The forth field specifies the time to wait in seconds until VDP protocol acknowledgement is sent. A value of 0 stands for no delay at all. A value larger then 30 seconds disables acknowledgements. .IP error The forth field specifies the error number to be returned when a VDP protocol message requesting an association is received. .RE .IP 3 The third field specifies the sender's MAC address. A dash ('-') will be replaced by the sender's MAC address on transmission. .IP 4 The forth field specifies the ethertype variable. For LLDP protocol this should be 88cc. For ECP protocol this should be 8890 (in hex). Any other protocols are not supported and the program exits with an appropriate error message. The value of this field determines the data layout of the remaining fields. .IP 5 The remaining fields specify the LLDP protocol TLVs or the ECP protocol TLVs. There is no check for consistency, but at least the mandatory LLDP protocol TLVs (which are chassis identifier, port identifer, time to live (TTL), and End) have to be listed. To be useful an EVB TLV should be included. The same is true for the ECP protocol. At least an ECP protocol header has to be listed. .IP 6 When a TLV is preceded by an at-sign ('@'), this TLV is not transmitted, but added to an expected reply list. When the LLDP data unit is sent out, a possible reply from .BR lldpad (8) is waited for and searched. If the reply contains this TLV and they match, the program continues. If they mismatch or the expected TLV is not found in the reply, .B qbg22sim terminates. If there is no reply at all .B qbg22sim issues a warning, discards the expected TLV list and continues. Missing EVB protocol replies, even when expected TLVs are specified, do not cause .B qbg22sim to terminate. .B qbg22sim waits for a possible reply up to the next timer expiration. This simulates the LLDP TLV exchange mechanims as defined in the standard. .PP All fields represent byte streams. Each byte is denoted by a two digit hexadecimal number. Bytes are delimited by colon. Little to no input file checking is done. As an example the string 88:cc is the ethertype field for LLDP data units. .sp 1 Here is a sample input file with explanations. .IP A. The first line contains an EVP TLV and is sent after four seconds program run time. The line expects a reply from .BR lldpad (8) which contains the reply to the EVB TLV just sent out. In this case .BR lldpad (8) is configured as station and does not request reflective-relay. Other values are defaults. .IP B. The second, third and forth line resend the first line to simulate fast initialization. Same replies are expected. .IP C. The last line is executed after 20 seconds program run time. It sends a TTL TLV of value 0 and causes the EVB entry on the remote side to be discarded. .PP .ne 20 .EX .nf /* * Startup the EVB negotiation */ #define dst_mac 01:80:c2:00:00:0e #define lldp_type 88:cc #define chassis_tlv 02:07:06:64:75:6d:6d:79:00 #define port_tlv 04:05:05:65:74:68:30 #define ttl_tlv120 06:02:00:78 #define ttl_tlv0 06:02:00:00 #define end_tlv 00:00 #define evbhead fe:09:00:80:c2:0d /* No Reflective Relay on bridge */ #define evb_norr evbhead:00:00:00:40:00 #define evb_norr_ack evbhead:00:00:68:88:08 4 dst_mac - lldp_type chassis_tlv port_tlv ttl_tlv120 \\ evb_norr end_tlv @evb_norr_ack 5 dst_mac - lldp_type chassis_tlv port_tlv ttl_tlv120 \\ evb_norr end_tlv @evb_norr_ack 6 dst_mac - lldp_type chassis_tlv port_tlv ttl_tlv120 \\ evb_norr end_tlv @evb_norr_ack 7 dst_mac - lldp_type chassis_tlv port_tlv ttl_tlv120 \\ evb_norr end_tlv @evb_norr_ack 20 dst_mac - lldp_type chassis_tlv port_tlv ttl_tlv0 end_tlv .fi .EE .PP Now following is an example file for ECP protocol negotiation: .IP A. The first line contains an ECP TLV and is sent after 18 seconds. It consists of the ECP header with version, operation and subtype. The next field contains the sequence number followed by a 4 byte payload data and the END TLV. .IP B. The second and third line send the same data, only the sequence number is incremented. No reply is expected. .PP .ne 20 .EX .nf /* * ECP Protocol test data. Define a complete ECP DU in hex. One complete set * is needed for each transmission. */ #define lldpad_mac 01:80:c2:00:00:00 #define end_tlv 00:00 #define ecp_type 88:90 #include "defines.ecp" 18 lldpad_mac - ecp_type 10:01 12:24 0a:0b:0c:0d end_tlv 21 lldpad_mac - ecp_type 10:01 12:25 0a:0b:0c:0d end_tlv 24 lldpad_mac - ecp_type 10:01 12:26 0a:0b:0c:0d end_tlv .EE .SH OPTIONS .TP .B "\-v" Enables verbose mode. This option can be applied more than once. Each time specified, the output is more verbose. If set once, .B qbg22sim displays the progress of sent and received messages. If set twice, the message contents is also displayed. If set three times, the event wait time is also displayed. .TP .B "\-d\fIduration\fP" Specifies the number in seconds the program will run. Defaults to 120 seconds of total run time. .TP .B "\-t\fItimeout\fP" Specifies the time (seconds portion) to wait for an event. Default is one. .TP .B "\-T\fItimeout\fP" Specifies the time (nano-seconds portion) to wait for an event. Default is zero. .SH "ENVIRONMENT" Linux and virtual machines connected with a virtual bridge. Requires Linux kernel 3.2 or later. Disable stp on the virtual bridge to forward mutlicast mac addresses 01:80:C2:00:00:00! .SH "SEE ALSO" lldpad(8), lldptool(8) .SH DIAGNOSTICS Exit status is zero on success and non zero on failure or mismatch. .SH AUTHOR Thomas Richter, IBM Research and Development GmbH, Germany. lldpad-0.9.46/test/qbg22sim.c000066400000000000000000001032051215142747300156320ustar00rootroot00000000000000/****************************************************************************** Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2012, 2013 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ /* * Test Program for lldpad to respond to LLDP TLV with EVB DU. The program * behaves like a switch and sends out LLDP traffic with EVB DU. Only the * EVB DU varies. * * The EVB data to be sent is read from a configuration file. Data is send * once per second. The configuration file specifies the data to be send * for each transmission. * * Support for ECP and VDP protocol has been added. The support is very simple * and contains automatic acknowledgement with some delay and error response. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MYDEBUG 0 #define DIM(x) (sizeof(x)/sizeof(x[0])) #define ETH_P_LLDP 0x88cc #define ETH_P_ECP 0x8890 #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAC2STR(a) (a)[0] & 0xff, (a)[1] & 0xff, (a)[2] & 0xff, \ (a)[3] & 0xff, (a)[4] & 0xff, (a)[5] & 0xff #define ECPMAXACK 20 /* Longest ECP ack delay */ #define VDPMAXACK 30 /* Longest VDP ack delay */ #define ECP_HLEN 4 /* ECP protocol header length */ static char *progname; static unsigned char eth_p_lldp[2] = { 0x88, 0xcc }; static unsigned char eth_p_ecp[2] = { 0x88, 0x90 }; static int verbose; static char *tokens[1024]; /* Used to parse command line params */ static int myfd; /* Raw socket for LLDP protocol */ static int ecpfd; /* Raw socket for ECP protocol */ static int timerfd; /* Time descriptor */ static unsigned char my_mac[ETH_ALEN]; /* My source MAC address */ static unsigned long duration = 120; /* Time to run program in seconds */ static unsigned long timeout = 1; /* Time to wait in select in seconds */ static unsigned long timeout_us = 0; /* Time to wait in select in usecs */ enum pr_optype { /* Protocol actions */ ECP_ACK, /* ECP acknowledgement delay */ ECP_SEQNO, /* ECP sequnece number */ VDP_ACK, /* VDP acknowledgement delay */ VDP_ERROR, /* VDP Error returned */ CMD_LAST /* Must be last */ }; struct pr_op { /* Protocol command */ enum pr_optype type; /* Protocol to apply this command */ unsigned long value; /* Value */ int enabled; /* True if active */ }; static struct pr_op cmd_on[CMD_LAST]; struct lldpdu { /* LLDP Data unit to send */ unsigned char *data; struct lldpdu *next; unsigned short len; /* No of bytes */ unsigned char manda; /* Mandatory in reply chain */ }; struct lldp { /* LLDP DUs */ unsigned long time; /* Delta to send */ unsigned char *dst; /* Destination mac address */ unsigned char *src; /* Source mac address */ unsigned char *ether; /* Ethertype value */ struct lldpdu *head; /* Ptr to TLV data units */ struct lldpdu *recv; /* Ptr to expected receive TLV data units */ struct lldp *next; /* Ptr to sucessor */ struct pr_op *opr; /* Protocol operation */ }; unsigned long runtime; /* Program run time in seconds */ static struct lldp *lldphead; /* List of commands */ static struct lldp *er_ecp; /* Expected replies ECP protocol */ static struct lldp *er_evb; /* Expected replies ECP protocol */ struct ecphdr { /* ECP header received */ unsigned char version; /* Version number */ unsigned char op; /* Operation REQ, ACK */ unsigned short subtype; /* Subtype */ unsigned short seqno; /* Sequence number */ }; struct vdphdr { /* VDP header */ unsigned char opr; /* Operation requested */ unsigned char status; /* Status */ unsigned long vsi_tyid; /* VSI Type id */ unsigned char vsi_tyv; /* VSI Type version */ unsigned char vsi_tyfm; /* VSI Type id format */ unsigned char vsi_uuid[16]; /* VSI UUID */ unsigned char fil_info; /* Filter info format */ }; /* Return ECP protocol acknowledgement delay and other settings */ static unsigned long ecp_inc_seqno(void) { if (++cmd_on[ECP_SEQNO].value == 0) ++cmd_on[ECP_SEQNO].value; return cmd_on[ECP_SEQNO].value; } static unsigned long ecp_ackdelay(void) { return cmd_on[ECP_ACK].value; } static unsigned long vdp_ackdelay(void) { return cmd_on[VDP_ACK].value; } static unsigned long vdp_error(void) { return cmd_on[VDP_ERROR].value; } static void showmsg(char *txt, char nl, unsigned char *buf, int len) { int i, do_nl = 1; printf("%s%c", txt, nl); for (i = 1; i <= len; ++i) { printf("%02x ", *buf++); do_nl = 1; if (i % 16 == 0) { printf("\n"); do_nl = 0; } } if (do_nl) printf("\n"); } /* * Convert a number from ascii to int. */ static unsigned long getnumber(char *key, char *word, char stopchar, int *ec) { char *endp; unsigned long no = strtoul((const char *)word, &endp, 0); if (word == 0 || *word == '\0') { fprintf(stderr, "key %s has missing number\n", key); *ec = 1; return 0; } #if MYDEBUG printf("%s:stopchar:%c endp:%c\n", __func__, stopchar, *endp); #endif if (*endp != stopchar) { fprintf(stderr, "key %s has invalid parameter %s\n", key, word); *ec = 1; return 0; } *ec = 0; return no; } /* * Add a command to the list on how to react to unsolicited messages. * If not recognized, proceed with normal input processing. * * Currently recognized: * ecp ack 0 --> no delay in sending out acknowledgement * ecp ack 1..20 --> x seconds delay in sending out acknowledgement * ecp ack 21 --> no acknowledgement * vdp ack ## --> same as ecp ack * vdp error ## --> return error number on VDP request. */ static struct pr_op *valid_cmd() { unsigned long no; int ec; struct pr_op *p = calloc(1, sizeof *p); if (!p) { perror(progname); exit(1); } if (!strcmp(tokens[1], "vdp") && !strcmp(tokens[2], "error")) { no = getnumber(tokens[2], tokens[3], '\0', &ec); if (ec) exit(1); p->value = no; p->type = VDP_ERROR; return p; } if (!strcmp(tokens[1], "vdp") && !strcmp(tokens[2], "ack")) { no = getnumber(tokens[2], tokens[3], '\0', &ec); if (ec) exit(1); if (no > VDPMAXACK) no = VDPMAXACK; p->value = no; p->type = VDP_ACK; return p; } if (!strcmp(tokens[1], "ecp") && !strcmp(tokens[2], "seqno")) { no = getnumber(tokens[2], tokens[3], '\0', &ec); if (ec) exit(1); p->value = no; p->type = ECP_SEQNO; return p; } if (!strcmp(tokens[1], "ecp") && !strcmp(tokens[2], "ack")) { no = getnumber(tokens[2], tokens[3], '\0', &ec); if (ec) exit(1); if (no > ECPMAXACK) no = ECPMAXACK; p->value = no; p->type = ECP_ACK; return p; } free(p); return 0; } static int numeric(unsigned char x) { if (isdigit(x)) return x - '0'; return x - 97 + 10; } /* * Convert string to hex. */ static void mkbin(unsigned char *to, unsigned char *s) { int byte1, byte2; for (; *s != '\0'; ++s) { byte1 = numeric(*s++); byte2 = numeric(*s); byte1 = byte1 << 4 | byte2; *to++ = byte1; if (*s == '\0') return; } } static unsigned char *validate(char *s, unsigned short *len) { unsigned char buf[512], *cp = buf; int pos = 0; #if MYDEBUG printf("%s:%s\n", __func__, s); #endif if (!strcmp("-", (const char *)s)) { cp = calloc(ETH_ALEN, sizeof *cp); if (!cp) { perror(progname); exit(2); } memcpy(cp, my_mac, ETH_ALEN); return cp; } memset(buf, 0, sizeof buf); for (; *s != '\0'; ++s) { ++pos; if (isxdigit(*s)) *cp++ = isupper(*s) ? tolower(*s) : *s; else if (*s == ':') { if (pos % 3 != 0) { fprintf(stderr, "%s:invalid input format:%s\n", progname, s); exit(2); } continue; } else { fprintf(stderr, "invalid data in input:%s\n", s); exit(2); } } *cp = '\0'; pos = (strlen((const char *)buf) + 1) / 2; #if MYDEBUG printf("%s:%s binary length:%d\n", __func__, buf, pos); #endif cp = calloc(pos, sizeof *cp); if (!cp) { perror(progname); exit(2); } mkbin(cp, buf); #if MYDEBUG showmsg("validate", ':', cp, pos); #endif if (len) *len = pos; return cp; } static void removedataunit(struct lldpdu *q) { free(q->data); free(q); } static void removedata(struct lldpdu *dup) { struct lldpdu *q; while (dup) { q = dup; dup = dup->next; removedataunit(q); } } static void removeentry(struct lldp *p) { #if MYDEBUG printf("%s %p:\n", __func__, p); #endif removedata(p->head); removedata(p->recv); free(p->opr); free(p->dst); free(p->src); free(p->ether); free(p); } static void show_cmd(struct pr_op *p) { char *txt = "unknown"; switch (p->type) { case ECP_SEQNO: txt = "ecp seqno"; break; case ECP_ACK: txt = "ecp ack"; break; case VDP_ACK: txt = "vdp ack"; break; case VDP_ERROR: txt = "vdp error"; break; case CMD_LAST: break; } printf("\t%s %ld\n", txt, p->value); } /* * Display one node entry on stdout. */ static void showentry(char *txt, struct lldp *p) { struct lldpdu *dup; #if MYDEBUG printf("%p: ", p); #endif printf("%s time:%ld\n", txt, p->time); if (p->opr) { show_cmd(p->opr); return; } showmsg("\tdstmac", ':', p->dst, ETH_ALEN); showmsg("\tsrcmac", ':', p->src, ETH_ALEN); showmsg("\tethtype", ':', p->ether, 2); for (dup = p->head; dup; dup = dup->next) showmsg("\tout", ':', dup->data, dup->len); for (dup = p->recv; dup; dup = dup->next) showmsg("\tin", ':', dup->data, dup->len); } static int show_queue(char *txt, struct lldp *p) { int cnt = 0; for (; p; p = p->next, ++cnt) showentry(txt, p); return cnt; } /* * Delete a complete lldp queue. */ static void delete_queue(struct lldp **root) { struct lldp *node, *p = *root; while (p) { node = p; p = p->next; removeentry(node); } *root = 0; } /* * Append a node to expected reply queue. */ static void appendnode(struct lldp **root, struct lldp *add) { struct lldp *p2 = *root; if (!p2) { /* Empty queue */ *root = add; return; } for (; p2->next; p2 = p2->next) ; p2->next = add; } /* * Timer expired. Check if evb reply received. There is only one EVB message * pending for expected reply. */ static void timeout_evb(void) { if (er_evb) { fprintf(stderr, "missing EVB reply (time:%ld)\n", er_evb->time); showentry("missing EVB reply", er_evb); delete_queue(&er_evb); } } static void appendentry(struct lldp *add) { int type = memcmp(add->ether, eth_p_lldp, sizeof eth_p_lldp); if (type == 0) timeout_evb(); appendnode(type == 0 ? &er_evb : &er_ecp, add); } /* * Insert node into queue. * Sorting criteria is time in ascending order. * There is only one entry for the evb reply queue and multiple entries for * the ecp reply queue for each time entry. */ static void insertentry(struct lldp *add) { struct lldp *p2; int type; if (!lldphead) { /* Empty queue */ lldphead = add; return; } type = memcmp(add->ether, eth_p_lldp, sizeof eth_p_lldp); if (add->time <= lldphead->time) { /* Insert at head */ if (type == 0 && add->time == lldphead->time) { showentry("duplicate entry -- ignored", add); removeentry(add); return; } add->next = lldphead; lldphead = add; return; } for (p2 = lldphead; p2->next && add->time > p2->next->time; p2 = p2->next) ; if (type == 0 && p2->next && add->time == p2->next->time) { showentry("duplicate entry -- ignored", add); removeentry(add); return; } add->next = p2->next; p2->next = add; } static int addone(void) { struct lldp *p = calloc(1, sizeof *p); struct lldpdu *dup, *dup2; unsigned int i; int ec; if (!p) { perror("addone"); return 1; } p->time = getnumber("time", tokens[0], '\0', &ec); if (ec) exit(3); p->opr = valid_cmd(); if (p->opr) { p->ether = validate("88:90", 0); goto out; } p->dst = validate(tokens[1], 0); p->src = validate(tokens[2], 0); p->ether = validate(tokens[3], 0); if (memcmp(p->ether, eth_p_lldp, sizeof eth_p_lldp) && memcmp(p->ether, eth_p_ecp, sizeof eth_p_ecp)) { fprintf(stderr, "%s: unsupported ethernet protocol %s\n", progname, tokens[3]); exit(11); } for (i = 4; i < DIM(tokens) && tokens[i]; ++i) { if ((dup = calloc(1, sizeof *dup))) { if (*tokens[i] == '@') { dup->data = validate(++tokens[i], &dup->len); if (p->recv) { for (dup2 = p->recv; dup2->next; dup2 = dup2->next) ; dup2->next = dup; } else p->recv = dup; continue; } dup->data = validate(tokens[i], &dup->len); if (p->head) { for (dup2 = p->head; dup2->next; dup2 = dup2->next) ; dup2->next = dup; } else p->head = dup; } else { perror("addone2"); return 1; } } out: insertentry(p); return 0; } static void settokens(char *parm) { unsigned int i; for (i = 0; i < DIM(tokens) && (tokens[i] = strtok(parm, "\t ")) != 0; ++i, parm = 0) { #if MYDEBUG printf("%s:tokens[%d]:%s:\n", __func__, i, tokens[i]); #endif } } static int forwardline(char *line) { settokens(line); return addone(); } /* * Read a full line from the file. Remove comments and ignore blank lines. * Also concatenate lines terminated with . */ #define COMMENT "#*;" /* Comments in [#*;] and */ static char *fullline(FILE *fp, char *buffer, size_t buflen) { int more = 0, off = 0; char *cp; static int lineno; do { if ((cp = fgets(buffer + off, buflen - off, fp)) == NULL) { if (more == 2) { fprintf(stderr, "%s line %d unexpected EOF\n", progname, lineno); exit(1); } return NULL; /* No more lines */ } ++lineno; if ((cp = strchr(buffer, '\n')) == NULL) { fprintf(stderr, "%s line %d too long", progname, lineno); exit(1); } else *cp = '\0'; if ((cp = strpbrk(buffer, COMMENT)) != NULL) *cp = '\0'; /* Drop comment */ for (cp = buffer; *cp && isspace(*cp); ++cp) ; /* Skip leading space */ if (*cp == '\0') more = 1; /* Empty line */ else if (*(cp + strlen(cp) - 1) == '\\') { more = 2; /* Line concatenation */ *(cp + strlen(cp) - 1) = '\0'; off = strlen(buffer); } else more = 0; } while (more); memmove(buffer, cp, strlen(cp) + 1); return buffer; } /* * Read the configuration file containing the LLDP DUs. */ static int read_profiles(char *cfgfile) { FILE *fp; char buffer[1024 * 5]; char cmd[128]; int rc = 0; sprintf(cmd, "cpp %s", cfgfile); if ((fp = popen(cmd, "r")) == NULL) { perror(cmd); exit(1); } while (fullline(fp, buffer, sizeof buffer)) rc |= forwardline(buffer); pclose(fp); return rc; } static int tlv_id(unsigned char *outp) { return *outp >> 1; } static int tlv_len(unsigned char *outp) { int byte1 = *outp & 1; int byte2 = *(outp + 1); return byte1 << 8 | byte2; } static void l2_close(int fd) { close(fd); } static const unsigned char nearest_bridge[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }; static const unsigned char nearest_nontpmr_bridge[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; static const unsigned char nearest_customer_bridge[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; static int l2_init(char *ifname, unsigned short pno) { struct ifreq ifr; struct sockaddr_ll ll; struct packet_mreq mr; int ifindex, option = 1; int option_size = sizeof(option); int fd; fd = socket(PF_PACKET, SOCK_RAW, htons(pno)); if (fd < 0) { perror("socket(PF_PACKET)"); return -1; } strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl[SIOCGIFINDEX]"); close(fd); return -1; } ifindex = ifr.ifr_ifindex; memset(&ll, 0, sizeof(ll)); ll.sll_family = PF_PACKET; ll.sll_ifindex = ifr.ifr_ifindex; ll.sll_protocol = htons(pno); if (bind(fd, (struct sockaddr *)&ll, sizeof(ll)) < 0) { perror("bind[PF_PACKET]"); close(fd); return -1; } /* current hw address */ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { perror("ioctl[SIOCGIFHWADDR]"); close(fd); return -1; } memcpy(my_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); if (verbose) printf("%s MAC address is " MACSTR "\n", ifname, MAC2STR(my_mac)); memset(&mr, 0, sizeof(mr)); mr.mr_ifindex = ifindex; mr.mr_alen = ETH_ALEN; memcpy(mr.mr_address, &nearest_bridge, ETH_ALEN); mr.mr_type = PACKET_MR_MULTICAST; if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) { perror("setsockopt nearest_bridge"); close(fd); return -1; } memcpy(mr.mr_address, &nearest_customer_bridge, ETH_ALEN); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) perror("setsockopt nearest_customer_bridge"); memcpy(mr.mr_address, &nearest_nontpmr_bridge, ETH_ALEN); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) perror("setsockopt nearest_nontpmr_bridge"); if (setsockopt(fd, SOL_PACKET, PACKET_ORIGDEV, &option, option_size) < 0) { perror("setsockopt SOL_PACKET"); close(fd); return -1; } return fd; } static void apply_cmd(struct pr_op *p) { switch (p->type) { case ECP_SEQNO: cmd_on[ECP_SEQNO].value = p->value; if (verbose >= 2) show_cmd(p); break; case ECP_ACK: cmd_on[ECP_ACK].value = p->value; if (verbose >= 2) show_cmd(p); break; case VDP_ACK: cmd_on[VDP_ACK].value = p->value; if (verbose >= 2) show_cmd(p); break; case VDP_ERROR: cmd_on[VDP_ERROR].value = p->value; if (verbose >= 2) show_cmd(p); break; case CMD_LAST: break; } } /* * Send one entry. Return 0 on failure and number of bytes on success. */ static int sendentry(struct lldp *p) { struct lldpdu *dup; unsigned char out[2300]; char tracebuf[128]; struct ethhdr *ehdr = (struct ethhdr *)out; unsigned char *outp = out + sizeof *ehdr; int fd = -1; int len; if (p->opr) { apply_cmd(p->opr); return 0; } memset(out, 0, sizeof out); memcpy(ehdr->h_dest, p->dst, ETH_ALEN); memcpy(ehdr->h_source, p->src, ETH_ALEN); memcpy(&ehdr->h_proto, p->ether, 2); for (dup = p->head; dup; dup = dup->next) { memcpy(outp, dup->data, dup->len); outp += dup->len; } if (verbose >= 2) { sprintf(tracebuf, "sendout time(%ld)", p->time); showmsg(tracebuf, '\n', out, outp - out); } if (!memcmp(p->ether, eth_p_lldp, sizeof eth_p_lldp)) fd = myfd; else fd = ecpfd; len = send(fd, out, outp - out, 0); if (len != outp - out) { fprintf(stderr, "%s:send error %d bytes:%ld\n", __func__, len, outp - out); len = 0; } return len; } /* * Get first entry from list which is equal or older than current program * run time and remove it from list. * * Return pointer to node or 0. */ static struct lldp *findentry(struct lldp **root) { struct lldp *p = *root; if (p && runtime >= p->time) { *root = p->next; p->next = 0; return p; } return 0; } static void sendall(void) { struct lldp *p; long long int expired; if (read(timerfd, &expired, sizeof expired) != sizeof expired) { fprintf(stderr, "%s:read error %d bytes:%ld\n", __func__, errno, sizeof expired); return; } for (p = findentry(&lldphead); p; p = findentry(&lldphead)) { if (sendentry(p) && p->recv) appendentry(p); else removeentry(p); } } /* * Check if an expected TLV is in the ECP/LLDP DU reply. * Return 1 if it matches and has been removed from the data unit list. */ static int check_tlv(struct lldp *node, unsigned char *tlv) { struct lldpdu *dup = node->recv; struct lldpdu *dup_pr = 0; while (dup) { if (!memcmp(tlv, dup->data, dup->len)) { char txt[32]; if (verbose >= 2) { sprintf(txt, "tlv id:%d len:%d", tlv_id(dup->data), tlv_len(dup->data)); showmsg(txt, ':', tlv, dup->len); } if (dup_pr) dup_pr->next = dup->next; else node->recv = dup->next; removedataunit(dup); return 1; } dup_pr = dup; dup = dup->next; } return 0; } /* * Show all TLV expected in a reply. */ static void show_expect(struct lldp *node, int exitcode) { struct lldpdu *dup = node->recv; char txt[32]; if (verbose) printf("ERROR expected reply for message sent at %ld sec missing\n", node->time); while (dup) { if (verbose) { sprintf(txt, "tlv-id:%d len:%d", tlv_id(dup->data), tlv_len(dup->data)); showmsg(txt, ':', dup->data, 2 + tlv_len(dup->data)); } dup = dup->next; } exit(exitcode); } /* * Check if the received reply contains this data we expect as response. */ static void search_evbreply(unsigned char *buf, size_t buflen) { struct ethhdr *ehdr = (struct ethhdr *)buf; unsigned char *tlv = (unsigned char *)(ehdr + 1); int del = 0; if (!er_evb || !er_evb->recv) /* No reply expected */ return; do { int len = 2 + tlv_len(tlv); if (tlv_id(tlv)) del += check_tlv(er_evb, tlv); tlv += len; } while (tlv < buf + buflen); if (er_evb->recv) show_expect(er_evb, 5); /* Got all expected replies, delete queue */ delete_queue(&er_evb); } /* * Wait for a message from switch * * Return number of bytes received. 0 means timeout and -1 on error. */ static int get_evb(void) { unsigned char buf[2300]; size_t buflen = sizeof buf; struct sockaddr_ll from; socklen_t from_len = sizeof from; int result = 0; memset(buf, 0, buflen); memset(&from, 0, from_len); result = recvfrom(myfd, buf, buflen, 0, (struct sockaddr *)&from, &from_len); if (result < 0) fprintf(stderr, "%s receive error:%s\n", progname, strerror(errno)); else { if (verbose) printf("received %d bytes from ifindex %d\n", result, from.sll_ifindex); if (verbose >= 2) printf("\tfamily:%hd protocol:%hx hatype:%hd\n" "\tpkttype:%d halen:%d MAC:" MACSTR "\n", from.sll_family, ntohs(from.sll_protocol), from.sll_hatype, from.sll_pkttype, from.sll_halen, MAC2STR(from.sll_addr)); if (result > 0) { if (verbose >= 2) showmsg("get_evb", ':', buf, result); search_evbreply(buf, result); } } return result; } /* * Pack the vdp data into the buffer. For now just use the first 3 bytes. */ static void convert_vdp(unsigned char *load, struct vdphdr *p) { *load = (p->opr << 1); *(load + 1) = 1; *(load + 2) = p->status; } /* * Pack the ecp header into 4 bytes. */ static void convert_ecp(unsigned char *load, struct ecphdr *ecp) { *load = (ecp->version << 4) | (ecp->op << 2) | ((ecp->subtype >> 8) & 0xff); *(load + 1) = ecp->subtype & 0xff; *(load + 2) = ecp->seqno >> 8 & 0xff; *(load + 3) = ecp->seqno & 0xff; } /* * Check if this vdp data needs acknowledgement, Return false in this case * true otherwise. */ static int vdp_get(unsigned char *vdpdata, int len, struct vdphdr *vdp) { unsigned char tlv_type = tlv_id(vdpdata); unsigned short tlv_length = tlv_len(vdpdata); memset(vdp, 0, sizeof *vdp); for ( ; tlv_type >= 5 && len >= 0; len -= tlv_length + 2, vdpdata += tlv_length + 2) ; if (tlv_type < 5) { vdp->opr = tlv_id(vdpdata); vdpdata += 2; vdp->status = *vdpdata; vdp->vsi_tyid = *(vdpdata + 1) << 16 | *(vdpdata + 2) << 8 | *(vdpdata + 3); vdp->vsi_tyv = *(vdpdata + 4); vdp->vsi_tyfm = *(vdpdata + 5); memcpy(vdp->vsi_uuid, vdpdata + 6, sizeof vdp->vsi_uuid); vdp->fil_info = *(vdpdata + 22); if ((vdp->status & 2) == 0) return 0; } return 1; } /* * Handle acknowledgement of vdp protocol header. */ static void handle_vdp(unsigned char *data, int len) { unsigned char *load, *rcv, *dst, *src, *ether; struct lldp *ack; struct lldpdu *ackdata, *rcvdata; struct ethhdr *ethhdr = (struct ethhdr *)data; struct vdphdr vdp; struct ecphdr ecp; if (vdp_ackdelay() > VDPMAXACK) /* Acknowledge request */ return; if (vdp_get(data + ETH_HLEN + ECP_HLEN, len - (ETH_HLEN + ECP_HLEN), &vdp)) return; ack = calloc(1, sizeof *ack); ackdata = calloc(1, sizeof *ackdata); rcvdata = calloc(1, sizeof *rcvdata); load = calloc(ECP_HLEN + 3, sizeof *load); rcv = calloc(ECP_HLEN, sizeof *load); dst = calloc(ETH_ALEN, sizeof *dst); src = calloc(ETH_ALEN, sizeof *src); ether = calloc(2, sizeof *ether); if (!ack || !ackdata || !load || !dst || !src || !ether || !rcv || !rcvdata) { free(load); free(rcv); free(dst); free(src); free(ether); free(ackdata); free(rcvdata); free(ack); return; } ack->time = runtime + vdp_ackdelay(); /* Prepare ETH header */ ack->dst = dst; memcpy(ack->dst, ethhdr->h_source, sizeof ethhdr->h_source); ack->src = src; memcpy(ack->src, my_mac, sizeof ethhdr->h_source); ack->ether = ether; memcpy(ack->ether, eth_p_ecp, sizeof ethhdr->h_proto); /* Prepare ECP header */ ecp.op = 0; ecp.seqno = ecp_inc_seqno(); ecp.subtype = 1; ecp.version = 1; convert_ecp(load, &ecp); /* Prepare ECP acknowledgement */ ecp.op = 1; convert_ecp(rcv, &ecp); rcvdata->data = rcv; rcvdata->len = ECP_HLEN; ack->recv = rcvdata; /* Prepare VDP acknowledgement */ vdp.status = ((vdp_error() & 0xf) << 4) | 2; convert_vdp(load + ECP_HLEN, &vdp); ackdata->data = load; ackdata->len = ECP_HLEN + 3; ack->head = ackdata; if (vdp_ackdelay()) { /* Insert ack in queue */ insertentry(ack); return; } sendentry(ack); appendnode(&er_ecp, ack); } /* * Send out acknowledgement when request received. */ static void ack_ecp(unsigned char *ecpdata, struct ecphdr *ecp) { unsigned char *load, *dst, *src, *ether; struct lldp *ack; struct lldpdu *ackdata; struct ethhdr *ethhdr = (struct ethhdr *)ecpdata; if (ecp_ackdelay() > ECPMAXACK) /* Acknowledge request */ return; ack = calloc(1, sizeof *ack); ackdata = calloc(1, sizeof *ackdata); load = calloc(ECP_HLEN, sizeof *load); dst = calloc(ETH_ALEN, sizeof *dst); src = calloc(ETH_ALEN, sizeof *src); ether = calloc(2, sizeof *ether); if (!ack || !ackdata || !load || !dst || !src || !ether) { free(ack); free(ackdata); free(load); free(dst); free(src); free(ether); return; } ack->time = runtime + ecp_ackdelay(); ack->dst = dst; memcpy(ack->dst, ethhdr->h_source, sizeof ethhdr->h_source); ack->src = src; memcpy(ack->src, my_mac, sizeof ethhdr->h_source); ack->ether = ether; memcpy(ack->ether, eth_p_ecp, sizeof ethhdr->h_proto); ecp->op = 1; convert_ecp(load, ecp); ackdata->data = load; ackdata->len = ECP_HLEN; ack->head = ackdata; if (ecp_ackdelay()) { /* Insert ack in queue */ insertentry(ack); return; } sendentry(ack); removeentry(ack); } /* * Show ECP expected in a reply. */ static void show_ecpexpect(struct lldp *node, int exitcode) { struct lldpdu *dup = node->recv; if (verbose) printf("ERROR expected ECP ACK for message sent at %ld sec missing\n", node->time); while (dup) { if (verbose) showmsg("ecp-ack", ':', dup->data, dup->len); dup = dup->next; } exit(exitcode); } /* * Check if an expected TLV is in the ECP/LLDP DU reply. * Return 1 if it matches and has been removed from the data unit list. */ static int check_ecpack(struct lldp *node, unsigned char *buf) { struct lldpdu *dup = node->recv; struct lldpdu *dup_pr = 0; while (dup) { if (!memcmp(buf, dup->data, dup->len)) { if (dup_pr) dup_pr->next = dup->next; else node->recv = dup->next; removedataunit(dup); return 1; } dup_pr = dup; dup = dup->next; } return 0; } /* * Find an ECP send command and check the returned acknowledgement. */ static void search_ecpack(unsigned char *ecpdata) { struct lldp *np, *np_prev = 0; for (np = er_ecp; np; np_prev = np, np = np->next) { check_ecpack(np, ecpdata + ETH_HLEN); if (np->recv) show_ecpexpect(np, 6); else { if (!np_prev) er_ecp = np->next; else np_prev->next = np->next; removeentry(np); } } } static int handle_ecp(unsigned char *ecpdata) { unsigned char *buf = ecpdata + ETH_HLEN; struct ecphdr ecphdr; int rc = 0; ecphdr.version = *buf >> 4; ecphdr.op = (*buf >> 2) & 3; ecphdr.subtype = (*buf & 3) << 8 | *(buf + 1); ecphdr.seqno = *(buf + 2) << 8 | *(buf + 3); if (verbose >= 2) printf("ecp.version:%d op:%d subtype:%d seqno:%#hx\n", ecphdr.version, ecphdr.op, ecphdr.subtype, ecphdr.seqno); if (ecphdr.op == 0) { /* Request received, send ACK */ ack_ecp(ecpdata, &ecphdr); rc = ecphdr.subtype; } else /* ACK received, check list */ search_ecpack(ecpdata); return rc; } static int get_ecp(void) { unsigned char buf[2300]; size_t buflen = sizeof buf; struct sockaddr_ll from; socklen_t from_len = sizeof from; int type, result = 0; memset(buf, 0, buflen); memset(&from, 0, from_len); result = recvfrom(ecpfd, buf, buflen, 0, (struct sockaddr *)&from, &from_len); if (result < 0) fprintf(stderr, "%s receive error:%s\n", progname, strerror(errno)); else { if (verbose) printf("received %d bytes from ifindex %d\n", result, from.sll_ifindex); if (verbose >= 2) printf("\tfamily:%hd protocol:%hx hatype:%hd\n" "\tpkttype:%d halen:%d MAC:" MACSTR "\n", from.sll_family, ntohs(from.sll_protocol), from.sll_hatype, from.sll_pkttype, from.sll_halen, MAC2STR(from.sll_addr)); if (result > 0) { if (verbose >= 2) showmsg("get_ecp", ':', buf, result); type = handle_ecp(buf); if (type == 1) /* VDP payload in ECP */ handle_vdp(buf, result); } } return result; } /* * c := a - b, with a->tv_sec always larger or equal than b->tv_sec */ static void past(struct timespec *a, struct timespec *b, struct timespec *c) { c->tv_sec = a->tv_sec - b->tv_sec; if (a->tv_nsec > b->tv_nsec) c->tv_nsec = a->tv_nsec - b->tv_nsec; else { c->tv_nsec = 1000000000 - b->tv_nsec + a->tv_nsec; --c->tv_sec; } } static void timer_init(void) { struct itimerspec val; if ((timerfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) { perror(progname); exit(2); } val.it_value.tv_sec = timeout; val.it_value.tv_nsec = timeout_us; val.it_interval.tv_sec = timeout; val.it_interval.tv_nsec = timeout_us; if (timerfd_settime(timerfd, 0, &val, 0) != 0) { perror(progname); exit(2); } } static void timer_close(void) { struct itimerspec val; val.it_value.tv_sec = 0; val.it_value.tv_nsec = 0; val.it_interval.tv_sec = 0; val.it_interval.tv_nsec = 0; timerfd_settime(timerfd, 0, &val, 0); close(timerfd); } static void hear(void) { int n, cnt = 0; struct timespec start_time, now, diff; fd_set readfds; timer_init(); clock_gettime(CLOCK_MONOTONIC, &start_time); do { FD_ZERO(&readfds); FD_SET(ecpfd, &readfds); FD_SET(myfd, &readfds); FD_SET(timerfd, &readfds); if (verbose) printf("%s wait for event %d...", progname, ++cnt); n = (myfd > timerfd) ? myfd : timerfd; n = (n > ecpfd) ? n : ecpfd; n = select(n + 1, &readfds, NULL, NULL, 0); clock_gettime(CLOCK_MONOTONIC, &now); past(&now, &start_time, &diff); runtime = diff.tv_sec; if (n < 0) { fprintf(stderr, "%s error select:%s\n", progname, strerror(errno)); } else { if (FD_ISSET(ecpfd, &readfds)) { if (verbose >= 3) printf("ECP msg received %ld.%09ld\n", diff.tv_sec, diff.tv_nsec); get_ecp(); } if (FD_ISSET(myfd, &readfds)) { if (verbose >= 3) printf("EVB msg received %ld.%09ld\n", diff.tv_sec, diff.tv_nsec); get_evb(); } if (FD_ISSET(timerfd, &readfds)) { if (verbose >= 3) printf("timer expired %ld.%09ld\n", diff.tv_sec, diff.tv_nsec); else if (verbose) printf("\n"); timeout_evb(); sendall(); } } } while ((unsigned long)diff.tv_sec < duration); timer_close(); } static void help(void) { printf("\t-a specifies the ECP acknowledgement delay ((default %lds)\n" "\t-d specifies the run time of the program (default %ld)\n" "\t-t specifies the timeout in seconds to wait (default %lds)\n" "\t-T specifies the timeout in microseconds to wait" " (default %ldus)\n" "\t-v verbose mode, can be set more than once\n" "\t device is the network interface to listen on\n" "\t file is one or more input files to read LLDP data from\n", ecp_ackdelay(), duration, timeout, timeout_us); } int main(int argc, char **argv) { extern int optind, opterr; extern char *optarg; int rc = 0, ch; char *slash; progname = (slash = strrchr(argv[0], '/')) ? slash + 1 : argv[0]; while ((ch = getopt(argc, argv, ":d:t:T:v")) != EOF) switch (ch) { case '?': fprintf(stderr, "%s: unknown option -%c\n", progname, optopt); help(); exit(1); case ':': fprintf(stderr, "%s missing option argument for -%c\n", progname, optopt); exit(1); case 'T': timeout_us = strtoul(optarg, 0, 0); if (!timeout_us) { fprintf(stderr, "%s wrong timeout %s\n", progname, optarg); exit(1); } break; case 't': timeout = strtoul(optarg, 0, 0); if (!timeout) { fprintf(stderr, "%s wrong timeout %s\n", progname, optarg); exit(1); } break; case 'd': duration = strtoul(optarg, 0, 0); if (!duration) { fprintf(stderr, "%s wrong duration %s\n", progname, optarg); exit(1); } break; case 'v': ++verbose; break; } if (argc == optind) { fprintf(stderr, "%s interface not specified\n", progname); return 2; } if (!if_nametoindex(argv[optind])) { fprintf(stderr, "%s interface %s does not exist\n", progname, argv[optind]); return 2; } myfd = l2_init(argv[optind], ETH_P_LLDP); if (myfd < 0) return 2; ecpfd = l2_init(argv[optind], ETH_P_ECP); if (ecpfd < 0) { l2_close(myfd); return 2; } for (; ++optind < argc;) { rc |= read_profiles(argv[optind]); if (verbose >= 3) show_queue("command", lldphead); } hear(); l2_close(ecpfd); l2_close(myfd); show_queue("expected evb replies", er_evb); if (show_queue("expected ecp replies", er_ecp)) rc = 7; /* This queue should be empty */ return rc; } lldpad-0.9.46/test/vdptest.1000066400000000000000000000267261215142747300156270ustar00rootroot00000000000000.PU .TH vdptest 1 "LLDPAD" "Revision: 0.1" .SH NAME vdptest \- VDP/VSI Protocol Test Program for LLDPAD .SH SYNOPSIS .ll +8 .B vdptest [ \-SvV ] [ \-i\ interface] [ \-e text ] [ \-E cmd ] [ \-X defaults ] [ \-adprsg\ \fIcmd\fP ] [ \-ACDF\ \fIprofile\fP ] .br .ll -8 .SH DESCRIPTION .B vdptest sends command messages to llpdad(8) . .B vdptest behaves the same way as libvirtd(8) does. Each command message is sent as a .I RTM_SETLINK message. This message is confirmed by lldpad(8) with a netlink message of type .IR NLMSG_ERROR . The error status indicates if the message parsing was successful (zero) or not (non-zero). Each command message is followed by one or more .I RTM_GETLINK messages. The .I RTM_GETLINK message is used to query the switch status and lldpad(8) responds with one netlink message of type NLMSG_DONE. The wait time and number of .I RTM_GETLINK messages sent can be fine tuned. The message flow is: .sp 1 .EX vdptest --- SETLINK(ifname,command,VSI) ---> lldpad vdptest <-- NLMSG_ERROR(Error code) -------- lldpad vdptest --- GETLINK(Request status) -------> lldpad vdptest <-- NLMSG_DONE(Response) ----------- lldpad .EE .sp 1 If the .I RTM_SETLINK netlink message can not be parsed correctly by lldpad(8), a negative error code is returned and no .I RTM_GETLINK netlink messages are sent. If the netlink message parsing succeeds, lldpad(8) returns zero in the .I NLMSG_ERROR netlink message. .B vdptest then sends out .I RTM_GETLINK status requests. lldpad(8) immediately responds to the status requests with an .I NLMSG_DONE netlink message. It returns the value of 255 for pending operation and the values 0..3 for the switch status. Only when the returned status value is 255 (no status available from switch) .B vdptest retries sending of .I RTM_GETLINK messages a predefined number of times employing a predefined wait time between each send. .br The process identifier for lldpad(8) is read from file .IR /var/run/lldpad.pid . If this file does not exist, lldpad(8) is not running and .B vdptest terminates. .br .B vdptest should be used for testing and debug purposes only. .SS Profiles .br Profile is a comma separated list of key '=' value pairs describing the VSI profile data. Profiles consists of the following keywords, they are all mandatory: .TP name: Defines the name of the entry. Commands refer to a profile entry by this name. The value is an alphanumeric word including dash ('-') and underscore ('_') starting with a character. .TP mgrid: Defines the manager identifier for this entry. The value should be a number in the range of 0..255 inclusive. .TP typeid: Defines the type identifier for this entry. The value should be a number in the range of 0..16777215 (2^24-1) inclusive. .TP typeidver: Defines the type identifier version for this entry. The value should be a number in the range of 0..255 inclusive. .TP uuid: Defines the UUID identifier version for this entry. The value is a 16 byte identifier following this format: .EX aabbccdd-eeff-gghh-iijj-kkllmmnn .EE The string is converted into 16 byte UUID with 2 nibbles converted into one byte. .TP map: Defines a VLAN-MAC address pair for this entry. The value is of the following format: .EX vlanid-aa:bb:cc:dd:ee:ff .EE Vlandid is converted into a number ranging from 0..4095. Following the vlanid is a dash (\-) and an MAC address. The MAC address is a 6 byte value, each byte delimited by a colon (':'). This keyword can be listed several times to allow multiple MAC-VLAN pairs per entry. .TP Example: Here is an example of a profile definition: .EX name=thomas2,mgrid=1,typeid=123452,typeidver=1,\(rs uuid=a1412857-60f7-4ce1-e95a-2164943f53dd,\(rs map=2-52:54:00:8e:50:53 .EE .SS Commands Command is an option character followed by a comma separated list of modifiers. Each modifier is a character followed by an equal sign ('=') and a number. The following modifiers are supported and their meaning varies slightly, depending on the command. Not all commands allow all modifiers. The following modifiers are supported: .TP .B d=no Specify the delay time (in milli seconds) to wait after a .I RTM_SETLINK command has been sent and before a .I RTM_GETLINK message will be sent to query lldpad(8) for status. Default is one second. .TP .B e=no Specify the expected status code returned from lldpad(8) for this command. This modifier can be given multiple times to allow for different status values. If this modifier is not specified, the expected status code is zero. .TP .B r=no Specify the maximum number of .I RTM_GETLINK messages sent out by .B vdptest to lldpad(8) for the expected status from the switch. Default value is one. .TP .B w=no Specify the maximum wait time .B vdptest waits to receive a .I NLMSG_DONE or .I NLMSG_ERROR netlink message from lldpad(8). This allows for some delay in the response from lldpad(8) for acknowledgement of message parsing and expected status code from the switch. Default value is one. .SH OPTIONS The options are applied from left to right and are accumulated before being exexcuted. Capital letter options operate on profiles. Profiles can be defined, copied, deleted and read from a configuration file. .br Lower case letter options define operations send to lldpad(8) using netlink .I RTM_SETLINK messages. Here is the list of possible options: .TP .B "\-v" Enables verbose mode. This option can be applied more than once. Each time specified, the output is more verbose. If set once, .B vdptest displays the progress of sent and received messages. If set twice, the message contents is also displayed. Furthermore a command summary list is displayed. The list is shown twice. Before command execution begins without return codes and after command execution with actual return codes. If set three times, the netlink attributes in the message contents is also displayed. .TP .B "\-e\fItext\fP" The string .I text is printed on stdout. If text contains spaces it has to be quoted. No modifiers are possible for this option. .TP .B "\-E\fItext\fP" Run external command .IR text . The string .I text is given as is to the system(3) library function. If text contains spaces it has to be quoted. No modifiers are possible for this option. .TP .B "\-i\fIinterface\fP" Specifies the interface name of lldpad(8) to operate on. This option is mandatory and must be set. .TP .B "\-A\fIlist\fP" Define a profile using above mentioned profile keywords. Since this is a definition, all keywords are mandatory. If an error is encountered during keyword parsing or the values are not in the allowed range, the profile definition is ignored. .I Name has to be unique. Several definitions using the same .I name are not allowed. .TP .B "\-C\fIname,new=new-name,changes\fP" Copy the profile entry named .I name and assign it .IR new-name. After the first comma, list the fields which are to be changed using the same syntax as in the profile definition. If an error is encountered during keyword parsing or the values are not in the allowed range, the profile definition is ignored. Only the modified keys need to be listed. Also .I new-name has to be unique. .TP .B "\-D\fIname\fP" Delete the profile definition assigned to .IR name . .TP .B "\-F\fIconfiguration file\fP" Read profile definitions from a configuration file. The file can contain comments, any characters between the hash sign ('#') and a newline. Empty lines are silently discarded and long lines can be split using the backslash ('\(rs') newline syntax. .sp 0 To copy an entry in the configuration file, use the keyword .I new=newname at the beginning of the line: .sp 1 .EX # Copy entry thomas2 and add new mac address new=mac54,name=thomas2,map=2-52:54:00:8e:50:54 .EE .sp 1 .TP .B "\-S[key]" Show all profiles defined if the option key is missing. If the key is specified just show the data associated with key. .TP .B "\-V" Displays the version number. .TP .B "\-a\fIname\fP[,e=E][,w=W][,r=R][d=D]" Send an associate command and use the VSI profile data stored under .IR name . After D milli seconds delay send out the .I RTM_GETLINK status query up to .B R times and wait .B W seconds for a response from lldpad(8). The expected status from the switch is .BR E . .TP .B "\-d\fIname\fP[,e=E][,w=W][,r=R][d=D]" Send a dis-associate command and use the VSI profile data stored under .IR name . For the modifier meanings see option .BR "\-a" . .TP .B "\-p\fIname\fP[,e=E][,w=W][,r=R][d=D]" Send an pre-associate command and use the VSI profile data stored under .IR name . For the modifier meanings see option .BR "\-a" . .TP .B "\-r\fIname\fP[,e=E][,w=W][,r=R][d=D]" Send an pre-associate with resource restrictions command and use the VSI profile data stored under .IR name . For the modifier meanings see option .BR "\-a" . .TP .B "\-s[,w=W][,r=R]" Sleep command. Wait for .B W seconds .B R times to kill some time. If both modifiers are omitted, it defaults to one second delay. As this option has optional parameters, no whitespace is allowed between the option character and the comma. .TP .B "\-g[,e=E][,w=W][,r=R]" Message wait command. Wait for up to .B W seconds .B R times for .B one unsolicited message from lldpad(8). This is useful for testing scenarios where lldpad(8) sends DIS-ASSOCIATE messages. Modifier .B e=0 stands for time out expected. No message should be received. If one is received, it is treated as an error and the program terminates. Modifier .B e=1 stands for one message expected. A message has to be received. If none is received, it is treated as an error and the program terminates. Use .B e=0,e=1 to allow an optional message reception. This is the default. .TP .B "\-X[,d=D][,w=W][,r=R]" Specifies different default value for the modifiers delaytime, waittime and number of aknowledgement reads. The new default values are applied for each command specified on the command line, regardless or the sequence specified. If specified several times, the last one wins. If no modifiers are specified with the VDP action, use the defaults specified with the .B "\-X" flag. If this option is not set then the following default values are used: delaytime (1 second), waittime (1 second) and number of acknowledgements reads (1). .SH "EXAMPLES" .EX vdptest -Aname=thomas2,mgrid=1,typeid=123452,typeidver=1, uuid=a1412857-60f7-4ce1-e95a-2164943f53dd,map=2-52:54:00:8e:50:53 -S .EE .sp 1 Define a profile and show its definition. .sp 1 .EX vdptest -C thomas2,new=unknown,typeid=99999 .EE .sp 1 Copies the entry named .I thomas2 and assigns its contents to the name .IR unknown . The difference between both entries .I thomas2 and .I unknown is the .I typeid field. .sp 1 .EX vdptest -i eth2 -F vdptest.cfg -a unknown,w=10,r=2,e=3 -s .EE .sp 1 Use interface eth2 and read the VSI configuration from file .IR vdptest.cfg . Use the VSI definition named .I unknown and send an ASSOCIATION command to the switch. Wait up to 10 seconds for the status confirmation 2 times and expected the error code 3 from the switch. Wait one second before termintation. .sp 1 .EX vdptest -i eth2 -F vdptest.cfg -Cthomas2,new=x1,mgrid=2 \(rs -athomas2,w=10,r=2,e=3 -s -a x1,w=5 .EE .sp 1 Use interface eth2 and read the VSI configuration from file .IR vdptest.cfg . Create a new profile definition named .I x1 by copying from .I thomas2 and change the the .IR mgrid . Send an ASSOCIATION command with parameters stored in .IR thomas2 , wait one second and send an ASSOCIATION command with parameters stored in .IR x1 . .SH FILES /var/run/lldpad.pid .SH "ENVIRONMENT" Linux RHEL .SH "SEE ALSO" lldpad(8), lldptool(8) .SH DIAGNOSTICS Exit status is zero on success and non zero on failure or mismatch. .SH AUTHOR Thomas Richter, IBM Research and Development GmbH, Germany. lldpad-0.9.46/test/vdptest.c000066400000000000000000001134041215142747300156770ustar00rootroot00000000000000/****************************************************************************** Implementation of VDP according to IEEE 802.1Qbg (c) Copyright IBM Corp. 2012 Author(s): Thomas Richter This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". ******************************************************************************/ /* * Test Program for lldpad to create and delete VSI profiles. * Send and receive netlink messages to lldpad to * - associate a VSI * - disassociate a VSI * - receive a netlink message from lldpad when * - the switch de-associates the VSI profile (switch data base cleaned) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clif.h" #include "clif_msgs.h" #define UUIDLEN 16 #define DIM(x) (sizeof(x)/sizeof(x[0])) #define COPY_OP "new=" #define KEYLEN 16 #define CMD_ASSOC 'a' /* Association */ #define CMD_DEASSOC 'd' /* DE-Association */ #define CMD_PREASSOC 'p' /* pre-Association */ #define CMD_RRPREASSOC 'r' /* pre-Association with RR */ #define CMD_SLEEP 's' /* Wait some time */ #define CMD_GETMSG 'g' /* Receive messages */ #define CMD_ECHO 'e' /* ECHO command */ #define CMD_EXTERN 'E' /* External command */ #define CMD_SETDF 'X' /* Change defaults */ /* * Set the define MYDEBUG to any value for detailed debugging */ enum { f_map, f_mgrid, f_typeid, f_typeidver, f_uuid }; struct macvlan { unsigned char mac[ETH_ALEN]; /* MAC address */ unsigned short vlanid; /* VLAN Id */ }; static struct vdpdata { char key[KEYLEN]; /* Profile name */ unsigned char modified; /* Field altered */ unsigned char pairs; /* # of MAC/VLAN pairs */ unsigned char mgrid; /* Manager ID */ unsigned char typeidver; /* Type ID version */ unsigned int typeid; /* Type ID */ unsigned char uuid[UUIDLEN]; /* Instance ID */ struct macvlan addr[10]; /* Pairs of MAC/VLAN */ } vsidata[32]; static struct command { /* Command structure */ char key[KEYLEN]; /* Name of profile to use */ unsigned int waittime; /* Time (in secs) to wait after cmd */ unsigned int repeats; /* # of times to repeat this command */ unsigned int delay; /* Delay (in us) before a GETLINK msg */ unsigned char cmd; /* Type of command */ unsigned char no_err; /* # of expected errors */ int errors[4]; /* Expected errors */ int rc; /* Encountered error */ char *text; /* Text to display */ } cmds[32], defaults = { /* Default values in structure */ .waittime = 1, .repeats = 1, .delay = 1000 }; static char *progname; static int verbose; static char *tokens[256]; /* Used to parse command line params */ static unsigned int cmdidx; /* Index into cmds[] array */ static int ifindex; /* Index of ifname */ static char *ifname; /* Interface to operate on */ static pid_t lldpad; /* LLDPAD process identifier */ static int my_sock; /* Netlink socket for lldpad talk */ static char mybuf[1024]; /* Buffer for netlink message decode */ static struct nla_policy ifla_vf_policy[IFLA_VF_MAX + 1] = { [IFLA_VF_MAC] = { .minlen = sizeof(struct ifla_vf_mac), .maxlen = sizeof(struct ifla_vf_mac) }, [IFLA_VF_VLAN] = { .minlen = sizeof(struct ifla_vf_vlan), .maxlen = sizeof(struct ifla_vf_vlan) } }; static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] = { [IFLA_PORT_RESPONSE] = { .type = NLA_U16 } }; static void uuid2buf(const unsigned char *p, char *buf) { sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" "%02x%02x-%02x%02x%02x%02x%02x%02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); } static int addit(char *format, ...) { size_t left = strlen(mybuf); int c; va_list ap; va_start(ap, format); c = vsnprintf(mybuf + left, sizeof mybuf - left, format, ap); va_end(ap); return (c < 0 || ((unsigned)c >= sizeof mybuf - left)) ? -1 : 0; } static int showerror(struct nlmsghdr *nlh) { struct nlmsgerr *err = NLMSG_DATA(nlh); if (verbose) printf("%s setlink response:%d\n", progname, err->error); return err->error; } static void parse_vfinfolist(struct nlattr *vfinfolist) { struct nlattr *le1, *vf[IFLA_VF_MAX + 1]; int rem; addit("\tfound IFLA_VFINFO_LIST!\n"); nla_for_each_nested(le1, vfinfolist, rem) { if (nla_type(le1) != IFLA_VF_INFO) { fprintf(stderr, "%s nested parsing of" "IFLA_VFINFO_LIST failed\n", progname); return; } if (nla_parse_nested(vf, IFLA_VF_MAX, le1, ifla_vf_policy)) { fprintf(stderr, "%s nested parsing of " "IFLA_VF_INFO failed\n", progname); return; } if (vf[IFLA_VF_MAC]) { struct ifla_vf_mac *mac = RTA_DATA(vf[IFLA_VF_MAC]); unsigned char *m = mac->mac; addit("\tIFLA_VF_MAC=%02x:%02x:%02x:" " %02x:%02x:%02x\n", m[0], m[1], m[2], m[3], m[4], m[5]); } if (vf[IFLA_VF_VLAN]) { struct ifla_vf_vlan *vlan = RTA_DATA(vf[IFLA_VF_VLAN]); addit("\tIFLA_VF_VLAN=%d\n", vlan->vlan); } } } static void show_nlas(struct nlattr **tb, int max) { int rem; for (rem = 0; rem < max; ++rem) { if (tb[rem]) printf("nlattr %02d type:%d len:%d\n", rem, tb[rem]->nla_type, tb[rem]->nla_len); } } static void showmsg(struct nlmsghdr *nlh, int *status) { struct nlattr *tb[IFLA_MAX + 1], *tb3[IFLA_PORT_MAX + 1]; struct ifinfomsg ifinfo; char *ifname; int rem; if (status) *status = -1; if (nlh->nlmsg_type == NLMSG_ERROR) { if (status) *status = showerror(nlh); return; } memset(mybuf, 0, sizeof mybuf); addit("\tnlh.nl_pid:%d nlh_type:%d nlh_seq:%#x nlh_len:%#x\n", nlh->nlmsg_pid, nlh->nlmsg_type, nlh->nlmsg_seq, nlh->nlmsg_len); memcpy(&ifinfo, NLMSG_DATA(nlh), sizeof ifinfo); addit("\tifinfo.family:%#x type:%#x index:%d flags:%#x change:%#x\n", ifinfo.ifi_family, ifinfo.ifi_type, ifinfo.ifi_index, ifinfo.ifi_flags, ifinfo.ifi_change); if (nlmsg_parse(nlh, sizeof ifinfo, (struct nlattr **)&tb, IFLA_MAX, NULL)) { fprintf(stderr, "%s error parsing request...\n", progname); return; } if (verbose >= 3) show_nlas(tb, IFLA_MAX); if (tb[IFLA_IFNAME]) { ifname = (char *)RTA_DATA(tb[IFLA_IFNAME]); addit("\tIFLA_IFNAME=%s\n", ifname); } if (tb[IFLA_OPERSTATE]) { rem = *(unsigned short *)RTA_DATA(tb[IFLA_OPERSTATE]); addit("\tIFLA_OPERSTATE=%d\n", rem); } if (tb[IFLA_VFINFO_LIST]) parse_vfinfolist(tb[IFLA_VFINFO_LIST]); if (tb[IFLA_VF_PORTS]) { struct nlattr *tb_vf_ports; addit("\tfound IFLA_VF_PORTS\n"); nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) { if (nla_type(tb_vf_ports) != IFLA_VF_PORT) { fprintf(stderr, "%s not a IFLA_VF_PORT, " " skipping\n", progname); continue; } if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb_vf_ports, ifla_port_policy)) { fprintf(stderr, "%s nested parsing on level 2" " failed\n", progname); } if (tb3[IFLA_PORT_VF]) addit("\tIFLA_PORT_VF=%d\n", *(uint32_t *) RTA_DATA(tb3[IFLA_PORT_VF])); if (tb3[IFLA_PORT_VSI_TYPE]) { struct ifla_port_vsi *pvsi; int tid = 0; pvsi = (struct ifla_port_vsi *) RTA_DATA(tb3[IFLA_PORT_VSI_TYPE]); tid = pvsi->vsi_type_id[2] << 16 | pvsi->vsi_type_id[1] << 8 | pvsi->vsi_type_id[0]; addit("\tIFLA_PORT_VSI_TYPE=mgr_id:%d " " type_id:%d typeid_version:%d\n", pvsi->vsi_mgr_id, tid, pvsi->vsi_type_version); } if (tb3[IFLA_PORT_INSTANCE_UUID]) { char uuidbuf[64]; unsigned char *uuid; uuid = (unsigned char *) RTA_DATA(tb3[IFLA_PORT_INSTANCE_UUID]); uuid2buf(uuid, uuidbuf); addit("\tIFLA_PORT_INSTANCE_UUID=%s\n", uuidbuf); } if (tb3[IFLA_PORT_REQUEST]) addit("\tIFLA_PORT_REQUEST=%d\n", *(uint8_t *) RTA_DATA(tb3[IFLA_PORT_REQUEST])); if (tb3[IFLA_PORT_RESPONSE]) { addit("\tIFLA_PORT_RESPONSE=%d\n", *(uint16_t *) RTA_DATA(tb3[IFLA_PORT_RESPONSE])); *status = *(int *) RTA_DATA(tb3[IFLA_PORT_RESPONSE]); } } } if (verbose >= 2) printf("%s", mybuf); } /* * Wait for a message from LLDPAD * * Return number of bytes received. 0 means timeout and -1 on error. */ static int waitmsg(struct command *cp, int *status) { struct msghdr msg; struct sockaddr_nl dest_addr; struct iovec iov; unsigned char msgbuf[1024]; struct nlmsghdr *nlh = (struct nlmsghdr *)msgbuf; int n, result = 0; fd_set readfds; struct timeval tv = { .tv_sec = cp->waittime }; memset(&msgbuf, 0, sizeof msgbuf); memset(&dest_addr, 0, sizeof dest_addr); iov.iov_base = (void *)nlh; iov.iov_len = sizeof msgbuf; msg.msg_name = (void *)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (verbose) printf("%s Waiting %d seconds for message...\n", progname, cp->waittime); FD_ZERO(&readfds); FD_SET(my_sock, &readfds); n = select(my_sock + 1, &readfds, NULL, NULL, &tv); if (n <= 0) { if (n < 0) fprintf(stderr, "%s error netlink socket:%s\n", progname, strerror(errno)); if (n == 0) if (verbose) printf("%s no netlink response received\n", progname); return n; } result = recvmsg(my_sock, &msg, MSG_DONTWAIT); if (result < 0) fprintf(stderr, "%s receive error:%s\n", progname, strerror(errno)); else { if (verbose) printf("%s received %d bytes from %d\n", progname, result, dest_addr.nl_pid); showmsg(nlh, status); } return result; } static int lldp_wait(struct command *cp) { int rc = 0; unsigned int cnt; for (cnt = 0; cnt < cp->repeats && rc >= 0; ++cnt) if ((rc = waitmsg(cp, 0))) { cp->rc = 1; break; } return rc; } /* * Construct the GETLINK message to lldpad. */ static int mk_nlas(char *buf) { int total; struct nlattr *nlap; char *cp; struct ifinfomsg *to = (struct ifinfomsg *)buf; to->ifi_index = ifindex; to->ifi_family = AF_UNSPEC; total = NLMSG_ALIGN(sizeof *to); nlap = (struct nlattr *)(buf + NLMSG_ALIGN(sizeof *to)); nlap->nla_type = IFLA_IFNAME; nlap->nla_len = NLA_HDRLEN + NLA_ALIGN(1 + strlen(ifname)); total += nlap->nla_len; cp = (char *)nlap + NLA_HDRLEN; strcpy(cp, ifname); return total; } /* * Send a GETLINK message to lldpad to query the status of the operation. */ static int getlink(void) { struct sockaddr_nl d_nladdr; struct msghdr msg; char buffer[256]; struct nlmsghdr *nlh = (struct nlmsghdr *)buffer; struct iovec iov; int rc; memset(buffer, 0, sizeof buffer); /* Destination address */ memset(&d_nladdr, 0, sizeof d_nladdr); d_nladdr.nl_family = PF_NETLINK; d_nladdr.nl_pid = lldpad; /* Fill the netlink message header */ nlh->nlmsg_len = NLMSG_HDRLEN + mk_nlas((char *)NLMSG_DATA(nlh)); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_type = RTM_GETLINK; /* Iov structure */ iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; /* Msg */ memset(&msg, 0, sizeof msg); msg.msg_name = (void *)&d_nladdr; msg.msg_namelen = sizeof d_nladdr; msg.msg_iov = &iov; msg.msg_iovlen = 1; if ((rc = sendmsg(my_sock, &msg, 0)) == -1) perror(progname); if (verbose) printf("%s query status --> rc:%d\n", progname, rc); return rc; } /* * Send a RTM_GETLINK message and retrieve the status of the pending * command. */ static int lldp_ack(struct command *cp) { int bytes; int status; bytes = getlink(); if (bytes <= 0) return bytes; bytes = waitmsg(cp, &status); if (bytes <= 0) return bytes; cp->rc = status; if (verbose) printf("%s lldp_ack status:%d\n", progname, cp->rc); return bytes; } static int addvfs(struct nl_msg *nl_msg, struct vdpdata *vdp, unsigned char cmd) { struct nlattr *vfports, *vfport; struct ifla_port_vsi vsi; unsigned char op; switch (cmd) { case CMD_ASSOC: op = PORT_REQUEST_ASSOCIATE; break; case CMD_DEASSOC: op = PORT_REQUEST_DISASSOCIATE; break; case CMD_PREASSOC: op = PORT_REQUEST_PREASSOCIATE; break; case CMD_RRPREASSOC: op = PORT_REQUEST_PREASSOCIATE_RR; break; } vsi.vsi_mgr_id = vdp->mgrid; vsi.vsi_type_version = vdp->typeidver; vsi.vsi_type_id[2] = vdp->typeid >> 16; vsi.vsi_type_id[1] = vdp->typeid >> 8; vsi.vsi_type_id[0] = vdp->typeid; if (!(vfports = nla_nest_start(nl_msg, IFLA_VF_PORTS))) return -ENOMEM; if (!(vfport = nla_nest_start(nl_msg, IFLA_VF_PORT))) return -ENOMEM; if (nla_put(nl_msg, IFLA_PORT_VSI_TYPE, sizeof vsi, &vsi) < 0) return -ENOMEM; if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, UUIDLEN, vdp->uuid) < 0) return -ENOMEM; if (nla_put(nl_msg, IFLA_PORT_REQUEST, sizeof op, &op) < 0) return -ENOMEM; nla_nest_end(nl_msg, vfport); nla_nest_end(nl_msg, vfports); return 0; } static int addmacs(struct nl_msg *nl_msg, struct vdpdata *vdp) { int i; struct nlattr *vfinfolist, *vfinfo; if (vdp->pairs == 0) return 0; if (!(vfinfolist = nla_nest_start(nl_msg, IFLA_VFINFO_LIST))) return -ENOMEM; for (i = 0; i < vdp->pairs; ++i) { if (!(vfinfo = nla_nest_start(nl_msg, IFLA_VF_INFO))) return -ENOMEM; if (vdp->addr[i].mac) { struct ifla_vf_mac ifla_vf_mac; ifla_vf_mac.vf = PORT_SELF_VF; memcpy(ifla_vf_mac.mac, vdp->addr[i].mac, ETH_ALEN); if (nla_put(nl_msg, IFLA_VF_MAC, sizeof ifla_vf_mac, &ifla_vf_mac) < 0) return -ENOMEM; } if (vdp->addr[i].vlanid) { struct ifla_vf_vlan ifla_vf_vlan = { .vf = PORT_SELF_VF, .vlan = vdp->addr[i].vlanid, .qos = 0, }; if (nla_put(nl_msg, IFLA_VF_VLAN, sizeof ifla_vf_vlan, &ifla_vf_vlan) < 0) return -ENOMEM; } nla_nest_end(nl_msg, vfinfo); } nla_nest_end(nl_msg, vfinfolist); return 0; } /* * Build the netlink message, return total length of message */ static int buildmsg(unsigned char *buf, size_t len, unsigned char cmd, struct vdpdata *vdp) { struct nlmsghdr *nlh; struct nl_msg *nl_msg; struct ifinfomsg ifinfo; nl_msg = nlmsg_alloc(); if (!nl_msg) goto err_exit; ifinfo.ifi_index = ifindex; ifinfo.ifi_family = AF_UNSPEC; if (nlmsg_append(nl_msg, &ifinfo, sizeof ifinfo, NLMSG_ALIGNTO) < 0) goto err_exit; if (addmacs(nl_msg, vdp)) goto err_exit; if (addvfs(nl_msg, vdp, cmd)) goto err_exit; /* * Fill the netlink message header */ nlh = nlmsg_hdr(nl_msg); nlh->nlmsg_type = RTM_SETLINK; nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = NLM_F_REQUEST; if (len < nlh->nlmsg_len) goto err_exit; memcpy(buf, nlh, nlh->nlmsg_len); nlmsg_free(nl_msg); return 0; err_exit: if (nl_msg) nlmsg_free(nl_msg); fprintf(stderr, "%s: can not build netlink message\n", progname); return -ENOMEM; } /* * Send a netlink message to lldpad. Its a SETLINK message to trigger an * action. LLDPAD responds with an error netlink message indicating if the * profile was accepted. * LLDPAD sends negative numbers as error indicators. */ static int lldp_send(struct command *cp, struct vdpdata *vdp) { unsigned char sndbuf[1024]; struct iovec iov; struct msghdr msg; struct sockaddr_nl d_nladdr; struct nlmsghdr *nlh = (struct nlmsghdr *)sndbuf; int rc, vsiok = 0; memset(&d_nladdr, 0, sizeof d_nladdr); d_nladdr.nl_family = AF_NETLINK; d_nladdr.nl_pid = lldpad; /* Target PID */ memset(sndbuf, 0, sizeof sndbuf); rc = buildmsg(sndbuf, sizeof sndbuf, cp->cmd, vdp); if (rc) return -ENOMEM; iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; /* Msg */ memset(&msg, 0, sizeof msg); msg.msg_name = (void *)&d_nladdr; msg.msg_namelen = sizeof d_nladdr; msg.msg_iov = &iov; msg.msg_iovlen = 1; rc = sendmsg(my_sock, &msg, 0); if (rc < 0) perror(progname); else { if (verbose) printf("%s send message to %d --> rc:%d\n", progname, lldpad, rc); rc = waitmsg(cp, &vsiok); if (rc > 0) rc = vsiok; else if (rc == 0) /* Time out */ rc = -1; } return rc; } /* * Open netlink socket to talk to lldpad daemon. */ static int open_socket(int protocol) { int sd; int rcv_size = 8 * 1024; struct sockaddr_nl snl; sd = socket(PF_NETLINK, SOCK_RAW, protocol); if (sd < 0) { perror("socket"); return sd; } if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &rcv_size, sizeof(int)) < 0) { perror("setsockopt"); close(sd); return -EIO; } memset(&snl, 0, sizeof(struct sockaddr_nl)); snl.nl_family = PF_NETLINK; snl.nl_pid = getpid(); if (bind(sd, (struct sockaddr *)&snl, sizeof snl) < 0) { perror("bind"); close(sd); return -EIO; } return sd; } /* * Get PID of lldpad from 'ping' command */ static void lldpad_pid(void) { lldpad = clif_getpid(); if (!lldpad) { fprintf(stderr, "%s error getting pid of lldpad\n", progname); exit(5); } if (verbose >= 2) printf("%s my pid %d lldpad pid %d\n", progname, getpid(), lldpad); } /* * Functions to parse command line parameters */ /* * Convert a number from ascii to int. */ static unsigned long getnumber(char *key, char *word, char stopchar, int *ec) { char *endp; unsigned long no = strtoul(word, &endp, 0); if (word == 0 || *word == '\0') { fprintf(stderr, "key %s has missing number\n", key); *ec = 1; return 0; } #ifdef MYDEBUG printf("%s:stopchar:%c endp:%c\n", __func__, stopchar, *endp); #endif if (*endp != stopchar) { fprintf(stderr, "key %s has invalid parameter %s\n", key, word); *ec = 1; return 0; } *ec = 0; return no; } /* * Remove all whitespace from string. */ static void kill_white(char *s) { char *cp = s; for (; *s != '\0'; ++s) { if (isspace(*s)) continue; if (isprint(*s)) *cp++ = *s; } *cp = '\0'; } static void settokens(char *parm) { unsigned int i; kill_white(parm); for (i = 0; i < DIM(tokens) && (tokens[i] = strtok(parm, ",")) != 0; ++i, parm = 0) { #ifdef MYDEBUG printf("%s:tokens[%d]:%s:\n", __func__, i, tokens[i]); #endif } } static struct vdpdata *findkey(char *name) { unsigned int i; for (i = 0; i < DIM(vsidata); ++i) if (!strncmp(vsidata[i].key, name, strlen(name))) return &vsidata[i]; return 0; } static struct vdpdata *nextfree() { unsigned int i; for (i = 0; i < DIM(vsidata); ++i) if (!vsidata[i].key[0]) return &vsidata[i]; return 0; } static int check_map(char *value, struct vdpdata *profile) { char *delim = strchr(value, '-'); unsigned long vlan; int i, ec, x[ETH_ALEN]; if (!delim) { fprintf(stderr, "%s invalid map format %s\n", progname, value); return -1; } vlan = getnumber("map", value, '-', &ec); if (ec) { fprintf(stderr, "%s invalid vlanid %s\n", progname, value); return -1; } if (vlan >= 4095) { fprintf(stderr, "%s vlanid %ld too high\n", progname, vlan); return -1; } ++delim; ec = sscanf(delim, "%02x:%02x:%02x:%02x:%02x:%02x", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]); if (ec != ETH_ALEN) { fprintf(stderr, "%s mac %s invalid\n", progname, delim); return -1; } /* Check for last character */ delim = strrchr(value, ':') + 2; if (*delim && (strchr("0123456789abcdefABCDEF", *delim) == 0 || *(delim + 1) != '\0')) { fprintf(stderr, "%s last mac part %s invalid\n", progname, delim); return -1; } #ifdef MYDEBUG for (i = 0; i < ETH_ALEN; ++i) printf("x[%d]=%#x ", i, x[i]); puts(""); #endif for (ec = 0; ec < profile->pairs; ++ec) { for (i = 0; i < ETH_ALEN; ++i) if (profile->addr[ec].mac[i] != x[i]) break; if (i == ETH_ALEN) { fprintf(stderr, "%s duplicate mac address %s\n", progname, value); return -1; } } ec = profile->pairs++; profile->addr[ec].vlanid = vlan; for (i = 0; i < ETH_ALEN; ++i) profile->addr[ec].mac[i] = x[i]; profile->modified |= 1 << f_map; return 0; } static int check_uuid(char *value, struct vdpdata *profile) { unsigned int rc; unsigned int p[UUIDLEN]; rc = sscanf(value, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" "%02x%02x-%02x%02x%02x%02x%02x%02x", &p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7], &p[8], &p[9], &p[10], &p[11], &p[12], &p[13], &p[14], &p[15]); #ifdef MYDEBUG int i; printf("cc=%d\n", rc); for (i = 0; i < UUIDLEN; ++i) printf("p[%d]=%#x ", i, p[i]); puts(""); #endif if (rc != UUIDLEN) { fprintf(stderr, "%s invalid uuid %s\n", progname, value); return -1; } for (rc = 0; rc < sizeof(profile->uuid); ++rc) profile->uuid[rc] = p[rc]; profile->modified |= 1 << f_uuid; return 0; } static int check_typeid(char *value, struct vdpdata *profile) { unsigned long no; int ec; no = getnumber("typeid", value, '\0', &ec); if (!ec) { if ((no & 0xff000000) == 0) { profile->typeid = no; profile->modified |= 1 << f_typeid; } else { ec = -1; fprintf(stderr, "%s: invalid typeid %ld\n", progname, no); } #ifdef MYDEBUG printf("%s:typeid:%d ec:%d\n", __func__, profile->mgrid, ec); #endif } return ec; } static int check_typeidversion(char *value, struct vdpdata *profile) { unsigned long no; int ec; no = getnumber("typeidver", value, '\0', &ec); if (!ec) { if (no <= 255) { profile->typeidver = no; profile->modified |= 1 << f_typeidver; } else { ec = -1; fprintf(stderr, "%s: invalid typeidver %ld\n", progname, no); } #ifdef MYDEBUG printf("%s:typeidver:%d ec:%d\n", __func__, profile->mgrid, ec); #endif } return ec; } static int check_mgrid(char *value, struct vdpdata *profile) { unsigned long no; int ec; no = getnumber("mgrid", value, '\0', &ec); if (!ec) { if (no <= 255) { profile->mgrid = no; profile->modified |= 1 << f_mgrid; } else { ec = -1; fprintf(stderr, "%s: invalid mgrid %ld\n", progname, no); } #ifdef MYDEBUG printf("%s:mgrid:%d ec:%d\n", __func__, profile->mgrid, ec); #endif } return ec; } /* * Return true if the character is valid for a key */ static int check_char(char x) { switch (x) { case '-': case '_': return 1; } return isalnum(x); } static int check_name(char *value, struct vdpdata *profile) { int ec; char *cp; if (strlen(value) >= DIM(profile->key) - 1) { fprintf(stderr, "%s: key %s too long\n", progname, value); ec = -1; goto out; } for (cp = value; *cp; ++cp) if (!check_char(*cp)) { fprintf(stderr, "%s: invalid key %s\n", progname, value); ec = -1; goto out; } strcpy(profile->key, value); ec = 0; #ifdef MYDEBUG printf("%s:key:%s ec:%d\n", __func__, profile->key, ec); #endif out: return ec; } /* * Profile command line keywords. Append new words at end of list. */ static char *keytable[] = { "map", "mgrid", "typeidver", "typeid", "uuid", "name" }; static int findkeyword(char *word) { unsigned int i; for (i = 0; i < DIM(keytable); ++i) if (!strncmp(keytable[i], word, strlen(keytable[i]))) return i; return -1; } static int checkword(char *word, char *value, struct vdpdata *profile) { int rc, idx = findkeyword(word); switch (idx) { default: fprintf(stderr, "%s unknown keyword %s\n", progname, word); return -1; case 0: rc = check_map(value, profile); break; case 1: rc = check_mgrid(value, profile); break; case 3: rc = check_typeid(value, profile); break; case 2: rc = check_typeidversion(value, profile); break; case 4: rc = check_uuid(value, profile); break; case 5: rc = check_name(value, profile); break; } #ifdef MYDEBUG printf("%s word:%s value:%s rc:%d\n", __func__, word, value, rc); #endif return rc; } static int setprofile(struct vdpdata *profile) { unsigned int i; char *word, *value; for (i = 0; i < DIM(tokens) && tokens[i]; ++i) { word = tokens[i]; #ifdef MYDEBUG printf("%s word:%s tokens[%d]=%s\n", __func__, word, i, tokens[i]); #endif if ((value = strchr(tokens[i], '='))) *value++ = '\0'; else { fprintf(stderr, "%s missing argument in %s\n", progname, tokens[i]); return -1; } if (checkword(word, value, profile)) return -1; } return 0; } static int has_key(struct vdpdata *found) { return found->key[0] != '\0'; } static void print_pairs(struct vdpdata *found) { int i; char buf[32]; for (i = 0; i < found->pairs; ++i) { unsigned char *xp = found->addr[i].mac; sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", xp[0], xp[1], xp[2], xp[3], xp[4], xp[5]); printf("\t%hd %s\n", found->addr[i].vlanid, buf); } } static void print_profile(struct vdpdata *found) { char uuid[64]; printf("key:%s mgrid:%d typeid:%#x typeidver:%d\n", found->key, found->mgrid, found->typeid, found->typeidver); uuid2buf(found->uuid, uuid); printf("\tuuid:%s\n", uuid); if (found->pairs) print_pairs(found); } static int change_profile(struct vdpdata *change, struct vdpdata *alter) { #ifdef MYDEBUG printf("%s alter->modified:%#x\n", __func__, alter->modified); #endif if ((alter->modified & (1 << f_map))) { change->pairs = alter->pairs; memcpy(change->addr, alter->addr, sizeof alter->addr); } if ((alter->modified & (1 << f_mgrid))) change->mgrid = alter->mgrid; if ((alter->modified & (1 << f_typeid))) change->typeid = alter->typeid; if ((alter->modified & (1 << f_typeidver))) change->typeidver = alter->typeidver; if ((alter->modified & (1 << f_uuid))) memcpy(change->uuid, alter->uuid, sizeof change->uuid); return 0; } static void show_profiles(char *thisone) { unsigned int i; if (thisone) { struct vdpdata *showme = findkey(thisone); if (showme) print_profile(showme); } else for (i = 0; i < DIM(vsidata); ++i) if (has_key(&vsidata[i])) print_profile(&vsidata[i]); } /* * Parse the profile string of the form * key=###,mgrid=###,typeid=###,typeidver=###,uuid=###,mac=xxx,vlan=xxx{1,10} */ static int parse_profile(char *profile, struct vdpdata *target) { struct vdpdata nextone; char *copy = strdup(profile); #ifdef MYDEBUG printf("%s profile:%s\n", __func__, profile); #endif memset(&nextone, 0, sizeof nextone); settokens(profile); if (setprofile(&nextone)) { fprintf(stderr, "%s: ignore invalid profile data (%s)\n", progname, copy); free(copy); return -1; } if (!has_key(&nextone)) { fprintf(stderr, "%s ignore keyless profile data (%s)\n", progname, copy); free(copy); return -1; } free(copy); #ifdef MYDEBUG print_profile(&nextone); #endif *target = nextone; return 0; } static void find_field(unsigned char mode, char *buf) { int comma = 0; *buf = '\0'; if ((mode & (1 << f_map)) == 0) { strcat(buf, "map"); comma = 1; } if ((mode & (1 << f_mgrid)) == 0) { if (comma) strcat(buf, ","); strcat(buf, "mgrid"); comma = 1; } if ((mode & (1 << f_typeid)) == 0) { if (comma) strcat(buf, ","); strcat(buf, "typeid"); comma = 1; } if ((mode & (1 << f_typeidver)) == 0) { if (comma) strcat(buf, ","); strcat(buf, "typeidver"); comma = 1; } if ((mode & (1 << f_uuid)) == 0) { if (comma) strcat(buf, ","); strcat(buf, "uuid"); } } static void isvalid_profile(struct vdpdata *vdp) { char buf[64]; unsigned char mode = 1 << f_map | 1 << f_mgrid | 1 << f_typeid | 1 << f_typeidver | 1 << f_uuid; if (vdp->modified != mode) { find_field(vdp->modified, buf); fprintf(stderr, "%s key %s misses profile fields %s\n", progname, vdp->key, buf); memset(vdp->key, 0, sizeof vdp->key); } } static int make_profiles(char *profile, char *newkey) { struct vdpdata *vdp, nextone; if (parse_profile(profile, &nextone)) return -1; if (!newkey) { if (findkey(nextone.key)) { fprintf(stderr, "%s profile key %s already exits\n", progname, nextone.key); return -1; } if (!(vdp = nextfree())) { fprintf(stderr, "%s too many profiles\n", progname); return -1; } *vdp = nextone; } else { struct vdpdata *found; if (!(found = findkey(nextone.key))) { fprintf(stderr, "%s profile key %s does not exit\n", progname, nextone.key); return -1; } if (!(vdp = nextfree())) { fprintf(stderr, "%s too many profiles\n", progname); return -1; } *vdp = *found; strncpy(vdp->key, newkey, sizeof vdp->key); change_profile(vdp, &nextone); } isvalid_profile(vdp); return 0; } static int del_profiles(char *name) { struct vdpdata *vdp; if (!(vdp = findkey(name))) { fprintf(stderr, "%s profile key %s not found\n", progname, name); return -1; } memset(vdp->key, 0, sizeof vdp->key); return 0; } static int copy_profiles(char *profile) { char *newprofile, *newkey; kill_white(profile); if (strncmp(profile, COPY_OP, strlen(COPY_OP))) { fprintf(stderr, "%s missing key new=name\n", progname); return -1; } newkey = profile + 4; newprofile = strchr(newkey, ','); if (!newprofile) { fprintf(stderr, "%s invalid copy command\n", progname); return -1; } *newprofile = '\0'; return make_profiles(newprofile + 1, newkey); } /* * Detect the profile operation */ static int forwardline(char *buf) { if (strncmp(buf, COPY_OP, strlen(COPY_OP)) == 0) return copy_profiles(buf); return make_profiles(buf, 0); } /* * Read a full line from the file. Remove comments and ignore blank lines. * Also concatenate lines terminated with . */ #define COMMENT "#*;" /* Comments in [#*;] and */ static char *fullline(FILE * fp, char *buffer, size_t buflen) { int more = 0, off = 0; char *cp; static int lineno = 0; do { if ((cp = fgets(buffer + off, buflen - off, fp)) == NULL) { if (more == 2) { fprintf(stderr, "%s line %d unexpected EOF\n", progname, lineno); exit(1); } return NULL; /* No more lines */ } ++lineno; if ((cp = strchr(buffer, '\n')) == NULL) { fprintf(stderr, "%s line %d too long", progname, lineno); exit(1); } else *cp = '\0'; if ((cp = strpbrk(buffer, COMMENT)) != NULL) *cp = '\0'; /* Drop comment */ for (cp = buffer; *cp && isspace(*cp); ++cp) ; /* Skip leading space */ if (*cp == '\0') more = 1; /* Empty line */ else if (*(cp + strlen(cp) - 1) == '\\') { more = 2; /* Line concatenation */ *(cp + strlen(cp) - 1) = '\0'; off = strlen(buffer); } else more = 0; } while (more); memmove(buffer, cp, strlen(cp) + 1); return buffer; } static int read_profiles(char *cfgfile) { FILE *fp; char buffer[1024]; int rc = 0; if (strcmp(cfgfile, "-")) { if ((fp = fopen(cfgfile, "r")) == NULL) { perror(cfgfile); exit(1); } } else { fp = stdin; cfgfile = ""; } while (fullline(fp, buffer, sizeof buffer)) rc |= forwardline(buffer); if (fp != stdin) fclose(fp); return rc; } static char *cmd_name(char cmd) { switch (cmd) { case CMD_ASSOC: return "assoc"; case CMD_DEASSOC: return "dis-assoc"; case CMD_PREASSOC: return "preassoc"; case CMD_RRPREASSOC: return "rr-preassoc"; case CMD_SLEEP: return "sleep"; case CMD_ECHO: return "echo"; case CMD_EXTERN: return "extern"; case CMD_GETMSG: return "getmsg"; case CMD_SETDF: return "setdf"; } return "unknown"; } static void show_command(struct command *cmdp, int withrc) { int j; printf("%s key:%s waittime:%d repeats:%d delay:%d expected-rc:", cmd_name(cmdp->cmd), cmdp->key, cmdp->waittime, cmdp->repeats, cmdp->delay); for (j = 0; j < cmdp->no_err; ++j) printf("%d ", cmdp->errors[j]); if (withrc) printf("rc:%d", cmdp->rc); printf("\n"); } static void show_commands(int withrc) { unsigned int i; for (i = 0; i < DIM(cmds) && i < cmdidx; ++i) show_command(&cmds[i], withrc); } static int filldefaults(char *cmd, char *value) { int no, ec; no = getnumber(cmd, value, '\0', &ec); if (ec) return -1; if (*cmd == 'd') defaults.delay = no; if (*cmd == 'w') defaults.waittime = no; if (*cmd == 'r') defaults.repeats = no; return 0; } static int setdefaults(char *line) { unsigned int i = 0; int rc = 0; settokens(line); for (; i < DIM(tokens) && tokens[i]; ++i) { char *equal = strchr(tokens[i], '='); if (!equal) { fprintf(stderr, "%s: invalid syntax (%s) for" " command %s\n", progname, tokens[i], cmd_name(CMD_SETDF)); return -1; } rc |= filldefaults(tokens[i], equal + 1); } return rc; } static int fillvalue(char *cmd, char *value) { int no, ec; no = getnumber(cmd, value, '\0', &ec); if (ec) return -1; if (*cmd == 'd') cmds[cmdidx].delay = no; if (*cmd == 'w') cmds[cmdidx].waittime = no; else if (*cmd == 'r') cmds[cmdidx].repeats = no; else if (*cmd == 'e') { if (cmds[cmdidx].no_err >= DIM(cmds[cmdidx].errors)) { fprintf(stderr, "%s too many errors expected\n", progname); return -1; } cmds[cmdidx].errors[cmds[cmdidx].no_err++] = no; } return 0; } static int needkey(char x) { return x == CMD_ASSOC || x == CMD_DEASSOC || x == CMD_PREASSOC || x == CMD_RRPREASSOC; } static int parse_cmd(char type, char *line) { unsigned int i = 0; int rc = 0; #ifdef MYDEBUG printf("%s cmd:%c line:%s cmdidx:%d\n", __func__, type, line, cmdidx); #endif if (cmdidx >= DIM(cmds)) { fprintf(stderr, "%s: too many commands\n", progname); exit(2); } cmds[cmdidx].cmd = type; if (type == CMD_ECHO) { cmds[cmdidx].text = line; goto done; } else if (type == CMD_EXTERN) { cmds[cmdidx].text = line; goto done; } settokens(line); if (needkey(type) && !findkey(tokens[i])) { fprintf(stderr, "%s: unknown profile %s, command ignored\n", progname, tokens[i]); return -1; } if (needkey(type)) { strncpy(cmds[cmdidx].key, tokens[i], strlen(tokens[i])); i++; } else strcpy(cmds[cmdidx].key, "---"); for (; i < DIM(tokens) && tokens[i]; ++i) { char *equal = strchr(tokens[i], '='); if (!equal) { fprintf(stderr, "%s: invalid syntax (%s) for" " command %s\n", progname, tokens[i], cmd_name(type)); return -1; } rc |= fillvalue(tokens[i], equal + 1); } done: if (!cmds[cmdidx].no_err) { /* Default error is 0 */ cmds[cmdidx].no_err = 1; /* Default behavior for GETMSG, time out or 1 message */ if (cmds[cmdidx].cmd == CMD_GETMSG) { cmds[cmdidx].no_err = 2; cmds[cmdidx].errors[1] = 1; /* 1 Message */ } } if (!cmds[cmdidx].repeats) /* Default repeats is 1 */ cmds[cmdidx].repeats = defaults.repeats; if (!cmds[cmdidx].waittime) /* Default waittime is 1 sec */ cmds[cmdidx].waittime = defaults.waittime; if (!cmds[cmdidx].delay) /* Default delay is 1 sec */ cmds[cmdidx].delay = defaults.delay; if (rc == 0) ++cmdidx; return rc; } static int cmd_checkrc(struct command *cmdp) { int i; for (i = 0; i < cmdp->no_err; ++i) if (cmdp->rc == cmdp->errors[i]) { if (verbose) printf("SUCCESS command %s\n", cmd_name(cmdp->cmd)); return 0; } if (verbose) { printf("FAILURE "); show_command(cmdp, 1); } return -1; } static void cmd_extern(struct command *cmdp) { unsigned int i; int rc = 0; for (i = 0; i < cmdp->repeats; ++i) rc |= system(cmdp->text); cmdp->rc = rc; } static void cmd_echo(struct command *cmdp) { unsigned int i; for (i = 0; i < cmdp->repeats; ++i) puts(cmdp->text); cmdp->rc = 0; } static void cmd_sleep(struct command *cmdp) { unsigned int i; if (cmdp->waittime) for (i = 0; i < cmdp->repeats; ++i) cmdp->rc |= sleep(cmdp->waittime); } static int rc_ok(struct command *cmdp) { int i; for (i = 0; i < cmdp->no_err; ++i) if (cmdp->rc == cmdp->errors[i]) return 1; return 0; } static void cmd_profile(struct command *cmdp) { struct vdpdata *vdp = findkey(cmdp->key); int got_ack = 0; unsigned int i; if (!vdp) { cmdp->rc = ENFILE; return; } if ((cmdp->rc = lldp_send(cmdp, vdp)) >= 0) for (i = 0; got_ack == 0 && i < cmdp->repeats; ++i) { usleep(cmdp->delay * 1000); got_ack = lldp_ack(cmdp); if (got_ack < 0) { /* Error */ cmdp->rc = -1; break; } else if (got_ack > 0) { /* Got ack */ if (rc_ok(cmdp)) break; else got_ack = 0; } } } static int runcmds() { unsigned int i; int rc = 0; for (i = 0; i < cmdidx && i < DIM(cmds); ++i) { if (verbose) printf("start command %s waittime:%d\n", cmd_name(cmds[i].cmd), cmds[i].waittime); switch (cmds[i].cmd) { case CMD_ASSOC: case CMD_DEASSOC: case CMD_PREASSOC: case CMD_RRPREASSOC: cmd_profile(&cmds[i]); break; case CMD_SLEEP: cmd_sleep(&cmds[i]); break; case CMD_EXTERN: cmd_extern(&cmds[i]); break; case CMD_ECHO: cmd_echo(&cmds[i]); break; case CMD_GETMSG: lldp_wait(&cmds[i]); break; } rc = cmd_checkrc(&cmds[i]); if (rc) break; } return rc; } #include int main(int argc, char **argv) { extern int optind, opterr; extern char *optarg; int ch, rc, needif = 0; char *slash, mybuf[32]; progname = (slash = strrchr(argv[0], '/')) ? slash + 1 : argv[0]; while ((ch = getopt(argc, argv, ":A:C:D:F:S::a:d:e:E:g:p:r:s::i:v")) != EOF) switch (ch) { case '?': fprintf(stderr, "%s: unknown option -%c\n", progname, optopt); exit(1); case ':': fprintf(stderr, "%s missing option argument for -%c\n", progname, optopt); exit(1); case 'F': read_profiles(optarg); break; case 'D': del_profiles(optarg); break; case 'C': copy_profiles(optarg); break; case 'S': show_profiles(optarg); break; case 'A': make_profiles(optarg, 0); break; case 'v': ++verbose; break; case 'i': ifname = optarg; ifindex = if_nametoindex(ifname); break; case CMD_SETDF: if (setdefaults(optarg)) return 1; break; case CMD_SLEEP: if (!optarg) { optarg = mybuf, strncpy(mybuf, "w=1", sizeof mybuf); } parse_cmd(ch, optarg); break; case CMD_RRPREASSOC: case CMD_PREASSOC: case CMD_DEASSOC: case CMD_ASSOC: case CMD_GETMSG: needif = 1; /* Fall through intended */ case CMD_ECHO: case CMD_EXTERN: parse_cmd(ch, optarg); break; } #ifdef MYDEBUG for (; optind < argc; ++optind) printf("%d %s\n", optind, argv[optind]); #endif if (!needif) exit(0); if (!ifname) { fprintf(stderr, "%s interface missing or nonexistant\n", progname); exit(2); } lldpad_pid(); if ((my_sock = open_socket(NETLINK_ROUTE)) < 0) exit(4); if (verbose >= 2) show_commands(0); rc = runcmds(); close(my_sock); if (verbose >= 2) show_commands(1); return rc; } lldpad-0.9.46/tlv_dcbx.c000066400000000000000000001276341215142747300150460ustar00rootroot00000000000000/******************************************************************************* LLDP Agent Daemon (LLDPAD) Software Copyright(c) 2007-2010 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. The full GNU General Public License is included in this distribution in the file called "COPYING". Contact Information: open-lldp Mailing List *******************************************************************************/ #include #include #include "lldp.h" #include "dcb_types.h" #include "lldp_tlv.h" #include "tlv_dcbx.h" #include "dcb_protocol.h" #include "lldp_dcbx.h" #include "lldp/states.h" #include "lldp/agent.h" #include "messages.h" bool process_dcbx_ctrl_tlv(struct port *port, struct lldp_agent *); bool process_dcbx_pg_tlv(struct port *port, struct lldp_agent *); bool process_dcbx_pfc_tlv(struct port *port, struct lldp_agent *); bool process_dcbx_app_tlv(struct port *port, struct lldp_agent *); bool process_dcbx_llink_tlv(struct port *port, struct lldp_agent *); /* for the specified remote feature, if the feature is not present in the * EventFlag parameter (indicating it was not received in the DCB TLV), then * check and update the peer data store object for the feature if it is * currently marked as being present. * * returns true if the feature is not present now * the peer data store feature object is set to 'not present' * false otherwise. */ static u32 check_feature_not_present(char *device_name, u32 subtype, u32 EventFlag, u32 feature) { pg_attribs peer_pg; pfc_attribs peer_pfc; app_attribs peer_app; llink_attribs peer_llink; /* if (!DCB_TEST_FLAGS(EventFlag, feature, feature)) { */ if (DCB_TEST_FLAGS(EventFlag, feature, feature)) return false; switch (feature) { case DCB_REMOTE_CHANGE_PG: if ((get_peer_pg(device_name, &peer_pg) == cmd_success) && (peer_pg.protocol.TLVPresent == true)) { peer_pg.protocol.TLVPresent = false; put_peer_pg(device_name, &peer_pg); } break; case DCB_REMOTE_CHANGE_PFC: if ((get_peer_pfc(device_name, &peer_pfc) == cmd_success) && (peer_pfc.protocol.TLVPresent == true)) { peer_pfc.protocol.TLVPresent = false; put_peer_pfc(device_name, &peer_pfc); } break; case DCB_REMOTE_CHANGE_LLINK: if ((get_peer_llink(device_name, subtype, &peer_llink) == cmd_success) && (peer_llink.protocol.TLVPresent == true)) { peer_llink.protocol.TLVPresent = false; put_peer_llink(device_name, subtype, &peer_llink); } break; default: if (feature & DCB_REMOTE_CHANGE_APPTLV(subtype)) { if ((get_peer_app(device_name, subtype, &peer_app) == cmd_success) && (peer_app.protocol.TLVPresent == true)) { peer_app.protocol.TLVPresent = false; peer_app.Length = 0; put_peer_app(device_name, subtype, &peer_app); } } break; } return true; } struct unpacked_tlv *bld_dcbx1_tlv(struct dcbx_tlvs *dcbx) { struct unpacked_tlv *tlv = create_tlv(); struct packed_tlv *ptlv = NULL; u8 oui[DCB_OUI_LEN] = INIT_DCB_OUI; u8 subtype = dcbx_subtype1; u32 offset = 0; if (!tlv) return NULL; tlv->type = ORG_SPECIFIC_TLV; tlv->length = DCB_OUI_LEN + OUI_SUBTYPE_LEN; if (dcbx->control) { tlv->length = tlv->length + dcbx->control->length; if (dcbx->control->length) tlv->length+=2; } if (dcbx->pg1) { tlv->length = tlv->length + dcbx->pg1->length; if (dcbx->pg1->length) tlv->length+=2; } if (dcbx->pfc1) { tlv->length = tlv->length + dcbx->pfc1->length; if (dcbx->pfc1->length) tlv->length+=2; } if (dcbx->app1) { tlv->length = tlv->length + dcbx->app1->length; tlv->length+=2; } if (dcbx->llink) { tlv->length = tlv->length + dcbx->llink->length; if (dcbx->llink->length) tlv->length+=2; } tlv->info = (u8 *)malloc(tlv->length); if (tlv->info == NULL) goto error; memset(tlv->info,0, tlv->length); if ((DCB_OUI_LEN + OUI_SUBTYPE_LEN) > tlv->length) goto error; memcpy(tlv->info, &oui, DCB_OUI_LEN); offset += DCB_OUI_LEN; memcpy(&tlv->info[offset], &subtype, OUI_SUBTYPE_LEN); offset += OUI_SUBTYPE_LEN; if (tlv_ok(dcbx->control)) { ptlv = pack_tlv(dcbx->control); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->pg1)) { ptlv = pack_tlv(dcbx->pg1); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->pfc1)) { ptlv = pack_tlv(dcbx->pfc1); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->app1)) { ptlv = pack_tlv(dcbx->app1); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->llink)) { ptlv = pack_tlv(dcbx->llink); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (offset != tlv->length) LLDPAD_DBG("assert offset == tlv->length\n"); assert(offset == tlv->length); return tlv; error: ptlv = free_pkd_tlv(ptlv); if (tlv) { if (tlv->info) free(tlv->info); free(tlv); } LLDPAD_DBG("bld_dcbx1_tlv: malloc failure \n"); return NULL; } struct unpacked_tlv *bld_dcbx2_tlv(struct dcbx_tlvs *dcbx) { struct unpacked_tlv *tlv = create_tlv(); struct packed_tlv *ptlv = NULL; u8 oui[DCB_OUI_LEN] = INIT_DCB_OUI; u8 subtype = dcbx_subtype2; u32 offset = 0; if (!tlv) return NULL; tlv->type = ORG_SPECIFIC_TLV; tlv->length = DCB_OUI_LEN + OUI_SUBTYPE_LEN; if (dcbx->control) { tlv->length = tlv->length + dcbx->control->length; if (dcbx->control->length) tlv->length+=2; } if (dcbx->pg2) { tlv->length = tlv->length + dcbx->pg2->length; if (dcbx->pg2->length) tlv->length+=2; } if (dcbx->pfc2) { tlv->length = tlv->length + dcbx->pfc2->length; if (dcbx->pfc2->length) tlv->length+=2; } if (dcbx->app2) { tlv->length = tlv->length + dcbx->app2->length; tlv->length+=2; } tlv->info = (u8 *)malloc(tlv->length); if (tlv->info == NULL) goto error; memset(tlv->info,0, tlv->length); if ((DCB_OUI_LEN + OUI_SUBTYPE_LEN) > tlv->length) goto error; memcpy(tlv->info, &oui, DCB_OUI_LEN); offset += DCB_OUI_LEN; memcpy(&tlv->info[offset], &subtype, OUI_SUBTYPE_LEN); offset += OUI_SUBTYPE_LEN; if (tlv_ok(dcbx->control)) { ptlv = pack_tlv(dcbx->control); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->pg2)) { ptlv = pack_tlv(dcbx->pg2); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->pfc2)) { ptlv = pack_tlv(dcbx->pfc2); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (tlv_ok(dcbx->app2)) { ptlv = pack_tlv(dcbx->app2); if (!ptlv || ((ptlv->size+offset) > tlv->length)) goto error; memcpy(&tlv->info[offset], ptlv->tlv, ptlv->size); offset += ptlv->size; ptlv = free_pkd_tlv(ptlv); } if (offset != tlv->length) LLDPAD_DBG("assert offset == tlv->length\n"); assert(offset == tlv->length); return tlv; error: ptlv = free_pkd_tlv(ptlv); if (tlv) { if (tlv->info) free(tlv->info); free(tlv); } LLDPAD_DBG("bld_dcbx2_tlv: malloc failure \n"); return NULL; } struct unpacked_tlv *bld_dcbx_ctrl_tlv(struct dcbx_tlvs *dcbx) { struct unpacked_tlv *tlv = create_tlv(); control_protocol_attribs ctrl_cfg; int i; u8 oper_version; u8 max_version; u32 seqno; u32 ackno; if (!tlv) return NULL; get_control(dcbx->ifname, &ctrl_cfg); oper_version = (u8)ctrl_cfg.Oper_version; max_version = (u8)ctrl_cfg.Max_version; seqno = htonl(ctrl_cfg.SeqNo); ackno = htonl(ctrl_cfg.AckNo); tlv->type = DCB_CONTROL_TLV; tlv->length = DCBX_CTRL_LEN; tlv->info = (u8 *)malloc(tlv->length); if (tlv->info) { memset(tlv->info,0, tlv->length); i = 0; memcpy(tlv->info, &oper_version, sizeof(oper_version)); i += sizeof(oper_version); memcpy(tlv->info + i, &max_version, sizeof(max_version)); i += sizeof(max_version); memcpy(tlv->info + i, &seqno, sizeof(seqno)); i += sizeof(seqno); memcpy(tlv->info + i, &ackno ,sizeof(ackno)); i = 0; } else { LLDPAD_DBG("bld_dcbx_ctrl_tlv: Failed to malloc info\n"); free(tlv); return NULL; } return tlv; } struct unpacked_tlv *bld_dcbx1_pg_tlv(struct dcbx_tlvs *dcbx, bool *success) { struct dcbx1_pg_info *pg_info; struct unpacked_tlv *tlv = create_tlv(); pg_attribs pg_cfg; int result, i; u8 tmpbyte = 0; *success = false; if (!tlv) { return NULL; } result = get_pg(dcbx->ifname, &pg_cfg); if (result == cmd_success) { mark_pg_sent(dcbx->ifname); if (!(pg_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } pg_info = (struct dcbx1_pg_info *)malloc(DCBX1_PG_LEN); if (pg_info) { memset(pg_info, 0, DCBX1_PG_LEN); pg_info->hdr.oper_version = (u8)pg_cfg.protocol.Oper_version; pg_info->hdr.max_version = (u8)pg_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if (pg_cfg.protocol.Enable == true) pg_info->hdr.ewe |= BIT7; if (pg_cfg.protocol.Willing == true) pg_info->hdr.ewe |= BIT6; if (pg_cfg.protocol.Error == true) pg_info->hdr.ewe |= BIT5; pg_info->hdr.sub_type = DEFAULT_SUBTYPE; for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { pg_info->data.pg_percent[i] = pg_cfg.tx.pg_percent[i]; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { tmpbyte = 0; tmpbyte = pg_cfg.tx.up[i].bwgid; tmpbyte = tmpbyte << 5; u8 tmpprio = 0; tmpprio = (u8)pg_cfg.tx.up[i].strict_priority; tmpprio = tmpprio << 3; tmpbyte |= tmpprio; pg_info->data.up_cfg[i].byte1 = tmpbyte; pg_info->data.up_cfg[i].byte2 = pg_cfg.tx.up[i].percent_of_pg_cap; } tlv->length = DCBX1_PG_LEN; } else { LLDPAD_DBG("bld_dcbx1_pg_tlv: Failed to malloc pg_info\n"); free(tlv); return NULL; } tlv->type = DCB_PRIORITY_GROUPS_TLV; tlv->info = (u8 *)pg_info; *success = true; return tlv; } struct unpacked_tlv *bld_dcbx2_pg_tlv(struct dcbx_tlvs *dcbx, bool *success) { struct dcbx2_pg_info *pg_info; struct unpacked_tlv *tlv = create_tlv(); pg_attribs pg_cfg; int result, i; u8 tmpbyte = 0; int j, k; *success = false; if (!tlv) { return NULL; } result = get_pg(dcbx->ifname, &pg_cfg); if (result == cmd_success) { mark_pg_sent(dcbx->ifname); if (!(pg_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } pg_info = (struct dcbx2_pg_info *)malloc(DCBX2_PG_LEN); if (pg_info) { memset(pg_info, 0, DCBX2_PG_LEN); pg_info->hdr.oper_version = (u8)pg_cfg.protocol.Oper_version; pg_info->hdr.max_version = (u8)pg_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if (pg_cfg.protocol.Enable == true) pg_info->hdr.ewe |= BIT7; if (pg_cfg.protocol.Willing == true) pg_info->hdr.ewe |= BIT6; if (pg_cfg.protocol.Error == true) pg_info->hdr.ewe |= BIT5; pg_info->hdr.sub_type = DEFAULT_SUBTYPE; for (j=0,k=0 ; k < MAX_BANDWIDTH_GROUPS; j++, k=k+2) { tmpbyte = 0; if (pg_cfg.tx.up[k].strict_priority == dcb_link) tmpbyte = 0xf; else tmpbyte = pg_cfg.tx.up[k].pgid & 0xf; tmpbyte <<= 4; if (pg_cfg.tx.up[k+1].strict_priority == dcb_link) tmpbyte |= 0xf; else tmpbyte |= (pg_cfg.tx.up[k+1].pgid & 0xf); pg_info->data.pg_ids[j] = tmpbyte; } for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { pg_info->data.pg_percent[i] = pg_cfg.tx.pg_percent[i]; } pg_info->data.num_tcs = pg_cfg.num_tcs; tlv->length = DCBX2_PG_LEN; } else { LLDPAD_DBG("bld_dcbx2_pg_tlv: Failed to malloc pg_info\n"); free(tlv); return NULL; } tlv->type = DCB_PRIORITY_GROUPS_TLV2; tlv->info = (u8 *)pg_info; *success = true; return tlv; } struct unpacked_tlv *bld_dcbx1_pfc_tlv(struct dcbx_tlvs *dcbx, bool *success) { struct dcbx1_pfc_info *pfc_info; struct unpacked_tlv *tlv = create_tlv(); pfc_attribs pfc_cfg; int result,i; *success = false; if (!tlv) return NULL; result = get_pfc(dcbx->ifname, &pfc_cfg); if (result == cmd_success) { mark_pfc_sent(dcbx->ifname); if (!(pfc_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } pfc_info = (struct dcbx1_pfc_info *)malloc(DCBX1_PFC_LEN); if (pfc_info) { memset(pfc_info, 0, DCBX1_PFC_LEN); pfc_info->hdr.oper_version = (u8)pfc_cfg.protocol.Oper_version; pfc_info->hdr.max_version = (u8)pfc_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if(pfc_cfg.protocol.Enable == true) pfc_info->hdr.ewe |= BIT7; if(pfc_cfg.protocol.Willing == true) pfc_info->hdr.ewe |= BIT6; if(pfc_cfg.protocol.Error == true) pfc_info->hdr.ewe |= BIT5; pfc_info->hdr.sub_type = DEFAULT_SUBTYPE; u8 temp = 0; for(i = 0; i < MAX_USER_PRIORITIES; i++) { temp = (u8)(pfc_cfg.admin[i] << i); pfc_info->data.admin_map |= temp; } tlv->length = DCBX1_PFC_LEN; } else { LLDPAD_DBG("bld_dcbx1_pfc_tlv: Failed to malloc pfc_info\n"); free(tlv); return NULL; } tlv->type = DCB_PRIORITY_FLOW_CONTROL_TLV; tlv->info = (u8 *)pfc_info; *success = true; return tlv; } struct unpacked_tlv *bld_dcbx2_pfc_tlv(struct dcbx_tlvs *dcbx, bool *success) { struct dcbx2_pfc_info *pfc_info; struct unpacked_tlv *tlv = create_tlv(); pfc_attribs pfc_cfg; int result,i; *success = false; if (!tlv) return NULL; result = get_pfc(dcbx->ifname, &pfc_cfg); if (result == cmd_success) { mark_pfc_sent(dcbx->ifname); if (!(pfc_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } pfc_info = (struct dcbx2_pfc_info *)malloc(DCBX2_PFC_LEN); if (pfc_info) { memset(pfc_info, 0, DCBX2_PFC_LEN); pfc_info->hdr.oper_version = (u8)pfc_cfg.protocol.Oper_version; pfc_info->hdr.max_version = (u8)pfc_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if(pfc_cfg.protocol.Enable == true) pfc_info->hdr.ewe |= BIT7; if(pfc_cfg.protocol.Willing == true) pfc_info->hdr.ewe |= BIT6; if(pfc_cfg.protocol.Error == true) pfc_info->hdr.ewe |= BIT5; pfc_info->hdr.sub_type = DEFAULT_SUBTYPE; u8 temp = 0; for(i = 0; i < MAX_USER_PRIORITIES; i++) { temp = (u8)(pfc_cfg.admin[i] << i); pfc_info->data.admin_map |= temp; } pfc_info->data.num_tcs = pfc_cfg.num_tcs; tlv->length = DCBX2_PFC_LEN; } else { LLDPAD_DBG("bld_dcbx2_pfc_tlv: Failed to malloc pfc_info\n"); free(tlv); return NULL; } tlv->type = DCB_PRIORITY_FLOW_CONTROL_TLV2; tlv->info = (u8 *)pfc_info; *success = true; return tlv; } struct unpacked_tlv *bld_dcbx1_app_tlv(struct dcbx_tlvs *dcbx, u32 sub_type, bool *success) { struct dcbx1_app_info *app_info; struct unpacked_tlv *tlv = create_tlv(); app_attribs app_cfg; int result; u32 i,len; *success = false; if (!tlv) return NULL; memset(&app_cfg, 0, sizeof(app_cfg)); result = get_app(dcbx->ifname, sub_type, &app_cfg); if (result == cmd_success) { mark_app_sent(dcbx->ifname); if (!(app_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } len = sizeof(struct dcbx_tlv_header) + app_cfg.Length; app_info = (struct dcbx1_app_info *)malloc(len); if (app_info) { memset(app_info,0,sizeof(struct dcbx1_app_info)); app_info->hdr.oper_version = (u8)app_cfg.protocol.Oper_version; app_info->hdr.max_version = (u8)app_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if(app_cfg.protocol.Enable == true) app_info->hdr.ewe |= BIT7; if(app_cfg.protocol.Willing == true) app_info->hdr.ewe |= BIT6; if(app_cfg.protocol.Error == true) app_info->hdr.ewe |= BIT5; app_info->hdr.sub_type = (u8)sub_type; for (i = 0; i < app_cfg.Length; i++) app_info->data[i] = app_cfg.AppData[i]; tlv->length = (u16)len; } else { LLDPAD_DBG("bld_dcbx1_app_tlv: Failed to malloc app_info\n"); free(tlv); return NULL; } tlv->type = DCB_APPLICATION_TLV; tlv->info = (u8 *)app_info; *success = true; return tlv; } void set_proto(struct dcbx2_app_cfg *app_cfg, int subtype) { u8 oui[DCB_OUI_LEN] = INIT_DCB_OUI; switch (subtype) { case APP_FCOE_STYPE: app_cfg->prot_id = PROTO_ID_FCOE; app_cfg->byte1 = (oui[0] & PROTO_ID_OUI_MASK) | (PROTO_ID_L2_ETH_TYPE & PROTO_ID_SF_TYPE); break; case APP_ISCSI_STYPE: app_cfg->prot_id = PROTO_ID_ISCSI; app_cfg->byte1 = (oui[0] & PROTO_ID_OUI_MASK) | (PROTO_ID_SOCK_NUM & PROTO_ID_SF_TYPE); break; case APP_FIP_STYPE: app_cfg->prot_id = PROTO_ID_FIP; app_cfg->byte1 = (oui[0] & PROTO_ID_OUI_MASK) | (PROTO_ID_L2_ETH_TYPE & PROTO_ID_SF_TYPE); break; } app_cfg->low_oui = (oui[2]<<8) | oui[1]; } struct unpacked_tlv *bld_dcbx2_app_tlv(struct dcbx_tlvs *dcbx, bool *success) { struct dcbx2_app_info *app_info; struct unpacked_tlv *tlv = create_tlv(); app_attribs app_cfg; int i, offset, result; bool advertise = false; *success = false; if (!tlv) return NULL; /* Verify there is something to advertise before building APP data */ for (i = 0; i < DCB_MAX_APPTLV; i++) { memset(&app_cfg, 0, sizeof(app_cfg)); result = get_app(dcbx->ifname, i, &app_cfg); if (result != cmd_success) { continue; } else if ((app_cfg.protocol.Advertise)) { advertise = true; break; } } if (!advertise) { free(tlv); *success = true; return NULL; } /* At least one APP entry exists so build the header and entries hdr * values are taken from the first APP entry found. The APP order is * set in dcb_types.h */ app_info = (struct dcbx2_app_info *)malloc(DCBX2_APP_LEN); tlv->length = DCBX2_APP_LEN; if (app_info) { struct dcbx2_app_cfg *app_data; memset(app_info, 0, DCBX2_APP_LEN); app_info->hdr.oper_version = (u8)app_cfg.protocol.Oper_version; app_info->hdr.max_version = (u8)app_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if(app_cfg.protocol.Enable == true) app_info->hdr.ewe |= BIT7; if(app_cfg.protocol.Willing == true) app_info->hdr.ewe |= BIT6; if(app_cfg.protocol.Error == true) app_info->hdr.ewe |= BIT5; app_info->hdr.sub_type = 0; for (offset = 0; i < DCB_MAX_APPTLV; i++) { result = get_app(dcbx->ifname, i, &app_cfg); if (result == cmd_success) { mark_app_sent(dcbx->ifname); if (!(app_cfg.protocol.Advertise)) continue; } tlv->length += DCBX2_APP_SIZE; app_info = realloc(app_info, tlv->length); if (!app_info) { free(app_info); free(tlv); return NULL; } app_data = &(app_info->data[offset++]); set_proto(app_data, i); memcpy (&app_data->up_map, &(app_cfg.AppData[0]), APP_STYPE_LEN); } } else { LLDPAD_DBG("bld_dcbx2_app_tlv: Failed to malloc app_info\n"); free(tlv); return NULL; } tlv->type = DCB_APPLICATION_TLV2; tlv->info = (u8 *)app_info; *success = true; return tlv; } struct unpacked_tlv *bld_dcbx_llink_tlv(struct dcbx_tlvs *dcbx, u32 sub_type, bool *success) { struct dcbx_llink_info *llink_info; struct unpacked_tlv *tlv = create_tlv(); llink_attribs llk_cfg; llink_cfg *cfg; struct dcbx_llink_cfg *pkt; int result; *success = false; if (!tlv) { return NULL; } result = get_llink(dcbx->ifname, sub_type, &llk_cfg); if (result == cmd_success) { mark_llink_sent(dcbx->ifname, sub_type); if (!(llk_cfg.protocol.Advertise)) { free(tlv); *success = true; return NULL; } } else { free(tlv); return NULL; } llink_info = (struct dcbx_llink_info *)malloc(DCBX_LLINK_LEN); if (llink_info) { memset(llink_info, 0, DCBX_LLINK_LEN); llink_info->hdr.oper_version = (u8)llk_cfg.protocol.Oper_version; llink_info->hdr.max_version = (u8)llk_cfg.protocol.Max_version; /* ewe Enable Willing Error */ if (llk_cfg.protocol.Enable == true) llink_info->hdr.ewe |= BIT7; if (llk_cfg.protocol.Willing == true) llink_info->hdr.ewe |= BIT6; if (llk_cfg.protocol.Error == true) llink_info->hdr.ewe |= BIT5; llink_info->hdr.sub_type = (u8)sub_type; cfg = &(llk_cfg.llink); pkt = &(llink_info->data); if(cfg->llink_status == true) pkt->byte1 |= BIT7; tlv->length = DCBX_LLINK_LEN; } else { LLDPAD_DBG("bld_dcbx_llink_tlv: Failed to malloc llink_info\n"); free(tlv); return NULL; } tlv->type = DCB_LLINK_TLV; tlv->info = (u8 *)llink_info; *success = true; return tlv; } bool unpack_dcbx1_tlvs(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { /* unpack the tlvs and store in manifest */ u8 *offset = NULL; /* iterator */ u16 current = 0, tl = 0; u16 end = 0; /* End of data blob */ struct unpacked_tlv *dcbtlv; struct dcbx_tlvs *tlvs; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; /* store highest dcbx subtype received */ if (agent->rx.dcbx_st < tlv->info[DCB_OUI_LEN]) { agent->rx.dcbx_st = tlv->info[DCB_OUI_LEN]; } /* OUI + subtype sizes equal the start of data blob */ offset = (u8 *)&tlv->info[OUI_SUBTYPE_LEN + DCB_OUI_LEN]; end = tlv->length - (OUI_SUBTYPE_LEN + DCB_OUI_LEN); /* Process */ do { dcbtlv = create_tlv(); if (!dcbtlv) { LLDPAD_DBG("ERROR: Failed to malloc space for incoming " "DCB TLV. \n"); return false; } memcpy(&tl, offset, sizeof(tl)); offset += sizeof(tl); dcbtlv->length = ntohs(tl) & 0x01FF; if (dcbtlv->length==0) { LLDPAD_DBG("ERROR: dcbtlv->length==0 \n"); free_unpkd_tlv(dcbtlv); return false; } dcbtlv->type = (u8)(ntohs(tl) >> 9); dcbtlv->info = (u8 *)malloc(dcbtlv->length); if (dcbtlv->info) { memset(dcbtlv->info, 0, dcbtlv->length); memcpy(dcbtlv->info, offset, dcbtlv->length); } else { LLDPAD_DBG("ERROR: Failed to malloc space for incoming " "TLV info \n"); free_unpkd_tlv(dcbtlv); return false; } current += dcbtlv->length + sizeof(tl); offset += dcbtlv->length; switch(dcbtlv->type) { case DCB_CONTROL_TLV: if (!(tlvs->dcbdu & RCVD_DCBX1_TLV_CTRL)) { tlvs->dcbdu |= RCVD_DCBX1_TLV_CTRL; tlvs->manifest->dcbx_ctrl = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP Ctrl TLV1 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_CTRL; free_unpkd_tlv(dcbtlv); } break; case DCB_PRIORITY_GROUPS_TLV: /* store if subtype 2 is not present */ if (agent->rx.dcbx_st == dcbx_subtype1) { if (tlvs->manifest->dcbx_pg == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_PG; tlvs->manifest->dcbx_pg = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP PG TLV1 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_PG; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; case DCB_PRIORITY_FLOW_CONTROL_TLV: /* store if subtype 2 is not present */ if (agent->rx.dcbx_st == dcbx_subtype1) { if (tlvs->manifest->dcbx_pfc == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_PFC; tlvs->manifest->dcbx_pfc = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP PFC TLV1 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_PFC; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; case DCB_APPLICATION_TLV: /* store if subtype 2 is not present */ if ((agent->rx.dcbx_st == dcbx_subtype1) && (dcbtlv->info[DCBX_HDR_SUB_TYPE_OFFSET] == APP_FCOE_STYPE)) { if (tlvs->manifest->dcbx_app == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_APP; tlvs->manifest->dcbx_app = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP APP TLV1 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_APP; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; case DCB_LLINK_TLV: if (dcbtlv->info[DCBX_HDR_SUB_TYPE_OFFSET] == LLINK_FCOE_STYPE) { if (tlvs->manifest->dcbx_llink == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_LLINK; tlvs->manifest->dcbx_llink = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP LLINK TLV1 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_LLINK; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; default: free_unpkd_tlv(dcbtlv); break; } dcbtlv = NULL; } while(current < end); return true; } bool unpack_dcbx2_tlvs(struct port *port, struct lldp_agent *agent, struct unpacked_tlv *tlv) { /* unpack the tlvs and store in manifest */ u8 *offset = NULL; /* iterator */ u16 current = 0, tl = 0; u16 end = 0; /* End of data blob */ struct unpacked_tlv *dcbtlv; struct dcbx_tlvs *tlvs; int subtype; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; /* store highest dcbx subtype received */ if (agent->rx.dcbx_st < tlv->info[DCB_OUI_LEN]) { agent->rx.dcbx_st = tlv->info[DCB_OUI_LEN]; } /* OUI + subtype sizes equal the start of data blob */ offset = (u8 *)&tlv->info[OUI_SUBTYPE_LEN + DCB_OUI_LEN]; end = tlv->length - (OUI_SUBTYPE_LEN + DCB_OUI_LEN); /* Process */ do { dcbtlv = create_tlv(); if (!dcbtlv) { LLDPAD_DBG("ERROR: Failed to malloc space for incoming " "DCB TLV. \n"); return false; } memcpy(&tl, offset, sizeof(tl)); offset += sizeof(tl); dcbtlv->length = ntohs(tl) & 0x01FF; if (dcbtlv->length==0) { LLDPAD_DBG("ERROR: dcbtlv->length==0 \n"); free_unpkd_tlv(dcbtlv); return false; } dcbtlv->type = (u8)(ntohs(tl) >> 9); dcbtlv->info = (u8 *)malloc(dcbtlv->length); if (dcbtlv->info) { memset(dcbtlv->info, 0, dcbtlv->length); memcpy(dcbtlv->info, offset, dcbtlv->length); } else { LLDPAD_DBG("ERROR: Failed to malloc space for incoming " "TLV info \n"); free_unpkd_tlv(dcbtlv); return false; } current += dcbtlv->length + sizeof(tl); offset += dcbtlv->length; switch(dcbtlv->type) { case DCB_CONTROL_TLV: if (tlvs->manifest->dcbx1 == NULL) { if (tlvs->manifest->dcbx_ctrl == NULL) { tlvs->dcbdu |= RCVD_DCBX2_TLV_CTRL; tlvs->manifest->dcbx_ctrl = dcbtlv; } else if (tlvs->dcbdu & RCVD_DCBX2_TLV_CTRL) { LLDPAD_DBG("** ERROR: DUP CTRL TLV2 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_CTRL; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; case DCB_PRIORITY_GROUPS_TLV2: if (tlvs->manifest->dcbx_pg == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_PG; tlvs->manifest->dcbx_pg = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP PG TLV2 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_PG; free_unpkd_tlv(dcbtlv); } break; case DCB_PRIORITY_FLOW_CONTROL_TLV2: if (tlvs->manifest->dcbx_pfc == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_PFC; tlvs->manifest->dcbx_pfc = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP PFC TLV2 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_PFC; free_unpkd_tlv(dcbtlv); } break; case DCB_APPLICATION_TLV2: subtype = dcbtlv->info[DCBX_HDR_SUB_TYPE_OFFSET]; if (subtype == 0) { if (tlvs->manifest->dcbx_app == NULL) { tlvs->dcbdu |= RCVD_DCBX_TLV_APP; tlvs->manifest->dcbx_app = dcbtlv; } else { LLDPAD_DBG("** ERROR: DUP APP TLV2 \n"); agent->rx.dupTlvs |= DUP_DCBX_TLV_APP; free_unpkd_tlv(dcbtlv); } } else { free_unpkd_tlv(dcbtlv); } break; default: free_unpkd_tlv(dcbtlv); break; } dcbtlv = NULL; } while(current < end); return true; } void mibUpdateObjects(struct port *port, struct lldp_agent *agent) { struct dcbx_tlvs *tlvs; u32 EventFlag = 0; int i; tlvs = dcbx_data(port->ifname); if (agent == NULL) return; if (tlvs->manifest->dcbx_ctrl) { if (process_dcbx_ctrl_tlv(port, agent) != true) { /* Error Set error condition for all features * on this port and trash DCB TLV */ } } else { /* Error Set error condition for all features * on this port and trash DCB TLV */ } if (tlvs->manifest->dcbx_pg) { if (process_dcbx_pg_tlv(port, agent) != true) { /* mark feature not present */ if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_PG)) { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG); } } else { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG); } } else { if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_PG)) { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PG); } } if (tlvs->manifest->dcbx_pfc) { if (process_dcbx_pfc_tlv(port, agent) != true) { /* mark feature not present */ if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_PFC)) { DCB_SET_FLAGS(EventFlag,DCB_REMOTE_CHANGE_PFC); } } else { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC); } } else { if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_PFC)) { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_PFC); } } if (tlvs->manifest->dcbx_app) { bool ret = process_dcbx_app_tlv(port, agent); if (ret) { for (i = 0; i < DCB_MAX_APPTLV; i++) DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(i)); } } else { app_attribs peer_app; memset(&peer_app, 0, sizeof(app_attribs)); for (i = 0; i < DCB_MAX_APPTLV; i++) { put_peer_app(port->ifname, i, &peer_app); DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_APPTLV(i)); } } if (tlvs->manifest->dcbx_llink) { if (process_dcbx_llink_tlv(port, agent) != true) { /* mark feature not present */ if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_LLINK)) { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK); } } else { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK); } } else { if (check_feature_not_present(port->ifname, 0, EventFlag, DCB_REMOTE_CHANGE_LLINK)) { DCB_SET_FLAGS(EventFlag, DCB_REMOTE_CHANGE_LLINK); } } /* Run the feature & control protocol for all features and subtypes */ run_dcb_protocol(port->ifname, EventFlag, DCB_MAX_APPTLV+1); EventFlag = 0; agent->rxChanges = true; return; } bool process_dcbx_ctrl_tlv(struct port *port, struct lldp_agent *agent) { struct dcbx_tlvs *tlvs; control_protocol_attribs peer_control; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; if (tlvs->manifest->dcbx_ctrl->length != DCBX_CTRL_LEN) { LLDPAD_DBG("process_dcbx_ctrl_tlv: ERROR - len\n"); return(false); } memset(&peer_control, 0, sizeof(control_protocol_attribs)); peer_control.Oper_version = tlvs->manifest->dcbx_ctrl->info [DCBX_CTRL_OPER_VER_OFFSET]; peer_control.Max_version = tlvs->manifest->dcbx_ctrl->info [DCBX_CTRL_MAX_VER_OFFSET]; u32 tmp32 = 0; memcpy(&tmp32, &tlvs->manifest->dcbx_ctrl->info [DCBX_CTRL_SEQNO_OFFSET], sizeof(u32)); peer_control.SeqNo = ntohl(tmp32); tmp32 = 0; memcpy(&tmp32,&tlvs->manifest->dcbx_ctrl->info [DCBX_CTRL_ACKNO_OFFSET], sizeof(u32)); peer_control.AckNo = ntohl(tmp32); LLDPAD_INFO("*** Received a DCB_CONTROL_TLV: -- SeqNo=%d, AckNo=%d \n", peer_control.SeqNo, peer_control.AckNo); peer_control.RxDCBTLVState = DCB_PEER_PRESENT; if (agent->rx.dupTlvs & DUP_DCBX_TLV_CTRL) { LLDPAD_INFO("** STORE: DUP CTRL TLV \n"); peer_control.Error_Flag |= DUP_DCBX_TLV_CTRL; } if (agent->rx.tooManyNghbrs) { LLDPAD_INFO("** STORE: TOO_MANY_NGHBRS\n"); peer_control.Error_Flag |= TOO_MANY_NGHBRS; } put_peer_control(port->ifname, &peer_control); return(true); } bool process_dcbx_pg_tlv(struct port *port, struct lldp_agent *agent) { pg_attribs peer_pg; struct dcbx_tlvs *tlvs; int i = 0; int j, k; u8 used[MAX_BANDWIDTH_GROUPS]; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; if (agent->rx.dcbx_st == dcbx_subtype2) { if (tlvs->manifest->dcbx_pg->length != DCBX2_PG_LEN) { LLDPAD_DBG("process_dcbx2_pg_tlv: ERROR - len\n"); return(false); } } else { if (tlvs->manifest->dcbx_pg->length != DCBX1_PG_LEN) { LLDPAD_DBG("process_dcbx1_pg_tlv: ERROR - len\n"); return(false); } } memset(&peer_pg, 0, sizeof(pg_attribs)); peer_pg.protocol.Advertise = true; peer_pg.protocol.Oper_version = tlvs->manifest->dcbx_pg->info [DCBX_HDR_OPER_VERSION_OFFSET]; peer_pg.protocol.Max_version = tlvs->manifest->dcbx_pg->info [DCBX_HDR_MAX_VERSION_OFFSET]; if (tlvs->manifest->dcbx_pg->info[DCBX_HDR_EWE_OFFSET] & BIT7) { peer_pg.protocol.Enable = true; } else { peer_pg.protocol.Enable = false; } if (tlvs->manifest->dcbx_pg->info[DCBX_HDR_EWE_OFFSET] & BIT6) { peer_pg.protocol.Willing = true; } else { peer_pg.protocol.Willing = false; } if (tlvs->manifest->dcbx_pg->info[DCBX_HDR_EWE_OFFSET] & BIT5) { peer_pg.protocol.Error = true; } else { peer_pg.protocol.Error = false; } peer_pg.protocol.dcbx_st = agent->rx.dcbx_st; peer_pg.protocol.TLVPresent = true; if (agent->rx.dupTlvs & DUP_DCBX_TLV_CTRL) { LLDPAD_INFO("** STORE: DUP CTRL TLV \n"); peer_pg.protocol.Error_Flag |= DUP_DCBX_TLV_CTRL; } if (agent->rx.dupTlvs & DUP_DCBX_TLV_PG) { LLDPAD_INFO("** STORE: DUP PG TLV \n"); peer_pg.protocol.Error_Flag |= DUP_DCBX_TLV_PG; } if (agent->rx.dcbx_st == dcbx_subtype2) { memset(used, false, sizeof(used)); for (j=0,k=0 ; k < MAX_BANDWIDTH_GROUPS; j++, k=k+2) { u8 tmpbyte = tlvs->manifest->dcbx_pg->info [DCBX2_PG_PGID_UP+j]; peer_pg.tx.up[k+1].pgid = tmpbyte & 0xf; peer_pg.rx.up[k+1].pgid = tmpbyte & 0xf; peer_pg.tx.up[k].pgid = (tmpbyte >> 4) & 0xf; peer_pg.rx.up[k].pgid = (tmpbyte >> 4) & 0xf; if (peer_pg.tx.up[k+1].pgid == LINK_STRICT_PGID) { peer_pg.tx.up[k+1].strict_priority = dcb_link; peer_pg.rx.up[k+1].strict_priority = dcb_link; } else { used[peer_pg.tx.up[k+1].pgid] = true; } if (peer_pg.tx.up[k].pgid == LINK_STRICT_PGID) { peer_pg.tx.up[k].strict_priority = dcb_link; peer_pg.rx.up[k].strict_priority = dcb_link; } else { used[peer_pg.tx.up[k].pgid] = true; } peer_pg.tx.up[k+1].bwgid = k + 1; peer_pg.rx.up[k+1].bwgid = k + 1; peer_pg.tx.up[k].bwgid = k; peer_pg.rx.up[k].bwgid = k; } /* assign LINK_STRICT_PGID's to an unused pgid value */ for (j = 0; j < MAX_BANDWIDTH_GROUPS; j++) if (!used[j]) break; for (k = 0; k < MAX_BANDWIDTH_GROUPS; k++) { if (peer_pg.tx.up[k].pgid == LINK_STRICT_PGID) { peer_pg.tx.up[k].pgid = (u8)j; peer_pg.rx.up[k].pgid = (u8)j; } } for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { peer_pg.tx.pg_percent[i] = tlvs->manifest->dcbx_pg->info [DCBX2_PG_PERCENT_OFFSET + i]; peer_pg.rx.pg_percent[i] = tlvs->manifest->dcbx_pg->info [DCBX2_PG_PERCENT_OFFSET + i]; } peer_pg.num_tcs = (u8)(tlvs->manifest->dcbx_pg->info [DCBX2_PG_NUM_TC_OFFSET]); } else { for (i = 0; i < MAX_BANDWIDTH_GROUPS; i++) { peer_pg.tx.pg_percent[i] = tlvs->manifest->dcbx_pg->info [DCBX1_PG_PERCENT_OFFSET + i]; peer_pg.rx.pg_percent[i] = tlvs->manifest->dcbx_pg->info [DCBX1_PG_PERCENT_OFFSET + i]; } for (i = 0; i < MAX_USER_PRIORITIES; i++) { u8 tmp_bwg_id = tlvs->manifest->dcbx_pg->info [DCBX1_PG_SETTINGS_OFFSET + 2*i +BYTE1_OFFSET]; tmp_bwg_id = tmp_bwg_id >> 5; peer_pg.tx.up[i].bwgid = tmp_bwg_id; peer_pg.rx.up[i].bwgid = tmp_bwg_id; u8 tmp_strict_prio = tlvs->manifest->dcbx_pg->info [DCBX1_PG_SETTINGS_OFFSET + 2*i +BYTE1_OFFSET]; tmp_strict_prio = tmp_strict_prio >> 3; tmp_strict_prio &= 0x3; peer_pg.tx.up[i].strict_priority = (dcb_strict_priority_type)tmp_strict_prio; peer_pg.rx.up[i].strict_priority = (dcb_strict_priority_type)tmp_strict_prio; peer_pg.tx.up[i].percent_of_pg_cap = tlvs->manifest->dcbx_pg->info [DCBX1_PG_SETTINGS_OFFSET + 2*i +BYTE2_OFFSET]; peer_pg.rx.up[i].percent_of_pg_cap = tlvs->manifest->dcbx_pg->info [DCBX1_PG_SETTINGS_OFFSET + 2*i +BYTE2_OFFSET]; tmp_bwg_id = tmp_strict_prio = 0; peer_pg.tx.up[i].pgid = i; peer_pg.rx.up[i].pgid = i; } } put_peer_pg(port->ifname, &peer_pg); return(true); } bool process_dcbx_pfc_tlv(struct port *port, struct lldp_agent *agent) { pfc_attribs peer_pfc; struct dcbx_tlvs *tlvs; int i = 0; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; if (agent->rx.dcbx_st == dcbx_subtype2) { if (tlvs->manifest->dcbx_pfc->length != DCBX2_PFC_LEN) { LLDPAD_DBG("process_dcbx2_pfc_tlv: ERROR - len\n"); return(false); } } else { if (tlvs->manifest->dcbx_pfc->length != DCBX1_PFC_LEN) { LLDPAD_DBG("process_dcbx1_pfc_tlv: ERROR - len\n"); return(false); } } memset(&peer_pfc, 0, sizeof(pfc_attribs)); peer_pfc.protocol.Advertise = true; peer_pfc.protocol.Oper_version = tlvs->manifest->dcbx_pfc->info [DCBX_HDR_OPER_VERSION_OFFSET]; peer_pfc.protocol.Max_version = tlvs->manifest->dcbx_pfc->info [DCBX_HDR_MAX_VERSION_OFFSET]; if (tlvs->manifest->dcbx_pfc->info[DCBX_HDR_EWE_OFFSET] & BIT7) { peer_pfc.protocol.Enable = true; } else { peer_pfc.protocol.Enable = false; } if (tlvs->manifest->dcbx_pfc->info[DCBX_HDR_EWE_OFFSET] & BIT6) { peer_pfc.protocol.Willing = true; } else { peer_pfc.protocol.Willing = false; } if (tlvs->manifest->dcbx_pfc->info[DCBX_HDR_EWE_OFFSET] & BIT5) { peer_pfc.protocol.Error = true; } else { peer_pfc.protocol.Error = false; } peer_pfc.protocol.dcbx_st = agent->rx.dcbx_st; peer_pfc.protocol.TLVPresent = true; if (agent->rx.dupTlvs & DUP_DCBX_TLV_CTRL) { LLDPAD_INFO("** STORE: DUP CTRL TLV \n"); peer_pfc.protocol.Error_Flag |= DUP_DCBX_TLV_CTRL; } if (agent->rx.dupTlvs & DUP_DCBX_TLV_PFC) { LLDPAD_INFO("** STORE: DUP PFC TLV \n"); peer_pfc.protocol.Error_Flag |= DUP_DCBX_TLV_PFC; } u8 temp = 0; for (i = 0; i < MAX_USER_PRIORITIES; i++) { temp = tlvs->manifest->dcbx_pfc->info[DCBX_PFC_MAP_OFFSET]; peer_pfc.admin[i] = (pfc_type)((temp >> i) & BIT0); } if (agent->rx.dcbx_st == dcbx_subtype2) { peer_pfc.num_tcs = tlvs->manifest->dcbx_pfc->info [DCBX2_PFC__NUM_TC_OFFSET]; } put_peer_pfc(port->ifname, &peer_pfc); return(true); } bool process_dcbx_app_tlv(struct port *port, struct lldp_agent *agent) { app_attribs peer_app; u32 i=0, len=0; u8 sub_type=0, sel_field=0, *pBuf=NULL; u16 peer_proto=0; u8 oui[DCB_OUI_LEN]=INIT_DCB_OUI; u8 peer_oui[DCB_OUI_LEN]; bool fcoe, fip, iscsi; struct dcbx_tlvs *tlvs; fcoe = fip = iscsi = false; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; len = tlvs->manifest->dcbx_app->length; if (agent->rx.dcbx_st == dcbx_subtype2) { if (len < DCBX2_APP_LEN) { LLDPAD_DBG("process_dcbx2_app_tlv: ERROR - len\n"); return(false); } } else { if (len < DCBX1_APP_LEN) { LLDPAD_DBG("process_dcbx1_app_tlv: ERROR - len\n"); return(false); } } memset(&peer_oui, 0, DCB_OUI_LEN); memset(&peer_app, 0, sizeof(app_attribs)); pBuf = tlvs->manifest->dcbx_app->info; peer_app.protocol.Oper_version = pBuf[DCBX_HDR_OPER_VERSION_OFFSET]; peer_app.protocol.Max_version = pBuf[DCBX_HDR_MAX_VERSION_OFFSET]; if (pBuf[DCBX_HDR_EWE_OFFSET] & BIT7) { peer_app.protocol.Enable = true; } else { peer_app.protocol.Enable = false; } if (pBuf[DCBX_HDR_EWE_OFFSET] & BIT6) { peer_app.protocol.Willing = true; } else { peer_app.protocol.Willing = false; } if (pBuf[DCBX_HDR_EWE_OFFSET] & BIT5) { peer_app.protocol.Error = true; } else { peer_app.protocol.Error = false; } peer_app.protocol.dcbx_st = agent->rx.dcbx_st; if (agent->rx.dupTlvs & DUP_DCBX_TLV_CTRL) { LLDPAD_INFO("** STORE: DUP CTRL TLV \n"); peer_app.protocol.Error_Flag |= DUP_DCBX_TLV_CTRL; } if (agent->rx.dupTlvs & DUP_DCBX_TLV_APP) { LLDPAD_INFO("** STORE: DUP APP TLV \n"); peer_app.protocol.Error_Flag |= DUP_DCBX_TLV_APP; } if (agent->rx.dcbx_st == dcbx_subtype2) { /* processs upper layer protocol IDs until we * match Selector Field, FCoE or FIP ID and OUI */ len -= DCBX2_APP_DATA_OFFSET; pBuf = &pBuf[DCBX2_APP_DATA_OFFSET]; while (len >= DCBX2_APP_SIZE) { sel_field = (u8)(pBuf[DCBX2_APP_BYTE1_OFFSET] & PROTO_ID_SF_TYPE); if (sel_field != PROTO_ID_L2_ETH_TYPE && sel_field != PROTO_ID_SOCK_NUM) { sel_field = 0; len -= DCBX2_APP_SIZE; pBuf += DCBX2_APP_SIZE; continue; } peer_proto = *((u16*)(&(pBuf[0]))); if ((peer_proto != PROTO_ID_FCOE) && (peer_proto != PROTO_ID_ISCSI) && (peer_proto != PROTO_ID_FIP)) { sel_field = 0; peer_proto = 0; len -= DCBX2_APP_SIZE; pBuf += DCBX2_APP_SIZE; continue; } peer_oui[0] = (u8)(pBuf[DCBX2_APP_BYTE1_OFFSET] & PROTO_ID_OUI_MASK); peer_oui[1] = pBuf[DCBX2_APP_LOW_OUI_OFFSET1]; peer_oui[2] = pBuf[DCBX2_APP_LOW_OUI_OFFSET2]; if (memcmp(peer_oui, oui, DCB_OUI_LEN) != 0) { sel_field = 0; peer_proto = 0; memset(&peer_oui, 0, DCB_OUI_LEN); len -= DCBX2_APP_SIZE; pBuf += DCBX2_APP_SIZE; continue; } if (sel_field == PROTO_ID_L2_ETH_TYPE && peer_proto == PROTO_ID_FCOE) { sub_type = APP_FCOE_STYPE; fcoe = true; } else if (sel_field == PROTO_ID_SOCK_NUM && peer_proto == PROTO_ID_ISCSI) { sub_type = APP_ISCSI_STYPE; iscsi = true; } else if (sel_field == PROTO_ID_L2_ETH_TYPE && peer_proto == PROTO_ID_FIP) { sub_type = APP_FIP_STYPE; fip = true; } peer_app.protocol.TLVPresent = true; peer_app.Length = APP_STYPE_LEN; memcpy (&(peer_app.AppData[0]), &(pBuf[DCBX2_APP_UP_MAP_OFFSET]), peer_app.Length); put_peer_app(port->ifname, sub_type, &peer_app); len -= DCBX2_APP_SIZE; pBuf += DCBX2_APP_SIZE; } /* NULL APP entry if not in TLV */ memset(&peer_app, 0, sizeof(app_attribs)); if (!fcoe) put_peer_app(port->ifname, APP_FCOE_STYPE, &peer_app); if (!iscsi) put_peer_app(port->ifname, APP_ISCSI_STYPE, &peer_app); if (!fip) put_peer_app(port->ifname, APP_FIP_STYPE, &peer_app); } else if (agent->rx.dcbx_st == dcbx_subtype1) { sub_type = pBuf[DCBX_HDR_SUB_TYPE_OFFSET]; len = tlvs->manifest->dcbx_app->length - sizeof(struct dcbx_tlv_header); peer_app.Length = len; if (DCB_MAX_TLV_LENGTH < len) { return false; } for (i = 0; i < len; i++) { peer_app.AppData[i] = pBuf[DCBX1_APP_DATA_OFFSET + i]; } peer_app.protocol.TLVPresent = true; put_peer_app(port->ifname, sub_type, &peer_app); return(true); } else { return false; } return true; } bool process_dcbx_llink_tlv(struct port *port, struct lldp_agent *agent) { llink_attribs peer_llk; struct dcbx_tlvs *tlvs; tlvs = dcbx_data(port->ifname); if (agent == NULL) return false; if (tlvs->manifest->dcbx_llink->length != DCBX_LLINK_LEN) { LLDPAD_DBG("process_dcbx_llink_tlv: ERROR - len\n"); return(false); } memset(&peer_llk, 0, sizeof(llink_attribs)); peer_llk.protocol.Advertise = true; peer_llk.protocol.Oper_version = tlvs->manifest->dcbx_llink->info [DCBX_HDR_OPER_VERSION_OFFSET]; peer_llk.protocol.Max_version = tlvs->manifest->dcbx_llink->info [DCBX_HDR_MAX_VERSION_OFFSET]; if (tlvs->manifest->dcbx_llink->info[DCBX_HDR_EWE_OFFSET] & BIT7) { peer_llk.protocol.Enable = true; } else { peer_llk.protocol.Enable = false; } if (tlvs->manifest->dcbx_llink->info[DCBX_HDR_EWE_OFFSET] & BIT6) { peer_llk.protocol.Willing = true; } else { peer_llk.protocol.Willing = false; } if (tlvs->manifest->dcbx_llink->info[DCBX_HDR_EWE_OFFSET] & BIT5) { peer_llk.protocol.Error = true; } else { peer_llk.protocol.Error = false; } peer_llk.protocol.dcbx_st = agent->rx.dcbx_st; peer_llk.protocol.TLVPresent = true; if (agent->rx.dupTlvs & DUP_DCBX_TLV_CTRL) { LLDPAD_INFO("** STORE: DUP CTRL TLV \n"); peer_llk.protocol.Error_Flag |= DUP_DCBX_TLV_CTRL; } if (agent->rx.dupTlvs & DUP_DCBX_TLV_LLINK) { LLDPAD_INFO("** STORE: DUP LLINK TLV \n"); peer_llk.protocol.Error_Flag |= DUP_DCBX_TLV_LLINK; } peer_llk.llink.llink_status = !!((tlvs->manifest->dcbx_llink->info [DCBX_LLINK_STATUS_OFFSET]) & BIT7); put_peer_llink(port->ifname, LLINK_FCOE_STYPE, &peer_llk); return(true); } lldpad-0.9.46/weak_readline.c000066400000000000000000000053611215142747300160230ustar00rootroot00000000000000/* * LLDP Agent Daemon (LLDPAD) Software * Copyright(c) 2012 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * * Contact Information: * open-lldp Mailing List */ #define _GNU_SOURCE #include #include #include #include #include static int inited; static char *(*readline_p)(const char *); static void (*using_history_p)(void); static void (*stifle_history_p)(int); static void (*add_history_p)(const char *); static void weak_readline_init(void) { void *hist_handle; void *rl_handle; inited = 1; hist_handle = dlopen("libhistory.so", RTLD_LAZY | RTLD_GLOBAL); if (!hist_handle) return; rl_handle = dlopen("libreadline.so", RTLD_LAZY | RTLD_GLOBAL); if (!rl_handle) goto out; using_history_p = dlsym(hist_handle, "using_history"); stifle_history_p = dlsym(hist_handle, "stifle_history"); add_history_p = dlsym(hist_handle, "add_history"); readline_p = dlsym(rl_handle, "readline"); if (readline_p && using_history_p && stifle_history_p && add_history_p) return; dlclose(rl_handle); rl_handle = NULL; out: dlclose(hist_handle); hist_handle = NULL; readline_p = NULL; using_history_p = NULL; stifle_history_p = NULL; add_history_p = NULL; } void using_history(void) { if (!inited) weak_readline_init(); if (using_history_p) using_history_p(); } void stifle_history(int max) { if (!inited) weak_readline_init(); if (stifle_history_p) stifle_history_p(max); } static char *do_readline(const char *prompt) { size_t size = 0; ssize_t rc; char *line = NULL; fputs(prompt, stdout); fflush(stdout); rc = getline(&line, &size, stdin); if (rc <= 0) return NULL; if (line[rc - 1] == '\n') line[rc - 1] = 0; return line; } char *readline(const char *prompt) { if (!inited) weak_readline_init(); if (readline_p) return readline_p(prompt); return do_readline(prompt); } void add_history(const char *cmd) { if (!inited) weak_readline_init(); if (add_history_p) add_history_p(cmd); } lldpad-0.9.46/ylwrap000077500000000000000000000140741215142747300143320ustar00rootroot00000000000000#! /bin/sh # ylwrap - wrapper for lex/yacc invocations. scriptversion=2007-11-22.22 # Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005, # 2007 Free Software Foundation, Inc. # # Written by Tom Tromey . # # 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, 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. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . case "$1" in '') echo "$0: No files given. Try \`$0 --help' for more information." 1>&2 exit 1 ;; --basedir) basedir=$2 shift 2 ;; -h|--h*) cat <<\EOF Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... Wrapper for lex/yacc invocations, renaming files as desired. INPUT is the input file OUTPUT is one file PROG generates DESIRED is the file we actually want instead of OUTPUT PROGRAM is program to run ARGS are passed to PROG Any number of OUTPUT,DESIRED pairs may be used. Report bugs to . EOF exit $? ;; -v|--v*) echo "ylwrap $scriptversion" exit $? ;; esac # The input. input="$1" shift case "$input" in [\\/]* | ?:[\\/]*) # Absolute path; do nothing. ;; *) # Relative path. Make it absolute. input="`pwd`/$input" ;; esac pairlist= while test "$#" -ne 0; do if test "$1" = "--"; then shift break fi pairlist="$pairlist $1" shift done # The program to run. prog="$1" shift # Make any relative path in $prog absolute. case "$prog" in [\\/]* | ?:[\\/]*) ;; *[\\/]*) prog="`pwd`/$prog" ;; esac # FIXME: add hostname here for parallel makes that run commands on # other machines. But that might take us over the 14-char limit. dirname=ylwrap$$ trap "cd '`pwd`'; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15 mkdir $dirname || exit 1 cd $dirname case $# in 0) "$prog" "$input" ;; *) "$prog" "$@" "$input" ;; esac ret=$? if test $ret -eq 0; then set X $pairlist shift first=yes # Since DOS filename conventions don't allow two dots, # the DOS version of Bison writes out y_tab.c instead of y.tab.c # and y_tab.h instead of y.tab.h. Test to see if this is the case. y_tab_nodot="no" if test -f y_tab.c || test -f y_tab.h; then y_tab_nodot="yes" fi # The directory holding the input. input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'` # Quote $INPUT_DIR so we can use it in a regexp. # FIXME: really we should care about more than `.' and `\'. input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'` while test "$#" -ne 0; do from="$1" # Handle y_tab.c and y_tab.h output by DOS if test $y_tab_nodot = "yes"; then if test $from = "y.tab.c"; then from="y_tab.c" else if test $from = "y.tab.h"; then from="y_tab.h" fi fi fi if test -f "$from"; then # If $2 is an absolute path name, then just use that, # otherwise prepend `../'. case "$2" in [\\/]* | ?:[\\/]*) target="$2";; *) target="../$2";; esac # We do not want to overwrite a header file if it hasn't # changed. This avoid useless recompilations. However the # parser itself (the first file) should always be updated, # because it is the destination of the .y.c rule in the # Makefile. Divert the output of all other files to a temporary # file so we can compare them to existing versions. if test $first = no; then realtarget="$target" target="tmp-`echo $target | sed s/.*[\\/]//g`" fi # Edit out `#line' or `#' directives. # # We don't want the resulting debug information to point at # an absolute srcdir; it is better for it to just mention the # .y file with no path. # # We want to use the real output file name, not yy.lex.c for # instance. # # We want the include guards to be adjusted too. FROM=`echo "$from" | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` TARGET=`echo "$2" | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \ -e "s,$FROM,$TARGET," "$from" >"$target" || ret=$? # Check whether header files must be updated. if test $first = no; then if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then echo "$2" is unchanged rm -f "$target" else echo updating "$2" mv -f "$target" "$realtarget" fi fi else # A missing file is only an error for the first file. This # is a blatant hack to let us support using "yacc -d". If -d # is not specified, we don't want an error when the header # file is "missing". if test $first = yes; then ret=1 fi fi shift shift first=no done else ret=$? fi # Remove the directory. cd .. rm -rf $dirname exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: