From 3880abcee451c8c481c10d70e30ad5490f31b5d4 Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 09 2020 17:23:18 +0000 Subject: iproute-5.9.0 base --- diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..901467b --- /dev/null +++ b/.clang-format @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 4. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + #AfterExternBlock: false # Unknown to clang-format-5.0 + BeforeCatch: false + BeforeElse: false + IndentBraces: false + #SplitEmptyFunction: true # Unknown to clang-format-4.0 + #SplitEmptyRecord: true # Unknown to clang-format-4.0 + #SplitEmptyNamespace: true # Unknown to clang-format-4.0 +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +#CompactNamespaces: false # Unknown to clang-format-4.0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +#FixNamespaceComments: false # Unknown to clang-format-4.0 + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | sort | uniq +ForEachMacros: + - 'list_for_each_entry' + - 'list_for_each_entry_safe' + - 'mnl_attr_for_each_nested' + - 'hlist_for_each' + - 'hlist_for_each_safe' + - 'hlist_for_each_entry' + +#IncludeBlocks: Preserve # Unknown to clang-format-5.0 +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +#IndentPPDirectives: None # Unknown to clang-format-5.0 +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +#SortUsingDeclarations: false # Unknown to clang-format-4.0 +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 +#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e5234a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# locally generated +Config +static-syms.h +config.* +*.o +*.a +*.so +*~ +\#*# + +# cscope +cscope.* +ncscope.* +tags +TAGS + +# git files that we don't want to ignore even it they are dot-files +!.gitignore +!.mailmap + +# for patch generation +*.diff +*.patch +*.orig +*.rej + +# for quilt +.pc +patches +series + +# for gdb +.gdbinit +.gdb_history +*.gdb + +# tests +testsuite/results +testsuite/iproute2/iproute2-this +testsuite/tools/generate_nlmsg +testsuite/tests/ip/link/dev_wo_vf_rate.nl diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..fd40c91 --- /dev/null +++ b/.mailmap @@ -0,0 +1,22 @@ +# +# This list is used by git-shortlog to fix a few botched name translations +# in the git archive, either because the author's full name was messed up +# and/or not always written the same way, making contributions from the +# same person appearing not to be so or badly displayed. +# +# Format +# Full name +Steve Wise +Steve Wise + +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger +Stephen Hemminger + +David Ahern diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU 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. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5b04041 --- /dev/null +++ b/Makefile @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0 +# Top level Makefile for iproute2 + +ifeq ("$(origin V)", "command line") +VERBOSE = $(V) +endif +ifndef VERBOSE +VERBOSE = 0 +endif + +ifeq ($(VERBOSE),0) +MAKEFLAGS += --no-print-directory +endif + +PREFIX?=/usr +LIBDIR?=$(PREFIX)/lib +SBINDIR?=/sbin +CONFDIR?=/etc/iproute2 +NETNS_RUN_DIR?=/var/run/netns +NETNS_ETC_DIR?=/etc/netns +DATADIR?=$(PREFIX)/share +HDRDIR?=$(PREFIX)/include/iproute2 +DOCDIR?=$(DATADIR)/doc/iproute2 +MANDIR?=$(DATADIR)/man +ARPDDIR?=/var/lib/arpd +KERNEL_INCLUDE?=/usr/include +BASH_COMPDIR?=$(DATADIR)/bash-completion/completions + +# Path to db_185.h include +DBM_INCLUDE:=$(DESTDIR)/usr/include + +SHARED_LIBS = y + +DEFINES= -DRESOLVE_HOSTNAMES -DLIBDIR=\"$(LIBDIR)\" +ifneq ($(SHARED_LIBS),y) +DEFINES+= -DNO_SHARED_LIBS +endif + +DEFINES+=-DCONFDIR=\"$(CONFDIR)\" \ + -DNETNS_RUN_DIR=\"$(NETNS_RUN_DIR)\" \ + -DNETNS_ETC_DIR=\"$(NETNS_ETC_DIR)\" + +#options for mpls +ADDLIB+=mpls_ntop.o mpls_pton.o + +CC := gcc +HOSTCC ?= $(CC) +DEFINES += -D_GNU_SOURCE +# Turn on transparent support for LFS +DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE +CCOPTS = -O2 -pipe +WFLAGS := -Wall -Wstrict-prototypes -Wmissing-prototypes +WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2 + +CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS) +YACCFLAGS = -d -t -v + +SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man + +LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a +LDLIBS += $(LIBNETLINK) + +all: config.mk + @set -e; \ + for i in $(SUBDIRS); \ + do echo; echo $$i; $(MAKE) -C $$i; done + +.PHONY: clean clobber distclean check cscope version + +help: + @echo "Make Targets:" + @echo " all - build binaries" + @echo " clean - remove products of build" + @echo " distclean - remove configuration and build" + @echo " install - install binaries on local machine" + @echo " check - run tests" + @echo " cscope - build cscope database" + @echo " version - update version" + @echo "" + @echo "Make Arguments:" + @echo " V=[0|1] - set build verbosity level" + +config.mk: + sh configure $(KERNEL_INCLUDE) + +install: all + install -m 0755 -d $(DESTDIR)$(SBINDIR) + install -m 0755 -d $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(ARPDDIR) + install -m 0755 -d $(DESTDIR)$(HDRDIR) + @for i in $(SUBDIRS); do $(MAKE) -C $$i install; done + install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(BASH_COMPDIR) + install -m 0644 bash-completion/tc $(DESTDIR)$(BASH_COMPDIR) + install -m 0644 bash-completion/devlink $(DESTDIR)$(BASH_COMPDIR) + install -m 0644 include/bpf_elf.h $(DESTDIR)$(HDRDIR) + +version: + echo "static const char version[] = \""`git describe --tags --long`"\";" \ + > include/version.h + +clean: + @for i in $(SUBDIRS) testsuite; \ + do $(MAKE) -C $$i clean; done + +clobber: + touch config.mk + $(MAKE) clean + rm -f config.mk cscope.* + +distclean: clobber + +check: all + $(MAKE) -C testsuite + $(MAKE) -C testsuite alltests + @if command -v man >/dev/null 2>&1; then \ + echo "Checking manpages for syntax errors..."; \ + $(MAKE) -C man check; \ + else \ + echo "man not installed, skipping checks for syntax errors."; \ + fi + +cscope: + cscope -b -q -R -Iinclude -sip -slib -smisc -snetem -stc + +.EXPORT_ALL_VARIABLES: diff --git a/README b/README new file mode 100644 index 0000000..bc82187 --- /dev/null +++ b/README @@ -0,0 +1,47 @@ +This is a set of utilities for Linux networking. + +Information: + https://wiki.linuxfoundation.org/networking/iproute2 + +Download: + http://www.kernel.org/pub/linux/utils/net/iproute2/ + +Stable version repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2.git + +Development repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git + +How to compile this. +-------------------- +1. libdbm + +arpd needs to have the berkeleydb development libraries. For Debian +users this is the package with a name like libdbX.X-dev. +DBM_INCLUDE points to the directory with db_185.h which +is the include file used by arpd to get to the old format Berkeley +database routines. Often this is in the db-devel package. + +2. make + +The makefile will automatically build a config.mk file which +contains definitions of libraries that may or may not be available +on the system such as: ATM, ELF, MNL, and SELINUX. + +3. To make documentation, cd to doc/ directory , then + look at start of Makefile and set correct values for + PAGESIZE=a4 , ie: a4 , letter ... (string) + PAGESPERPAGE=2 , ie: 1 , 2 ... (numeric) + and make there. It assumes, that latex, dvips and psnup + are in your path. + +4. This package includes matching sanitized kernel headers because + the build environment may not have up to date versions. See Makefile + if you have special requirements and need to point at different + kernel include files. + +Stephen Hemminger +stephen@networkplumber.org + +Alexey Kuznetsov +kuznet@ms2.inr.ac.ru diff --git a/README.devel b/README.devel new file mode 100644 index 0000000..60c9546 --- /dev/null +++ b/README.devel @@ -0,0 +1,18 @@ +Iproute2 development is closely tied to Linux kernel networking +development. Most new features require a kernel and a utility component. + +Please submit both to the Linux networking mailing list + + +The current source for the stable version is in the git repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2.git + +The development git repository is available at the following address: + git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git + +The stable repository contains the source corresponding to the +current code in the Linux networking tree (net), which in turn is +aligned on the mainline Linux kernel (ie follows Linus). +The iproute2-next repository tracks the code intended for the next +release; it corresponds with networking development tree (net-next) +in the kernel. diff --git a/bash-completion/devlink b/bash-completion/devlink new file mode 100644 index 0000000..f710c88 --- /dev/null +++ b/bash-completion/devlink @@ -0,0 +1,951 @@ +# bash completion for devlink(8) -*- shell-script -*- + +# Get all the optional commands for devlink +_devlink_get_optional_commands() +{ + local object=$1; shift + + local filter_options="" + local options="$(devlink $object help 2>&1 \ + | command sed -n -e "s/^.*devlink $object //p" \ + | cut -d " " -f 1)" + + # Remove duplicate options from "devlink $OBJECT help" command + local opt + for opt in $options; do + if [[ $filter_options =~ $opt ]]; then + continue + else + filter_options="$filter_options $opt" + fi + done + + echo $filter_options +} + +# Complete based on given word, for when an argument or an option name has +# but a few possible arguments. +_devlink_direct_complete() +{ + local dev port region value + + case $1 in + dev) + value=$(devlink dev show 2>/dev/null) + ;; + param_name) + dev=${words[4]} + value=$(devlink -j dev param show 2>/dev/null \ + | jq ".param[\"$dev\"][].name") + ;; + port) + value=$(devlink -j port show 2>/dev/null \ + | jq '.port as $ports | $ports | keys[] as $key + | ($ports[$key].netdev // $key)') + ;; + region) + value=$(devlink -j region show 2>/dev/null \ + | jq '.regions' | jq 'keys[]') + ;; + snapshot) + region=${words[3]} + value=$(devlink -j region show 2>/dev/null \ + | jq ".regions[\"$region\"].snapshot[]") + ;; + trap) + dev=${words[3]} + value=$(devlink -j trap show 2>/dev/null \ + | jq ".trap[\"$dev\"][].name") + ;; + trap_group) + dev=${words[4]} + value=$(devlink -j trap group show 2>/dev/null \ + | jq ".trap_group[\"$dev\"][].name") + ;; + trap_policer) + dev=${words[4]} + value=$(devlink -j trap policer show 2>/dev/null \ + | jq ".trap_policer[\"$dev\"][].policer") + ;; + health_dev) + value=$(devlink -j health show 2>/dev/null | jq '.health' \ + | jq 'keys[]') + ;; + reporter) + dev=${words[cword - 2]} + value=$(devlink -j health show 2>/dev/null \ + | jq ".health[\"$dev\"][].reporter") + ;; + pool) + dev=$pprev + value=$(devlink -j sb pool show 2>/dev/null \ + | jq ".pool[\"$dev\"][].pool") + ;; + port_pool) + port=${words[5]} + value=$(devlink -j sb port pool show 2>/dev/null \ + | jq ".port_pool[\"$port\"][].pool") + ;; + tc) + port=$pprev + value=$(devlink -j sb tc bind show 2>/dev/null \ + | jq ".tc_bind[\"$port\"][].tc") + ;; + esac + + COMPREPLY+=( $( compgen -W "$value" -- "$cur" ) ) + # Remove colon containing prefix from COMPREPLY items in order to avoid + # wordbreaks with colon. + __ltrim_colon_completions "$cur" +} + +# Completion for devlink dev eswitch set +_devlink_dev_eswitch_set() +{ + local -A settings=( + [mode]=notseen + [inline-mode]=notseen + [encap-mode]=notseen + ) + + if [[ $cword -eq 5 ]]; then + COMPREPLY=( $( compgen -W "mode inline-mode encap-mode" -- "$cur" ) ) + fi + + # Mark seen settings + local word + for word in "${words[@]:5:${#words[@]}-1}"; do + if [[ -n $word ]]; then + if [[ "${settings[$word]}" ]]; then + settings[$word]=seen + fi + fi + done + + case $prev in + mode) + COMPREPLY=( $( compgen -W "legacy switchdev" -- "$cur" ) ) + return + ;; + inline-mode) + COMPREPLY=( $( compgen -W "none link network transport" -- \ + "$cur" ) ) + return + ;; + encap-mode) + COMPREPLY=( $( compgen -W "none basic" -- "$cur" ) ) + return + ;; + esac + + local -a comp_words=() + + # Add settings not seen to completions + local setting + for setting in "${!settings[@]}"; do + if [ "${settings[$setting]}" = notseen ]; then + comp_words+=( "$setting" ) + fi + done + + COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) ) +} + +# Completion for devlink dev eswitch +_devlink_dev_eswitch() +{ + case "$cword" in + 3) + COMPREPLY=( $( compgen -W "show set" -- "$cur" ) ) + return + ;; + 4) + _devlink_direct_complete "dev" + return + ;; + esac + + case "${words[3]}" in + set) + _devlink_dev_eswitch_set + return + ;; + show) + return + ;; + esac +} + +# Completion for devlink dev param set +_devlink_dev_param_set() +{ + case $cword in + 7) + COMPREPLY=( $( compgen -W "value" -- "$cur" ) ) + return + ;; + 8) + # String argument + return + ;; + 9) + COMPREPLY=( $( compgen -W "cmode" -- "$cur" ) ) + return + ;; + 10) + COMPREPLY=( $( compgen -W "runtime driverinit permanent" -- \ + "$cur" ) ) + return + ;; + esac +} + +# Completion for devlink dev param +_devlink_dev_param() +{ + case "$cword" in + 3) + COMPREPLY=( $( compgen -W "show set" -- "$cur" ) ) + return + ;; + 4) + _devlink_direct_complete "dev" + return + ;; + 5) + COMPREPLY=( $( compgen -W "name" -- "$cur" ) ) + return + ;; + 6) + _devlink_direct_complete "param_name" + return + ;; + esac + + if [[ "${words[3]}" == "set" ]]; then + _devlink_dev_param_set + fi +} + +# Completion for devlink dev reload +_devlink_dev_reload() +{ + case "$cword" in + 4) + COMPREPLY=( $( compgen -W "netns" -- "$cur" ) ) + return + ;; + 5) + local nslist=$( ip netns list 2>/dev/null ) + COMPREPLY=( $( compgen -W "$nslist" -- "$cur" ) ) + return + ;; + esac +} + +# Completion for devlink dev flash +_devlink_dev_flash() +{ + case "$cword" in + 4) + COMPREPLY=( $( compgen -W "file" -- "$cur" ) ) + return + ;; + 5) + _filedir + return + ;; + 6) + COMPREPLY=( $( compgen -W "component" -- "$cur" ) ) + return + ;; + esac +} + +# Completion for devlink dev +_devlink_dev() +{ + case $command in + show|reload|info|flash) + if [[ $cword -le 3 ]]; then + _devlink_direct_complete "dev" + elif [[ $command == "reload" || $command == "flash" ]];then + _devlink_dev_$command + fi + return + ;; + eswitch|param) + _devlink_dev_$command + return + ;; + esac +} + +# Completion for devlink port set +_devlink_port_set() +{ + case "$cword" in + 3) + _devlink_direct_complete "port" + return + ;; + 4) + COMPREPLY=( $( compgen -W "type" -- "$cur" ) ) + return + ;; + 5) + COMPREPLY=( $( compgen -W "eth ib auto" -- "$cur" ) ) + return + ;; + esac +} + +# Completion for devlink port split +_devlink_port_split() +{ + case "$cword" in + 3) + _devlink_direct_complete "port" + return + ;; + 4) + COMPREPLY=( $( compgen -W "count" -- "$cur" ) ) + return + ;; + 5) + # Integer argument + return + ;; + esac +} + +# Completion for devlink port +_devlink_port() +{ + case $command in + set) + _devlink_port_set + return + ;; + split) + _devlink_port_split + return + ;; + show|unsplit) + if [[ $cword -eq 3 ]]; then + _devlink_direct_complete "port" + fi + return + ;; + esac +} + +# Completion for devlink dpipe +_devlink_dpipe() +{ + local options="$(devlink dpipe help 2>&1 \ + | command sed -e '/OBJECT-LIST := /!d' \ + -e 's/.*{ //' -e 's/}.*//' -e 's/|//g' )" + + if [[ $cword -eq 2 ]]; then + COMPREPLY+=( $( compgen -W "$options" -- "$cur" ) ) + fi +} + +# Completion for devlink monitor +_devlink_monitor() +{ + local options="$(devlink monitor help 2>&1 \ + | command sed -e '/OBJECT-LIST := /!d' \ + -e 's/.*{ //' -e 's/}.*//' -e 's/|//g' )" + + if [[ $cword -eq 2 ]]; then + COMPREPLY+=( $( compgen -W "all $options" -- "$cur" ) ) + fi +} + +# Completion for the rest of devlink sb $command +_devlink_sb_command_options() +{ + local subcmd + + case $command in + pool) + subcmd=${words[3]} + if [[ $cword -eq 5 ]]; then + COMPREPLY=( $( compgen -W "pool" -- "$cur" ) ) + fi + if [[ $subcmd == "set" ]]; then + case $cword in + 7) + COMPREPLY+=( $( compgen -W "size" -- "$cur" ) ) + ;; + 9) + COMPREPLY+=( $( compgen -W "thtype" -- "$cur" ) ) + ;; + esac + fi + ;; + port) + subcmd=${words[4]} + if [[ $cword -eq 6 ]]; then + COMPREPLY+=( $( compgen -W "pool" -- "$cur" ) ) + fi + if [[ $subcmd == "set" ]]; then + case $cword in + 8) + COMPREPLY+=( $( compgen -W "th" -- "$cur" ) ) + ;; + esac + fi + ;; + tc) + subcmd=${words[4]} + case $cword in + 6) + COMPREPLY+=( $( compgen -W "tc" -- "$cur" ) ) + ;; + 8) + COMPREPLY+=( $( compgen -W "type" -- "$cur" ) ) + ;; + esac + if [[ $subcmd == "set" ]]; then + case $cword in + 10) + COMPREPLY+=( $( compgen -W "pool" -- "$cur" ) ) + ;; + 12) + COMPREPLY+=( $( compgen -W "th" -- "$cur" ) ) + ;; + esac + fi + ;; + esac +} + +# Completion for devlink sb +_devlink_sb() +{ + case $prev in + bind) + COMPREPLY=( $( compgen -W "set show" -- "$cur" ) ) + ;; + occupancy) + COMPREPLY=( $( compgen -W "show snapshot clearmax" -- "$cur" ) ) + ;; + pool) + if [[ $cword -eq 3 || $cword -eq 4 ]]; then + COMPREPLY=( $( compgen -W "set show" -- "$cur" ) ) + elif [[ $command == "port" || $command == "tc" ]]; then + _devlink_direct_complete "port_pool" + else + _devlink_direct_complete "pool" + fi + ;; + port) + if [[ $cword -eq 3 ]]; then + COMPREPLY=( $( compgen -W "pool" -- "$cur" ) ) + fi + ;; + show|set|snapshot|clearmax) + case $command in + show|pool|occupancy) + _devlink_direct_complete "dev" + if [[ $command == "occupancy" && $prev == "show" ]];then + _devlink_direct_complete "port" + fi + ;; + port|tc) + _devlink_direct_complete "port" + ;; + esac + ;; + size) + # Integer argument + ;; + thtype) + COMPREPLY=( $( compgen -W "static dynamic" -- "$cur" ) ) + ;; + th) + # Integer argument + ;; + tc) + if [[ $cword -eq 3 ]]; then + COMPREPLY=( $( compgen -W "bind" -- "$cur" ) ) + else + _devlink_direct_complete "tc" + fi + ;; + type) + COMPREPLY=( $( compgen -W "ingress egress" -- "$cur" ) ) + ;; + esac + + _devlink_sb_command_options + return +} + +# Completion for devlink resource set path argument +_devlink_resource_path() +{ + local path parents parent all_path + local dev=${words[3]} + local -a path + + local all_path=$( + devlink resource show $dev \ + | sed -E '# Of resource lines, keep only the name itself. + s/name ([^ ]*) .*/\1/ + # Drop headers. + /:$/d + # First layer is not aligned enough, align it. + s/^/ / + # Use slashes as unary code for resource depth. + s, ,/,g + # Separate tally count from resource name. + s,/*,&\t,' \ + | while read d name; do + while ((${#path[@]} > ${#d})); do + unset path[$((${#path[@]} - 1))] + done + path[$((${#d} - 1))]=$name + echo ${path[@]} + done \ + | sed '# Convert paths to slash-separated + s,^,/,;s, ,/,g;s,$,/,' + ) + COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W "$all_path" -- "$cur" ) ) +} + +# Completion for devlink resource set +_devlink_resource_set() +{ + case "$cword" in + 3) + _devlink_direct_complete "dev" + return + ;; + 4) + COMPREPLY=( $( compgen -W "path" -- "$cur" ) ) + return + ;; + 5) + _devlink_resource_path + return + ;; + 6) + COMPREPLY=( $( compgen -W "size" -- "$cur" ) ) + return + ;; + 7) + # Integer argument + return + ;; + esac +} + +# Completion for devlink resource +_devlink_resource() +{ + case $command in + show) + if [[ $cword -eq 3 ]]; then + _devlink_direct_complete "dev" + fi + return + ;; + set) + _devlink_resource_set + return + ;; + esac +} + +# Completion for devlink region read +_devlink_region_read() +{ + case "$cword" in + 6) + COMPREPLY=( $( compgen -W "address" -- "$cur" ) ) + return + ;; + 7) + # Address argument, for example: 0x10 + return + ;; + 8) + COMPREPLY=( $( compgen -W "length" -- "$cur" ) ) + return + ;; + 9) + # Integer argument + return + ;; + esac +} + +# Completion for devlink region +_devlink_region() +{ + if [[ $cword -eq 3 && $command != "help" ]]; then + _devlink_direct_complete "region" + fi + + case $command in + show) + return + ;; + del|dump|read) + case "$cword" in + 4) + COMPREPLY=( $( compgen -W "snapshot" -- "$cur" ) ) + ;; + 5) + _devlink_direct_complete "snapshot" + ;; + esac + + if [[ $command == "read" ]]; then + _devlink_region_read + fi + return + ;; + esac +} + +# Completion reporter for devlink health +_devlink_health_reporter() +{ + local i=$1; shift + + case $cword in + $((3 + $i))) + _devlink_direct_complete "health_dev" + ;; + $((4 + $i))) + COMPREPLY=( $( compgen -W "reporter" -- "$cur" ) ) + ;; + $((5 + $i))) + _devlink_direct_complete "reporter" + ;; + esac +} + +# Completion for devlink health +_devlink_health() +{ + case $command in + show|recover|diagnose|set) + _devlink_health_reporter 0 + if [[ $command == "set" ]]; then + case $cword in + 6) + COMPREPLY=( $( compgen -W "grace_period auto_recover" \ + -- "$cur" ) ) + ;; + 7) + case $prev in + grace_period) + # Integer argument- msec + ;; + auto_recover) + COMPREPLY=( $( compgen -W "true false" -- \ + "$cur" ) ) + ;; + esac + esac + fi + return + ;; + dump) + if [[ $cword -eq 3 ]]; then + COMPREPLY=( $( compgen -W "show clear" -- "$cur" ) ) + fi + + _devlink_health_reporter 1 + return + ;; + esac +} + +# Completion for action in devlink trap set +_devlink_trap_set_action() +{ + local i=$1; shift + + case $cword in + $((6 + $i))) + COMPREPLY=( $( compgen -W "action" -- "$cur" ) ) + ;; + $((7 + $i))) + COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) ) + ;; + esac +} + +# Completion for devlink trap group set +_devlink_trap_group_set() +{ + local -A settings=( + [action]=notseen + [policer]=notseen + [nopolicer]=notseen + ) + + if [[ $cword -eq 7 ]]; then + COMPREPLY=( $( compgen -W "action policer nopolicer" -- "$cur" ) ) + fi + + # Mark seen settings + local word + for word in "${words[@]:7:${#words[@]}-1}"; do + if [[ -n $word ]]; then + if [[ "${settings[$word]}" ]]; then + settings[$word]=seen + fi + fi + done + + case $prev in + action) + COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) ) + return + ;; + policer) + _devlink_direct_complete "trap_policer" + return + ;; + esac + + local -a comp_words=() + + # Add settings not seen to completions + local setting + for setting in "${!settings[@]}"; do + if [ "${settings[$setting]}" = notseen ]; then + comp_words+=( "$setting" ) + fi + done + + COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) ) +} + +# Completion for devlink trap group +_devlink_trap_group() +{ + case $cword in + 3) + COMPREPLY=( $( compgen -W "set show" -- "$cur" ) ) + return + ;; + 4) + _devlink_direct_complete "dev" + return + ;; + 5) + COMPREPLY=( $( compgen -W "group" -- "$cur" ) ) + return + ;; + 6) + _devlink_direct_complete "trap_group" + return + ;; + esac + + if [[ ${words[3]} == "set" ]]; then + _devlink_trap_group_set + fi +} + +# Completion for devlink trap policer set +_devlink_trap_policer_set() +{ + local -A settings=( + [rate]=notseen + [burst]=notseen + ) + + if [[ $cword -eq 7 ]]; then + COMPREPLY=( $( compgen -W "rate burst" -- "$cur" ) ) + fi + + # Mark seen settings + local word + for word in "${words[@]:7:${#words[@]}-1}"; do + if [[ -n $word ]]; then + if [[ "${settings[$word]}" ]]; then + settings[$word]=seen + fi + fi + done + + case $prev in + rate) + # Integer argument + return + ;; + burst) + # Integer argument + return + ;; + esac + + local -a comp_words=() + + # Add settings not seen to completions + local setting + for setting in "${!settings[@]}"; do + if [ "${settings[$setting]}" = notseen ]; then + comp_words+=( "$setting" ) + fi + done + + COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) ) +} + +# Completion for devlink trap policer +_devlink_trap_policer() +{ + case $cword in + 3) + COMPREPLY=( $( compgen -W "set show" -- "$cur" ) ) + return + ;; + 4) + _devlink_direct_complete "dev" + return + ;; + 5) + COMPREPLY=( $( compgen -W "policer" -- "$cur" ) ) + return + ;; + 6) + _devlink_direct_complete "trap_policer" + return + ;; + esac + + if [[ ${words[3]} == "set" ]]; then + _devlink_trap_policer_set + fi +} + +# Completion for devlink trap +_devlink_trap() +{ + case $command in + show|set) + case $cword in + 3) + _devlink_direct_complete "dev" + ;; + 4) + COMPREPLY=( $( compgen -W "trap" -- "$cur" ) ) + ;; + 5) + _devlink_direct_complete "trap" + ;; + esac + + if [[ $command == "set" ]]; then + _devlink_trap_set_action 0 + fi + return + ;; + group) + _devlink_trap_$command + return + ;; + policer) + _devlink_trap_$command + return + ;; + esac +} + +# Complete any devlink command +_devlink() +{ + local cur prev words cword + local opt='--Version --no-nice-names --json --pretty --verbose \ + --statistics --force --Netns --batch' + local objects="$(devlink help 2>&1 | command sed -e '/OBJECT := /!d' \ + -e 's/.*{//' -e 's/}.*//' -e \ 's/|//g' )" + + _init_completion || return + # Gets the word-to-complete without considering the colon as word breaks + _get_comp_words_by_ref -n : cur prev words cword + + if [[ $cword -eq 1 ]]; then + case $cur in + -*) + COMPREPLY=( $( compgen -W "$opt" -- "$cur" ) ) + return 0 + ;; + *) + COMPREPLY=( $( compgen -W "$objects" -- "$cur" ) ) + return 0 + ;; + esac + fi + + # Deal with options + if [[ $prev == -* ]]; then + case $prev in + -V|--Version) + return 0 + ;; + -b|--batch) + _filedir + return 0 + ;; + --force) + COMPREPLY=( $( compgen -W "--batch" -- "$cur" ) ) + return 0 + ;; + -N|--Netns) + local nslist=$( ip netns list 2>/dev/null ) + COMPREPLY=( $( compgen -W "$nslist" -- "$cur" ) ) + return 0 + ;; + -j|--json) + COMPREPLY=( $( compgen -W "--pretty $objects" -- "$cur" ) ) + return 0 + ;; + *) + COMPREPLY=( $( compgen -W "$objects" -- "$cur" ) ) + return 0 + ;; + esac + fi + + # Remove all options so completions don't have to deal with them. + local i + for (( i=1; i < ${#words[@]}; )); do + if [[ ${words[i]::1} == - ]]; then + words=( "${words[@]:0:i}" "${words[@]:i+1}" ) + [[ $i -le $cword ]] && cword=$(( cword - 1 )) + else + i=$(( ++i )) + fi + done + + local object=${words[1]} + local command=${words[2]} + local pprev=${words[cword - 2]} + + if [[ $objects =~ $object ]]; then + if [[ $cword -eq 2 ]]; then + COMPREPLY=( $( compgen -W "help" -- "$cur") ) + if [[ $object != "monitor" && $object != "dpipe" ]]; then + COMPREPLY+=( $( compgen -W \ + "$(_devlink_get_optional_commands $object)" -- "$cur" ) ) + fi + fi + "_devlink_$object" + fi + +} && +complete -F _devlink devlink + +# ex: ts=4 sw=4 et filetype=sh diff --git a/bash-completion/tc b/bash-completion/tc new file mode 100644 index 0000000..086cb7f --- /dev/null +++ b/bash-completion/tc @@ -0,0 +1,809 @@ +# tc(8) completion -*- shell-script -*- +# Copyright 2016 6WIND S.A. +# Copyright 2016 Quentin Monnet + +QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \ + mqprio multiq netem pfifo_fast pie fq_pie red rr sfb sfq tbf atm \ + cbq drr dsmark hfsc htb prio qfq ' +FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall ' +ACTION_KIND=' gact mirred bpf sample ' + +# Takes a list of words in argument; each one of them is added to COMPREPLY if +# it is not already present on the command line. Returns no value. +_tc_once_attr() +{ + local w subcword found + for w in $*; do + found=0 + for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do + if [[ $w == ${words[subcword]} ]]; then + found=1 + break + fi + done + [[ $found -eq 0 ]] && \ + COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) ) + done +} + +# Takes a list of words in argument; each one of them is added to COMPREPLY if +# it is not already present on the command line from the provided index. Returns +# no value. +_tc_once_attr_from() +{ + local w subcword found from=$1 + shift + for w in $*; do + found=0 + for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do + if [[ $w == ${words[subcword]} ]]; then + found=1 + break + fi + done + [[ $found -eq 0 ]] && \ + COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) ) + done +} + +# Takes a list of words in argument; adds them all to COMPREPLY if none of them +# is already present on the command line. Returns no value. +_tc_one_of_list() +{ + local w subcword + for w in $*; do + for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do + [[ $w == ${words[subcword]} ]] && return 1 + done + done + COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) ) +} + +# Takes a list of words in argument; adds them all to COMPREPLY if none of them +# is already present on the command line from the provided index. Returns no +# value. +_tc_one_of_list_from() +{ + local w subcword from=$1 + shift + for w in $*; do + for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do + [[ $w == ${words[subcword]} ]] && return 1 + done + done + COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) ) +} + +# Returns "$cur ${cur}arg1 ${cur}arg2 ..." +_tc_expand_units() +{ + [[ $cur =~ ^[0-9]+ ]] || return 1 + local value=${cur%%[^0-9]*} + [[ $cur == $value ]] && echo $cur + echo ${@/#/$value} +} + +# Complete based on given word, usually $prev (or possibly the word before), +# for when an argument or an option name has but a few possible arguments (so +# tc does not take particular commands into account here). +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_direct_complete() +{ + case $1 in + # Command options + dev) + _available_interfaces + return 0 + ;; + classid) + return 0 + ;; + estimator) + local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) + COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) + return 0 + ;; + handle) + return 0 + ;; + parent|flowid) + local i iface ids cmd + for (( i=3; i < ${#words[@]}-2; i++ )); do + [[ ${words[i]} == dev ]] && iface=${words[i+1]} + break + done + for cmd in qdisc class; do + if [[ -n $iface ]]; then + ids+=$( tc $cmd show dev $iface 2>/dev/null | \ + cut -d\ -f 3 )" " + else + ids+=$( tc $cmd show 2>/dev/null | cut -d\ -f 3 ) + fi + done + [[ $ids != " " ]] && \ + COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) ) + return 0 + ;; + protocol) # list comes from lib/ll_proto.c + COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \ + all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \ + ddcmp dec diag dna_dl dna_rc dna_rt econet ieeepup ieeepupat \ + ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \ + ppp_mp ppp_ses ppptalk pup pupat rarp sca snap tipc tr_802_2 \ + wan_ppp x25' -- "$cur" ) ) + return 0 + ;; + prio) + return 0 + ;; + stab) + COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead + linklayer' -- "$cur" ) ) + ;; + + # Qdiscs and classes options + alpha|bands|beta|buckets|corrupt|debug|decrement|default|\ + default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\ + flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\ + penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\ + reorder|vq|vqs) + return 0 + ;; + setup) + COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) ) + return 0 + ;; + hw) + COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) ) + return 0 + ;; + distribution) + COMPREPLY+=( $( compgen -W 'uniform normal pareto + paretonormal' -- "$cur" ) ) + return 0 + ;; + loss) + COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) ) + return 0 + ;; + + # Qdiscs and classes options options + gap|gmodel|state) + return 0 + ;; + + # Filters options + map) + COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) ) + return 0 + ;; + hash) + COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) ) + return 0 + ;; + indev) + _available_interfaces + return 0 + ;; + eth_type) + COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) ) + return 0 + ;; + ip_proto) + COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) ) + return 0 + ;; + + # Filters options options + key|keys) + [[ ${words[@]} =~ graft ]] && return 1 + COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \ + priority mark nfct nfct-src nfct-dst nfct-proto-src \ + nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \ + "$cur" ) ) + return 0 + ;; + + # BPF options - used for filters, actions, and exec + export|bytecode|bytecode-file|object-file) + _filedir + return 0 + ;; + object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/ + [[ -n "$cur" ]] && _filedir && return 0 + COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir + compopt -o nospace + return 0 + ;; + section) + if (type objdump > /dev/null 2>&1) ; then + local fword objfile section_list + for (( fword=3; fword < ${#words[@]}-3; fword++ )); do + if [[ ${words[fword]} == object-file ]]; then + objfile=${words[fword+1]} + break + fi + done + section_list=$( objdump -h $objfile 2>/dev/null | \ + sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' ) + COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) ) + fi + return 0 + ;; + import|run) + _filedir + return 0 + ;; + type) + COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) ) + return 0 + ;; + + # Actions options + random) + _tc_one_of_list 'netrand determ' + return 0 + ;; + + # Units for option arguments + bandwidth|maxrate|peakrate|rate) + local list=$( _tc_expand_units 'bit' \ + 'kbit' 'kibit' 'kbps' 'kibps' \ + 'mbit' 'mibit' 'mbps' 'mibps' \ + 'gbit' 'gibit' 'gbps' 'gibps' \ + 'tbit' 'tibit' 'tbps' 'tibps' ) + COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) + ;; + admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\ + overhead|quantum|redflowlist) + local list=$( _tc_expand_units \ + 'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' ) + COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) + ;; + db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\ + target|tupdate) + local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) + COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) + ;; + esac + return 1 +} + +# Complete with options names for qdiscs. Each qdisc has its own set of options +# and it seems we cannot really parse it from anywhere, so we add it manually +# in this function. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_qdisc_options() +{ + case $1 in + choke) + _tc_once_attr 'limit bandwidth ecn min max burst' + return 0 + ;; + codel) + _tc_once_attr 'limit target interval' + _tc_one_of_list 'ecn noecn' + return 0 + ;; + bfifo|pfifo|pfifo_head_drop) + _tc_once_attr 'limit' + return 0 + ;; + fq) + _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \ + buckets' + _tc_one_of_list 'pacing nopacing' + return 0 + ;; + fq_codel) + _tc_once_attr 'limit flows target interval quantum' + _tc_one_of_list 'ecn noecn' + return 0 + ;; + gred) + _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \ + burst probability bandwidth ecn harddrop' + return 0 + ;; + hhf) + _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \ + evict_timeout non_hh_weight' + return 0 + ;; + mqprio) + _tc_once_attr 'num_tc map queues hw' + return 0 + ;; + netem) + _tc_once_attr 'delay distribution corrupt duplicate loss ecn \ + reorder rate' + return 0 + ;; + pie) + _tc_once_attr 'limit target tupdate alpha beta' + _tc_one_of_list 'bytemode nobytemode' + _tc_one_of_list 'ecn noecn' + _tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator' + return 0 + ;; + fq_pie) + _tc_once_attr 'limit flows target tupdate \ + alpha beta quantum memory_limit ecn_prob' + _tc_one_of_list 'ecn noecn' + _tc_one_of_list 'bytemode nobytemode' + _tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator' + return 0 + ;; + red) + _tc_once_attr 'limit min max avpkt burst adaptive probability \ + bandwidth ecn harddrop' + return 0 + ;; + rr|prio) + _tc_once_attr 'bands priomap multiqueue' + return 0 + ;; + sfb) + _tc_once_attr 'rehash db limit max target increment decrement \ + penalty_rate penalty_burst' + return 0 + ;; + sfq) + _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \ + redflowlimit min max avpkt burst probability ecn harddrop' + return 0 + ;; + tbf) + _tc_once_attr 'limit burst rate mtu peakrate latency overhead \ + linklayer' + return 0 + ;; + cbq) + _tc_once_attr 'bandwidth avpkt mpu cell ewma' + return 0 + ;; + dsmark) + _tc_once_attr 'indices default_index set_tc_index' + return 0 + ;; + hfsc) + _tc_once_attr 'default' + return 0 + ;; + htb) + _tc_once_attr 'default r2q direct_qlen debug' + return 0 + ;; + multiq|pfifo_fast|atm|drr|qfq) + return 0 + ;; + esac + return 1 +} + +# Complete with options names for BPF filters or actions. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_bpf_options() +{ + [[ ${words[${#words[@]}-3]} == object-file ]] && \ + _tc_once_attr 'section export' + [[ ${words[${#words[@]}-5]} == object-file ]] && \ + [[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \ + _tc_once_attr 'section export' + _tc_one_of_list 'bytecode bytecode-file object-file object-pinned' + _tc_once_attr 'verbose index direct-action action classid' + return 0 +} + +# Complete with options names for filter actions. +# This function is recursive, thus allowing multiple actions statement to be +# parsed. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_filter_action_options() +{ + for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); + do + if [[ action == ${words[acwd]} ]]; then + _tc_filter_action_options $((acwd+1)) && return 0 + fi + done + + local action acwd + for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do + if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then + _tc_one_of_list_from $acwd action + _tc_action_options $acwd && return 0 + fi + done + _tc_one_of_list_from $acwd $ACTION_KIND + return 0 +} + +# Complete with options names for filters. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_filter_options() +{ + + for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); + do + if [[ action == ${words[acwd]} ]]; then + _tc_filter_action_options $((acwd+1)) && return 0 + fi + done + + filter=${words[$1]} + case $filter in + basic) + _tc_once_attr 'match action classid' + return 0 + ;; + bpf) + _tc_bpf_options + return 0 + ;; + cgroup) + _tc_once_attr 'match action' + return 0 + ;; + flow) + local i + for (( i=5; i < ${#words[@]}-1; i++ )); do + if [[ ${words[i]} =~ ^keys?$ ]]; then + _tc_direct_complete 'key' + COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \ + "$cur" ) ) + break + fi + done + _tc_once_attr 'map hash divisor baseclass match action' + return 0 + ;; + matchall) + _tc_once_attr 'action classid skip_sw skip_hw' + return 0 + ;; + flower) + _tc_once_attr 'action classid indev dst_mac src_mac eth_type \ + ip_proto dst_ip src_ip dst_port src_port' + return 0 + ;; + fw) + _tc_once_attr 'action classid' + return 0 + ;; + route) + _tc_one_of_list 'from fromif' + _tc_once_attr 'to classid action' + return 0 + ;; + rsvp) + _tc_once_attr 'ipproto session sender classid action tunnelid \ + tunnel flowlabel spi/ah spi/esp u8 u16 u32' + [[ ${words[${#words[@]}-3]} == tunnel ]] && \ + COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) ) + [[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \ + COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) ) + [[ ${words[${#words[@]}-3]} == mask ]] && \ + COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) ) + return 0 + ;; + tcindex) + _tc_once_attr 'hash mask shift classid action' + _tc_one_of_list 'pass_on fall_through' + return 0 + ;; + u32) + _tc_once_attr 'match link classid action offset ht hashkey sample' + COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \ + divisor' -- "$cur" ) ) + return 0 + ;; + esac + return 1 +} + +# Complete with options names for actions. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_action_options() +{ + local from=$1 + local action=${words[from]} + case $action in + bpf) + _tc_bpf_options + return 0 + ;; + mirred) + _tc_one_of_list_from $from 'ingress egress' + _tc_one_of_list_from $from 'mirror redirect' + _tc_once_attr_from $from 'index dev' + return 0 + ;; + sample) + _tc_once_attr_from $from 'rate' + _tc_once_attr_from $from 'trunc' + _tc_once_attr_from $from 'group' + return 0 + ;; + gact) + _tc_one_of_list_from $from 'reclassify drop continue pass' + _tc_once_attr_from $from 'random' + return 0 + ;; + esac + return 1 +} + +# Complete with options names for exec. +# Returns 0 is completion should stop after running this function, 1 otherwise. +_tc_exec_options() +{ + case $1 in + import) + [[ ${words[${#words[@]}-3]} == import ]] && \ + _tc_once_attr 'run' + return 0 + ;; + graft) + COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) ) + [[ ${words[${#words[@]}-3]} == object-file ]] && \ + _tc_once_attr 'type' + _tc_bpf_options + return 0 + ;; + esac + return 1 +} + +# Main completion function +# Logic is as follows: +# 1. Check if previous word is a global option; if so, propose arguments. +# 2. Check if current word is a global option; if so, propose completion. +# 3. Check for the presence of a main command (qdisc|class|filter|...). If +# there is one, first call _tc_direct_complete to see if previous word is +# waiting for a particular completion. If so, propose completion and exit. +# 4. Extract main command and -- if available -- its subcommand +# (add|delete|show|...). +# 5. Propose completion based on main and sub- command in use. Additional +# functions may be called for qdiscs, classes or filter options. +_tc() +{ + local cur prev words cword + _init_completion || return + + case $prev in + -V|-Version) + return 0 + ;; + -b|-batch|-cf|-conf) + _filedir + return 0 + ;; + -force) + COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) ) + return 0 + ;; + -nm|name) + [[ -r /etc/iproute2/tc_cls ]] || \ + COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) ) + return 0 + ;; + -n|-net|-netns) + local nslist=$( ip netns list 2>/dev/null ) + COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) ) + return 0 + ;; + -tshort) + _tc_once_attr '-statistics' + COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) ) + return 0 + ;; + -timestamp) + _tc_once_attr '-statistics -tshort' + COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) ) + return 0 + ;; + esac + + # Search for main commands + local subcword cmd subcmd + for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do + [[ ${words[subcword]} == -b?(atch) ]] && return 0 + [[ -n $cmd ]] && subcmd=${words[subcword]} && break + [[ ${words[subcword]} != -* && \ + ${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \ + cmd=${words[subcword]} + done + + if [[ -z $cmd ]]; then + case $cur in + -*) + local c='-Version -statistics -details -raw -pretty \ + -iec -graphe -batch -name -netns -timestamp' + [[ $cword -eq 1 ]] && c+=' -force' + COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) + return 0 + ;; + *) + COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \ + command sed \ + -e '/OBJECT := /!d' \ + -e 's/.*{//' \ + -e 's/}.*//' \ + -e \ 's/|//g' )" -- "$cur" ) ) + return 0 + ;; + esac + fi + + [[ $subcmd == help ]] && return 0 + + # For this set of commands we may create COMPREPLY just by analysing the + # previous word, if it expects for a specific list of options or values. + if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then + _tc_direct_complete $prev && return 0 + if [[ ${words[${#words[@]}-3]} == estimator ]]; then + local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' ) + COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0 + fi + fi + + # Completion depends on main command and subcommand in use. + case $cmd in + qdisc) + case $subcmd in + add|change|replace|link|del|delete) + if [[ $(($cword-$subcword)) -eq 1 ]]; then + COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) + return 0 + fi + local qdisc qdwd + for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do + if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then + qdisc=${words[qdwd]} + _tc_qdisc_options $qdisc && return 0 + fi + done + _tc_one_of_list $QDISC_KIND + _tc_one_of_list 'root ingress parent clsact' + _tc_once_attr 'handle estimator stab' + ;; + show) + _tc_once_attr 'dev' + _tc_one_of_list 'ingress clsact' + _tc_once_attr '-statistics -details -raw -pretty -iec \ + -graph -name' + ;; + help) + return 0 + ;; + *) + [[ $cword -eq $subcword ]] && \ + COMPREPLY=( $( compgen -W 'help add delete change \ + replace link show' -- "$cur" ) ) + ;; + esac + ;; + + class) + case $subcmd in + add|change|replace|del|delete) + if [[ $(($cword-$subcword)) -eq 1 ]]; then + COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) + return 0 + fi + local qdisc qdwd + for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do + if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then + qdisc=${words[qdwd]} + _tc_qdisc_options $qdisc && return 0 + fi + done + _tc_one_of_list $QDISC_KIND + _tc_one_of_list 'root parent' + _tc_once_attr 'classid' + ;; + show) + _tc_once_attr 'dev' + _tc_one_of_list 'root parent' + _tc_once_attr '-statistics -details -raw -pretty -iec \ + -graph -name' + ;; + help) + return 0 + ;; + *) + [[ $cword -eq $subcword ]] && \ + COMPREPLY=( $( compgen -W 'help add delete change \ + replace show' -- "$cur" ) ) + ;; + esac + ;; + + filter) + case $subcmd in + add|change|replace|del|delete) + if [[ $(($cword-$subcword)) -eq 1 ]]; then + COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) ) + return 0 + fi + local filter fltwd + for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++)); + do + if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then + _tc_filter_options $fltwd && return 0 + fi + done + _tc_one_of_list $FILTER_KIND + _tc_one_of_list 'root ingress egress parent' + _tc_once_attr 'handle estimator pref protocol' + ;; + show) + _tc_once_attr 'dev' + _tc_one_of_list 'root ingress egress parent' + _tc_once_attr '-statistics -details -raw -pretty -iec \ + -graph -name' + ;; + help) + return 0 + ;; + *) + [[ $cword -eq $subcword ]] && \ + COMPREPLY=( $( compgen -W 'help add delete change \ + replace show' -- "$cur" ) ) + ;; + esac + ;; + + action) + case $subcmd in + add|change|replace) + local action acwd + for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do + if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then + _tc_action_options $acwd && return 0 + fi + done + _tc_one_of_list $ACTION_KIND + ;; + get|del|delete) + _tc_once_attr 'index' + ;; + lst|list|flush|show) + _tc_one_of_list $ACTION_KIND + ;; + *) + [[ $cword -eq $subcword ]] && \ + COMPREPLY=( $( compgen -W 'help add delete change \ + replace show list flush action' -- "$cur" ) ) + ;; + esac + ;; + + monitor) + COMPREPLY=( $( compgen -W 'help' -- "$cur" ) ) + ;; + + exec) + case $subcmd in + bpf) + local excmd exwd EXEC_KIND=' import debug graft ' + for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do + if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then + excmd=${words[exwd]} + _tc_exec_options $excmd && return 0 + fi + done + _tc_one_of_list $EXEC_KIND + ;; + *) + [[ $cword -eq $subcword ]] && \ + COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) ) + ;; + esac + ;; + esac +} && +complete -F _tc tc + +# ex: ts=4 sw=4 et filetype=sh diff --git a/bridge/.gitignore b/bridge/.gitignore new file mode 100644 index 0000000..7096907 --- /dev/null +++ b/bridge/.gitignore @@ -0,0 +1 @@ +bridge diff --git a/bridge/Makefile b/bridge/Makefile new file mode 100644 index 0000000..c6b7d08 --- /dev/null +++ b/bridge/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o + +include ../config.mk + +all: bridge + +bridge: $(BROBJ) $(LIBNETLINK) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 bridge $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(BROBJ) bridge diff --git a/bridge/br_common.h b/bridge/br_common.h new file mode 100644 index 0000000..b5798da --- /dev/null +++ b/bridge/br_common.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define MDB_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(struct br_mdb_entry)))) + +#define MDB_RTR_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + RTA_ALIGN(sizeof(__u32)))) + +void print_vlan_info(struct rtattr *tb, int ifindex); +int print_linkinfo(struct nlmsghdr *n, void *arg); +int print_mdb_mon(struct nlmsghdr *n, void *arg); +int print_fdb(struct nlmsghdr *n, void *arg); + +int do_fdb(int argc, char **argv); +int do_mdb(int argc, char **argv); +int do_monitor(int argc, char **argv); +int do_vlan(int argc, char **argv); +int do_link(int argc, char **argv); + +extern int preferred_family; +extern int show_stats; +extern int show_details; +extern int timestamp; +extern int compress_vlans; +extern int json; +extern struct rtnl_handle rth; diff --git a/bridge/bridge.c b/bridge/bridge.c new file mode 100644 index 0000000..453d689 --- /dev/null +++ b/bridge/bridge.c @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Get/set/delete bridge with netlink + * + * Authors: Stephen Hemminger + */ + +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "utils.h" +#include "br_common.h" +#include "namespace.h" +#include "color.h" + +struct rtnl_handle rth = { .fd = -1 }; +int preferred_family = AF_UNSPEC; +int oneline; +int show_stats; +int show_details; +static int color; +int compress_vlans; +int json; +int timestamp; +static const char *batch_file; +int force; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: bridge [ OPTIONS ] OBJECT { COMMAND | help }\n" +" bridge [ -force ] -batch filename\n" +"where OBJECT := { link | fdb | mdb | vlan | monitor }\n" +" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] |\n" +" -o[neline] | -t[imestamp] | -n[etns] name |\n" +" -c[ompressvlans] -color -p[retty] -j[son] }\n"); + exit(-1); +} + +static int do_help(int argc, char **argv) +{ + usage(); +} + + +static const struct cmd { + const char *cmd; + int (*func)(int argc, char **argv); +} cmds[] = { + { "link", do_link }, + { "fdb", do_fdb }, + { "mdb", do_mdb }, + { "vlan", do_vlan }, + { "monitor", do_monitor }, + { "help", do_help }, + { 0 } +}; + +static int do_cmd(const char *argv0, int argc, char **argv) +{ + const struct cmd *c; + + for (c = cmds; c->cmd; ++c) { + if (matches(argv0, c->cmd) == 0) + return c->func(argc-1, argv+1); + } + + fprintf(stderr, + "Object \"%s\" is unknown, try \"bridge help\".\n", argv0); + return -1; +} + +static int batch(const char *name) +{ + char *line = NULL; + size_t len = 0; + int ret = EXIT_SUCCESS; + + if (name && strcmp(name, "-") != 0) { + if (freopen(name, "r", stdin) == NULL) { + fprintf(stderr, + "Cannot open file \"%s\" for reading: %s\n", + name, strerror(errno)); + return EXIT_FAILURE; + } + } + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return EXIT_FAILURE; + } + + rtnl_set_strict_dump(&rth); + + cmdlineno = 0; + while (getcmdline(&line, &len, stdin) != -1) { + char *largv[100]; + int largc; + + largc = makeargs(line, largv, 100); + if (largc == 0) + continue; /* blank line */ + + if (do_cmd(largv[0], largc, largv)) { + fprintf(stderr, "Command failed %s:%d\n", + name, cmdlineno); + ret = EXIT_FAILURE; + if (!force) + break; + } + } + if (line) + free(line); + + rtnl_close(&rth); + return ret; +} + +int +main(int argc, char **argv) +{ + while (argc > 1) { + const char *opt = argv[1]; + + if (strcmp(opt, "--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + + if (matches(opt, "-help") == 0) { + usage(); + } else if (matches(opt, "-Version") == 0) { + printf("bridge utility, 0.0\n"); + exit(0); + } else if (matches(opt, "-stats") == 0 || + matches(opt, "-statistics") == 0) { + ++show_stats; + } else if (matches(opt, "-details") == 0) { + ++show_details; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; + } else if (matches(opt, "-timestamp") == 0) { + ++timestamp; + } else if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(argv[1], "help") == 0) + usage(); + else + invarg("invalid protocol family", argv[1]); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (matches(opt, "-netns") == 0) { + NEXT_ARG(); + if (netns_switch(argv[1])) + exit(-1); + } else if (matches(opt, "-compressvlans") == 0) { + ++compress_vlans; + } else if (matches_color(opt, &color)) { + } else if (matches(opt, "-force") == 0) { + ++force; + } else if (matches(opt, "-json") == 0) { + ++json; + } else if (matches(opt, "-pretty") == 0) { + ++pretty; + } else if (matches(opt, "-batch") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + batch_file = argv[1]; + } else { + fprintf(stderr, + "Option \"%s\" is unknown, try \"bridge help\".\n", + opt); + exit(-1); + } + argc--; argv++; + } + + _SL_ = oneline ? "\\" : "\n"; + + check_enable_color(color, json); + + if (batch_file) + return batch(batch_file); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + rtnl_set_strict_dump(&rth); + + if (argc > 1) + return do_cmd(argv[1], argc-1, argv+1); + + rtnl_close(&rth); + usage(); +} diff --git a/bridge/fdb.c b/bridge/fdb.c new file mode 100644 index 0000000..37465e4 --- /dev/null +++ b/bridge/fdb.c @@ -0,0 +1,690 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Get/set/delete fdb table with netlink + * + * TODO: merge/replace this with ip neighbour + * + * Authors: Stephen Hemminger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json_print.h" +#include "libnetlink.h" +#include "br_common.h" +#include "rt_names.h" +#include "utils.h" + +static unsigned int filter_index, filter_dynamic, filter_master, + filter_state, filter_vlan; + +static void usage(void) +{ + fprintf(stderr, + "Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n" + " [ self ] [ master ] [ use ] [ router ] [ extern_learn ]\n" + " [ sticky ] [ local | static | dynamic ] [ vlan VID ]\n" + " { [ dst IPADDR ] [ port PORT] [ vni VNI ] | [ nhid NHID ] }\n" + " [ via DEV ] [ src_vni VNI ]\n" + " bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ]\n" + " [ state STATE ] [ dynamic ] ]\n" + " bridge fdb get [ to ] LLADDR [ br BRDEV ] { brport | dev } DEV\n" + " [ vlan VID ] [ vni VNI ] [ self ] [ master ] [ dynamic ]\n"); + exit(-1); +} + +static const char *state_n2a(unsigned int s) +{ + static char buf[32]; + + if (s & NUD_PERMANENT) + return "permanent"; + + if (s & NUD_NOARP) + return "static"; + + if (s & NUD_STALE) + return "stale"; + + if (s & NUD_REACHABLE) + return ""; + + if (is_json_context()) + sprintf(buf, "%#x", s); + else + sprintf(buf, "state=%#x", s); + return buf; +} + +static int state_a2n(unsigned int *s, const char *arg) +{ + if (matches(arg, "permanent") == 0) + *s = NUD_PERMANENT; + else if (matches(arg, "static") == 0 || matches(arg, "temp") == 0) + *s = NUD_NOARP; + else if (matches(arg, "stale") == 0) + *s = NUD_STALE; + else if (matches(arg, "reachable") == 0 || matches(arg, "dynamic") == 0) + *s = NUD_REACHABLE; + else if (strcmp(arg, "all") == 0) + *s = ~0; + else if (get_unsigned(s, arg, 0)) + return -1; + + return 0; +} + +static void fdb_print_flags(FILE *fp, unsigned int flags) +{ + open_json_array(PRINT_JSON, + is_json_context() ? "flags" : ""); + + if (flags & NTF_SELF) + print_string(PRINT_ANY, NULL, "%s ", "self"); + + if (flags & NTF_ROUTER) + print_string(PRINT_ANY, NULL, "%s ", "router"); + + if (flags & NTF_EXT_LEARNED) + print_string(PRINT_ANY, NULL, "%s ", "extern_learn"); + + if (flags & NTF_OFFLOADED) + print_string(PRINT_ANY, NULL, "%s ", "offload"); + + if (flags & NTF_MASTER) + print_string(PRINT_ANY, NULL, "%s ", "master"); + + if (flags & NTF_STICKY) + print_string(PRINT_ANY, NULL, "%s ", "sticky"); + + close_json_array(PRINT_JSON, NULL); +} + +static void fdb_print_stats(FILE *fp, const struct nda_cacheinfo *ci) +{ + static int hz; + + if (!hz) + hz = get_user_hz(); + + if (is_json_context()) { + print_uint(PRINT_JSON, "used", NULL, + ci->ndm_used / hz); + print_uint(PRINT_JSON, "updated", NULL, + ci->ndm_updated / hz); + } else { + fprintf(fp, "used %d/%d ", ci->ndm_used / hz, + ci->ndm_updated / hz); + + } +} + +int print_fdb(struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[NDA_MAX+1]; + __u16 vid = 0; + + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { + fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->ndm_family != AF_BRIDGE) + return 0; + + if (filter_index && filter_index != r->ndm_ifindex) + return 0; + + if (filter_state && !(r->ndm_state & filter_state)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[NDA_VLAN]) + vid = rta_getattr_u16(tb[NDA_VLAN]); + + if (filter_vlan && filter_vlan != vid) + return 0; + + if (filter_dynamic && (r->ndm_state & NUD_PERMANENT)) + return 0; + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELNEIGH) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[NDA_LLADDR]) { + const char *lladdr; + SPRINT_BUF(b1); + + lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1)); + + print_color_string(PRINT_ANY, COLOR_MAC, + "mac", "%s ", lladdr); + } + + if (!filter_index && r->ndm_ifindex) + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "dev %s ", + ll_index_to_name(r->ndm_ifindex)); + + if (tb[NDA_DST]) { + int family = AF_INET; + const char *dst; + + if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr)) + family = AF_INET6; + + dst = format_host(family, + RTA_PAYLOAD(tb[NDA_DST]), + RTA_DATA(tb[NDA_DST])); + + print_color_string(PRINT_ANY, + ifa_family_color(family), + "dst", "dst %s ", dst); + } + + if (vid) + print_uint(PRINT_ANY, + "vlan", "vlan %hu ", vid); + + if (tb[NDA_PORT]) + print_uint(PRINT_ANY, + "port", "port %u ", + rta_getattr_be16(tb[NDA_PORT])); + + if (tb[NDA_VNI]) + print_uint(PRINT_ANY, + "vni", "vni %u ", + rta_getattr_u32(tb[NDA_VNI])); + + if (tb[NDA_SRC_VNI]) + print_uint(PRINT_ANY, + "src_vni", "src_vni %u ", + rta_getattr_u32(tb[NDA_SRC_VNI])); + + if (tb[NDA_IFINDEX]) { + unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]); + + if (tb[NDA_LINK_NETNSID]) + print_uint(PRINT_ANY, + "viaIfIndex", "via ifindex %u ", + ifindex); + else + print_string(PRINT_ANY, + "viaIf", "via %s ", + ll_index_to_name(ifindex)); + } + + if (tb[NDA_NH_ID]) + print_uint(PRINT_ANY, "nhid", "nhid %u ", + rta_getattr_u32(tb[NDA_NH_ID])); + + if (tb[NDA_LINK_NETNSID]) + print_uint(PRINT_ANY, + "linkNetNsId", "link-netnsid %d ", + rta_getattr_u32(tb[NDA_LINK_NETNSID])); + + if (show_stats && tb[NDA_CACHEINFO]) + fdb_print_stats(fp, RTA_DATA(tb[NDA_CACHEINFO])); + + fdb_print_flags(fp, r->ndm_flags); + + + if (tb[NDA_MASTER]) + print_string(PRINT_ANY, "master", "master %s ", + ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER]))); + + print_string(PRINT_ANY, "state", "%s\n", + state_n2a(r->ndm_state)); + close_json_object(); + fflush(fp); + return 0; +} + +static int fdb_linkdump_filter(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter_index) { + struct ifinfomsg *ifm = NLMSG_DATA(nlh); + + ifm->ifi_index = filter_index; + } + + if (filter_master) { + err = addattr32(nlh, reqlen, IFLA_MASTER, filter_master); + if (err) + return err; + } + + return 0; +} + +static int fdb_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter_index) { + struct ndmsg *ndm = NLMSG_DATA(nlh); + + ndm->ndm_ifindex = filter_index; + } + + if (filter_master) { + err = addattr32(nlh, reqlen, NDA_MASTER, filter_master); + if (err) + return err; + } + + return 0; +} + +static int fdb_show(int argc, char **argv) +{ + char *filter_dev = NULL; + char *br = NULL; + int rc; + + while (argc > 0) { + if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + filter_dev = *argv; + } else if (strcmp(*argv, "br") == 0) { + NEXT_ARG(); + br = *argv; + } else if (strcmp(*argv, "vlan") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vlan", *argv); + filter_vlan = atoi(*argv); + } else if (strcmp(*argv, "state") == 0) { + unsigned int state; + + NEXT_ARG(); + if (state_a2n(&state, *argv)) + invarg("invalid state", *argv); + filter_state |= state; + } else if (strcmp(*argv, "dynamic") == 0) { + filter_dynamic = 1; + } else { + if (matches(*argv, "help") == 0) + usage(); + } + argc--; argv++; + } + + if (br) { + int br_ifindex = ll_name_to_index(br); + + if (br_ifindex == 0) { + fprintf(stderr, "Cannot find bridge device \"%s\"\n", br); + return -1; + } + filter_master = br_ifindex; + } + + /*we'll keep around filter_dev for older kernels */ + if (filter_dev) { + filter_index = ll_name_to_index(filter_dev); + if (!filter_index) + return nodev(filter_dev); + } + + if (rth.flags & RTNL_HANDLE_F_STRICT_CHK) + rc = rtnl_neighdump_req(&rth, PF_BRIDGE, fdb_dump_filter); + else + rc = rtnl_fdb_linkdump_req_filter_fn(&rth, fdb_linkdump_filter); + if (rc < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int fdb_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndm.ndm_family = PF_BRIDGE, + .ndm.ndm_state = NUD_NOARP, + }; + char *addr = NULL; + char *d = NULL; + char abuf[ETH_ALEN]; + int dst_ok = 0; + inet_prefix dst; + unsigned long port = 0; + unsigned long vni = ~0; + unsigned long src_vni = ~0; + unsigned int via = 0; + char *endptr; + short vid = -1; + __u32 nhid = 0; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + if (dst_ok) + duparg2("dst", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + } else if (strcmp(*argv, "nhid") == 0) { + NEXT_ARG(); + if (get_u32(&nhid, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + } else if (strcmp(*argv, "port") == 0) { + + NEXT_ARG(); + port = strtoul(*argv, &endptr, 0); + if (endptr && *endptr) { + struct servent *pse; + + pse = getservbyname(*argv, "udp"); + if (!pse) + invarg("invalid port\n", *argv); + port = ntohs(pse->s_port); + } else if (port > 0xffff) + invarg("invalid port\n", *argv); + } else if (strcmp(*argv, "vni") == 0) { + NEXT_ARG(); + vni = strtoul(*argv, &endptr, 0); + if ((endptr && *endptr) || + (vni >> 24) || vni == ULONG_MAX) + invarg("invalid VNI\n", *argv); + } else if (strcmp(*argv, "src_vni") == 0) { + NEXT_ARG(); + src_vni = strtoul(*argv, &endptr, 0); + if ((endptr && *endptr) || + (src_vni >> 24) || src_vni == ULONG_MAX) + invarg("invalid src VNI\n", *argv); + } else if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + via = ll_name_to_index(*argv); + if (!via) + exit(nodev(*argv)); + } else if (strcmp(*argv, "self") == 0) { + req.ndm.ndm_flags |= NTF_SELF; + } else if (matches(*argv, "master") == 0) { + req.ndm.ndm_flags |= NTF_MASTER; + } else if (matches(*argv, "router") == 0) { + req.ndm.ndm_flags |= NTF_ROUTER; + } else if (matches(*argv, "local") == 0 || + matches(*argv, "permanent") == 0) { + req.ndm.ndm_state |= NUD_PERMANENT; + } else if (matches(*argv, "temp") == 0 || + matches(*argv, "static") == 0) { + req.ndm.ndm_state |= NUD_REACHABLE; + } else if (matches(*argv, "dynamic") == 0) { + req.ndm.ndm_state |= NUD_REACHABLE; + req.ndm.ndm_state &= ~NUD_NOARP; + } else if (matches(*argv, "vlan") == 0) { + if (vid >= 0) + duparg2("vlan", *argv); + NEXT_ARG(); + vid = atoi(*argv); + } else if (matches(*argv, "use") == 0) { + req.ndm.ndm_flags |= NTF_USE; + } else if (matches(*argv, "extern_learn") == 0) { + req.ndm.ndm_flags |= NTF_EXT_LEARNED; + } else if (matches(*argv, "sticky") == 0) { + req.ndm.ndm_flags |= NTF_STICKY; + } else { + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + + if (matches(*argv, "help") == 0) + usage(); + if (addr) + duparg2("to", *argv); + addr = *argv; + } + argc--; argv++; + } + + if (d == NULL || addr == NULL) { + fprintf(stderr, "Device and address are required arguments.\n"); + return -1; + } + + if (nhid && (dst_ok || port || vni != ~0)) { + fprintf(stderr, "dst, port, vni are mutually exclusive with nhid\n"); + return -1; + } + + /* Assume self */ + if (!(req.ndm.ndm_flags&(NTF_SELF|NTF_MASTER))) + req.ndm.ndm_flags |= NTF_SELF; + + /* Assume permanent */ + if (!(req.ndm.ndm_state&(NUD_PERMANENT|NUD_REACHABLE))) + req.ndm.ndm_state |= NUD_PERMANENT; + + if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + abuf, abuf+1, abuf+2, + abuf+3, abuf+4, abuf+5) != 6) { + fprintf(stderr, "Invalid mac address %s\n", addr); + return -1; + } + + addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN); + if (dst_ok) + addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen); + + if (vid >= 0) + addattr16(&req.n, sizeof(req), NDA_VLAN, vid); + if (nhid > 0) + addattr32(&req.n, sizeof(req), NDA_NH_ID, nhid); + + if (port) { + unsigned short dport; + + dport = htons((unsigned short)port); + addattr16(&req.n, sizeof(req), NDA_PORT, dport); + } + if (vni != ~0) + addattr32(&req.n, sizeof(req), NDA_VNI, vni); + if (src_vni != ~0) + addattr32(&req.n, sizeof(req), NDA_SRC_VNI, src_vni); + if (via) + addattr32(&req.n, sizeof(req), NDA_IFINDEX, via); + + req.ndm.ndm_ifindex = ll_name_to_index(d); + if (!req.ndm.ndm_ifindex) + return nodev(d); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -1; + + return 0; +} + +static int fdb_get(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNEIGH, + .ndm.ndm_family = AF_BRIDGE, + }; + char *d = NULL, *br = NULL; + struct nlmsghdr *answer; + unsigned long vni = ~0; + char abuf[ETH_ALEN]; + int br_ifindex = 0; + char *addr = NULL; + short vlan = -1; + char *endptr; + + while (argc > 0) { + if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "br") == 0) { + NEXT_ARG(); + br = *argv; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "vni") == 0) { + NEXT_ARG(); + vni = strtoul(*argv, &endptr, 0); + if ((endptr && *endptr) || + (vni >> 24) || vni == ULONG_MAX) + invarg("invalid VNI\n", *argv); + } else if (strcmp(*argv, "self") == 0) { + req.ndm.ndm_flags |= NTF_SELF; + } else if (matches(*argv, "master") == 0) { + req.ndm.ndm_flags |= NTF_MASTER; + } else if (matches(*argv, "vlan") == 0) { + if (vlan >= 0) + duparg2("vlan", *argv); + NEXT_ARG(); + vlan = atoi(*argv); + } else if (matches(*argv, "dynamic") == 0) { + filter_dynamic = 1; + } else { + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + + if (matches(*argv, "help") == 0) + usage(); + if (addr) + duparg2("to", *argv); + addr = *argv; + } + argc--; argv++; + } + + if ((d == NULL && br == NULL) || addr == NULL) { + fprintf(stderr, "Device or master and address are required arguments.\n"); + return -1; + } + + if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + abuf, abuf+1, abuf+2, + abuf+3, abuf+4, abuf+5) != 6) { + fprintf(stderr, "Invalid mac address %s\n", addr); + return -1; + } + + addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN); + + if (vlan >= 0) + addattr16(&req.n, sizeof(req), NDA_VLAN, vlan); + + if (vni != ~0) + addattr32(&req.n, sizeof(req), NDA_VNI, vni); + + if (d) { + req.ndm.ndm_ifindex = ll_name_to_index(d); + if (!req.ndm.ndm_ifindex) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + } + + if (br) { + br_ifindex = ll_name_to_index(br); + if (!br_ifindex) { + fprintf(stderr, "Cannot find bridge device \"%s\"\n", br); + return -1; + } + addattr32(&req.n, sizeof(req), NDA_MASTER, br_ifindex); + } + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + /* + * Initialize a json_writer and open an array object + * if -json was specified. + */ + new_json_obj(json); + if (print_fdb(answer, stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + return -1; + } + delete_json_obj(); + + return 0; +} + +int do_fdb(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "append") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_APPEND, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return fdb_get(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return fdb_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return fdb_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv); + exit(-1); +} diff --git a/bridge/link.c b/bridge/link.c new file mode 100644 index 0000000..3bc7af2 --- /dev/null +++ b/bridge/link.c @@ -0,0 +1,586 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json_print.h" +#include "libnetlink.h" +#include "utils.h" +#include "br_common.h" + +static unsigned int filter_index; + +static const char *port_states[] = { + [BR_STATE_DISABLED] = "disabled", + [BR_STATE_LISTENING] = "listening", + [BR_STATE_LEARNING] = "learning", + [BR_STATE_FORWARDING] = "forwarding", + [BR_STATE_BLOCKING] = "blocking", +}; + +static const char *hw_mode[] = { + "VEB", "VEPA" +}; + +static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) +{ + open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<"); + if (flags & IFF_UP && !(flags & IFF_RUNNING)) + print_string(PRINT_ANY, NULL, + flags ? "%s," : "%s", "NO-CARRIER"); + flags &= ~IFF_RUNNING; + +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); + _PF(UP); + _PF(LOWER_UP); + _PF(DORMANT); + _PF(ECHO); +#undef _PF + if (flags) + print_hex(PRINT_ANY, NULL, "%x", flags); + if (mdown) + print_string(PRINT_ANY, NULL, ",%s", "M-DOWN"); + close_json_array(PRINT_ANY, "> "); +} + +static void print_portstate(__u8 state) +{ + if (state <= BR_STATE_BLOCKING) + print_string(PRINT_ANY, "state", + "state %s ", port_states[state]); + else + print_uint(PRINT_ANY, "state", + "state (%d) ", state); +} + +static void print_onoff(FILE *fp, const char *flag, __u8 val) +{ + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, val); + else + fprintf(fp, "%s %s ", flag, val ? "on" : "off"); +} + +static void print_hwmode(__u16 mode) +{ + if (mode >= ARRAY_SIZE(hw_mode)) + print_0xhex(PRINT_ANY, "hwmode", + "hwmode %#llx ", mode); + else + print_string(PRINT_ANY, "hwmode", + "hwmode %s ", hw_mode[mode]); +} + +static void print_protinfo(FILE *fp, struct rtattr *attr) +{ + if (attr->rta_type & NLA_F_NESTED) { + struct rtattr *prtb[IFLA_BRPORT_MAX + 1]; + + parse_rtattr_nested(prtb, IFLA_BRPORT_MAX, attr); + + if (prtb[IFLA_BRPORT_STATE]) + print_portstate(rta_getattr_u8(prtb[IFLA_BRPORT_STATE])); + + if (prtb[IFLA_BRPORT_PRIORITY]) + print_uint(PRINT_ANY, "priority", + "priority %u ", + rta_getattr_u16(prtb[IFLA_BRPORT_PRIORITY])); + + if (prtb[IFLA_BRPORT_COST]) + print_uint(PRINT_ANY, "cost", + "cost %u ", + rta_getattr_u32(prtb[IFLA_BRPORT_COST])); + + if (!show_details) + return; + + if (!is_json_context()) + fprintf(fp, "%s ", _SL_); + + if (prtb[IFLA_BRPORT_MODE]) + print_onoff(fp, "hairpin", + rta_getattr_u8(prtb[IFLA_BRPORT_MODE])); + if (prtb[IFLA_BRPORT_GUARD]) + print_onoff(fp, "guard", + rta_getattr_u8(prtb[IFLA_BRPORT_GUARD])); + if (prtb[IFLA_BRPORT_PROTECT]) + print_onoff(fp, "root_block", + rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT])); + if (prtb[IFLA_BRPORT_FAST_LEAVE]) + print_onoff(fp, "fastleave", + rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE])); + if (prtb[IFLA_BRPORT_LEARNING]) + print_onoff(fp, "learning", + rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING])); + if (prtb[IFLA_BRPORT_LEARNING_SYNC]) + print_onoff(fp, "learning_sync", + rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC])); + if (prtb[IFLA_BRPORT_UNICAST_FLOOD]) + print_onoff(fp, "flood", + rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD])); + if (prtb[IFLA_BRPORT_MCAST_FLOOD]) + print_onoff(fp, "mcast_flood", + rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD])); + if (prtb[IFLA_BRPORT_MCAST_TO_UCAST]) + print_onoff(fp, "mcast_to_unicast", + rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST])); + if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS]) + print_onoff(fp, "neigh_suppress", + rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS])); + if (prtb[IFLA_BRPORT_VLAN_TUNNEL]) + print_onoff(fp, "vlan_tunnel", + rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL])); + + if (prtb[IFLA_BRPORT_BACKUP_PORT]) { + int ifidx; + + ifidx = rta_getattr_u32(prtb[IFLA_BRPORT_BACKUP_PORT]); + print_string(PRINT_ANY, + "backup_port", "backup_port %s ", + ll_index_to_name(ifidx)); + } + + if (prtb[IFLA_BRPORT_ISOLATED]) + print_onoff(fp, "isolated", + rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED])); + } else + print_portstate(rta_getattr_u8(attr)); +} + + +/* + * This is reported by HW devices that have some bridging + * capabilities. + */ +static void print_af_spec(struct rtattr *attr, int ifindex) +{ + struct rtattr *aftb[IFLA_BRIDGE_MAX+1]; + + parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, attr); + + if (aftb[IFLA_BRIDGE_MODE]) + print_hwmode(rta_getattr_u16(aftb[IFLA_BRIDGE_MODE])); + + if (!show_details) + return; + + if (aftb[IFLA_BRIDGE_VLAN_INFO]) + print_vlan_info(aftb[IFLA_BRIDGE_VLAN_INFO], ifindex); +} + +int print_linkinfo(struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + unsigned int m_flag = 0; + int len = n->nlmsg_len; + const char *name; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "Message too short!\n"); + return -1; + } + + if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC)) + return 0; + + if (filter_index && filter_index != ifi->ifi_index) + return 0; + + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED); + + name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]); + if (!name) + return -1; + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELLINK) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index); + m_flag = print_name_and_link("%s: ", name, tb); + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + print_int(PRINT_ANY, + "mtu", "mtu %u ", + rta_getattr_u32(tb[IFLA_MTU])); + + if (tb[IFLA_MASTER]) { + int master = rta_getattr_u32(tb[IFLA_MASTER]); + + print_string(PRINT_ANY, "master", "master %s ", + ll_index_to_name(master)); + } + + if (tb[IFLA_PROTINFO]) + print_protinfo(fp, tb[IFLA_PROTINFO]); + + if (tb[IFLA_AF_SPEC]) + print_af_spec(tb[IFLA_AF_SPEC], ifi->ifi_index); + + print_string(PRINT_FP, NULL, "%s", "\n"); + close_json_object(); + fflush(fp); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: bridge link set dev DEV [ cost COST ] [ priority PRIO ] [ state STATE ]\n" + " [ guard {on | off} ]\n" + " [ hairpin {on | off} ]\n" + " [ fastleave {on | off} ]\n" + " [ root_block {on | off} ]\n" + " [ learning {on | off} ]\n" + " [ learning_sync {on | off} ]\n" + " [ flood {on | off} ]\n" + " [ mcast_flood {on | off} ]\n" + " [ mcast_to_unicast {on | off} ]\n" + " [ neigh_suppress {on | off} ]\n" + " [ vlan_tunnel {on | off} ]\n" + " [ isolated {on | off} ]\n" + " [ hwmode {vepa | veb} ]\n" + " [ backup_port DEVICE ] [ nobackup_port ]\n" + " [ self ] [ master ]\n" + " bridge link show [dev DEV]\n"); + exit(-1); +} + +static bool on_off(char *arg, __s8 *attr, char *val) +{ + if (strcmp(val, "on") == 0) + *attr = 1; + else if (strcmp(val, "off") == 0) + *attr = 0; + else { + fprintf(stderr, + "Error: argument of \"%s\" must be \"on\" or \"off\"\n", + arg); + return false; + } + + return true; +} + +static int brlink_modify(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg ifm; + char buf[512]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_SETLINK, + .ifm.ifi_family = PF_BRIDGE, + }; + char *d = NULL; + int backup_port_idx = -1; + __s8 neigh_suppress = -1; + __s8 learning = -1; + __s8 learning_sync = -1; + __s8 flood = -1; + __s8 vlan_tunnel = -1; + __s8 mcast_flood = -1; + __s8 mcast_to_unicast = -1; + __s8 isolated = -1; + __s8 hairpin = -1; + __s8 bpdu_guard = -1; + __s8 fast_leave = -1; + __s8 root_block = -1; + __u32 cost = 0; + __s16 priority = -1; + __s8 state = -1; + __s16 mode = -1; + __u16 flags = 0; + struct rtattr *nest; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "guard") == 0) { + NEXT_ARG(); + if (!on_off("guard", &bpdu_guard, *argv)) + return -1; + } else if (strcmp(*argv, "hairpin") == 0) { + NEXT_ARG(); + if (!on_off("hairpin", &hairpin, *argv)) + return -1; + } else if (strcmp(*argv, "fastleave") == 0) { + NEXT_ARG(); + if (!on_off("fastleave", &fast_leave, *argv)) + return -1; + } else if (strcmp(*argv, "root_block") == 0) { + NEXT_ARG(); + if (!on_off("root_block", &root_block, *argv)) + return -1; + } else if (strcmp(*argv, "learning") == 0) { + NEXT_ARG(); + if (!on_off("learning", &learning, *argv)) + return -1; + } else if (strcmp(*argv, "learning_sync") == 0) { + NEXT_ARG(); + if (!on_off("learning_sync", &learning_sync, *argv)) + return -1; + } else if (strcmp(*argv, "flood") == 0) { + NEXT_ARG(); + if (!on_off("flood", &flood, *argv)) + return -1; + } else if (strcmp(*argv, "mcast_flood") == 0) { + NEXT_ARG(); + if (!on_off("mcast_flood", &mcast_flood, *argv)) + return -1; + } else if (strcmp(*argv, "mcast_to_unicast") == 0) { + NEXT_ARG(); + if (!on_off("mcast_to_unicast", &mcast_to_unicast, *argv)) + return -1; + } else if (strcmp(*argv, "cost") == 0) { + NEXT_ARG(); + cost = atoi(*argv); + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + priority = atoi(*argv); + } else if (strcmp(*argv, "state") == 0) { + NEXT_ARG(); + char *endptr; + size_t nstates = ARRAY_SIZE(port_states); + + state = strtol(*argv, &endptr, 10); + if (!(**argv != '\0' && *endptr == '\0')) { + for (state = 0; state < nstates; state++) + if (strcasecmp(port_states[state], *argv) == 0) + break; + if (state == nstates) { + fprintf(stderr, + "Error: invalid STP port state\n"); + return -1; + } + } + } else if (strcmp(*argv, "hwmode") == 0) { + NEXT_ARG(); + flags = BRIDGE_FLAGS_SELF; + if (strcmp(*argv, "vepa") == 0) + mode = BRIDGE_MODE_VEPA; + else if (strcmp(*argv, "veb") == 0) + mode = BRIDGE_MODE_VEB; + else { + fprintf(stderr, + "Mode argument must be \"vepa\" or \"veb\".\n"); + return -1; + } + } else if (strcmp(*argv, "self") == 0) { + flags |= BRIDGE_FLAGS_SELF; + } else if (strcmp(*argv, "master") == 0) { + flags |= BRIDGE_FLAGS_MASTER; + } else if (strcmp(*argv, "neigh_suppress") == 0) { + NEXT_ARG(); + if (!on_off("neigh_suppress", &neigh_suppress, + *argv)) + return -1; + } else if (strcmp(*argv, "vlan_tunnel") == 0) { + NEXT_ARG(); + if (!on_off("vlan_tunnel", &vlan_tunnel, + *argv)) + return -1; + } else if (strcmp(*argv, "isolated") == 0) { + NEXT_ARG(); + if (!on_off("isolated", &isolated, *argv)) + return -1; + } else if (strcmp(*argv, "backup_port") == 0) { + NEXT_ARG(); + backup_port_idx = ll_name_to_index(*argv); + if (!backup_port_idx) { + fprintf(stderr, "Error: device %s does not exist\n", + *argv); + return -1; + } + } else if (strcmp(*argv, "nobackup_port") == 0) { + backup_port_idx = 0; + } else { + usage(); + } + argc--; argv++; + } + if (d == NULL) { + fprintf(stderr, "Device is a required argument.\n"); + return -1; + } + + + req.ifm.ifi_index = ll_name_to_index(d); + if (req.ifm.ifi_index == 0) { + fprintf(stderr, "Cannot find bridge device \"%s\"\n", d); + return -1; + } + + /* Nested PROTINFO attribute. Contains: port flags, cost, priority and + * state. + */ + nest = addattr_nest(&req.n, sizeof(req), + IFLA_PROTINFO | NLA_F_NESTED); + /* Flags first */ + if (bpdu_guard >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_GUARD, bpdu_guard); + if (hairpin >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_MODE, hairpin); + if (fast_leave >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_FAST_LEAVE, + fast_leave); + if (root_block >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_PROTECT, root_block); + if (flood >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_UNICAST_FLOOD, flood); + if (mcast_flood >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_FLOOD, + mcast_flood); + if (mcast_to_unicast >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_TO_UCAST, + mcast_to_unicast); + if (learning >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_LEARNING, learning); + if (learning_sync >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_LEARNING_SYNC, + learning_sync); + + if (cost > 0) + addattr32(&req.n, sizeof(req), IFLA_BRPORT_COST, cost); + + if (priority >= 0) + addattr16(&req.n, sizeof(req), IFLA_BRPORT_PRIORITY, priority); + + if (state >= 0) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_STATE, state); + + if (neigh_suppress != -1) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_NEIGH_SUPPRESS, + neigh_suppress); + if (vlan_tunnel != -1) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_VLAN_TUNNEL, + vlan_tunnel); + if (isolated != -1) + addattr8(&req.n, sizeof(req), IFLA_BRPORT_ISOLATED, isolated); + + if (backup_port_idx != -1) + addattr32(&req.n, sizeof(req), IFLA_BRPORT_BACKUP_PORT, + backup_port_idx); + + addattr_nest_end(&req.n, nest); + + /* IFLA_AF_SPEC nested attribute. Contains IFLA_BRIDGE_FLAGS that + * designates master or self operation and IFLA_BRIDGE_MODE + * for hw 'vepa' or 'veb' operation modes. The hwmodes are + * only valid in 'self' mode on some devices so far. + */ + if (mode >= 0 || flags > 0) { + nest = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); + + if (flags > 0) + addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags); + + if (mode >= 0) + addattr16(&req.n, sizeof(req), IFLA_BRIDGE_MODE, mode); + + addattr_nest_end(&req.n, nest); + } + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -1; + + return 0; +} + +static int brlink_show(int argc, char **argv) +{ + char *filter_dev = NULL; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } + argc--; argv++; + } + + if (filter_dev) { + filter_index = ll_name_to_index(filter_dev); + if (!filter_index) + return nodev(filter_dev); + } + + if (show_details) { + if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE, + (compress_vlans ? + RTEXT_FILTER_BRVLAN_COMPRESSED : + RTEXT_FILTER_BRVLAN)) < 0) { + perror("Cannon send dump request"); + exit(1); + } + } else { + if (rtnl_linkdump_req(&rth, PF_BRIDGE) < 0) { + perror("Cannon send dump request"); + exit(1); + } + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_linkinfo, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + delete_json_obj(); + fflush(stdout); + return 0; +} + +int do_link(int argc, char **argv) +{ + ll_init_map(&rth); + if (argc > 0) { + if (matches(*argv, "set") == 0 || + matches(*argv, "change") == 0) + return brlink_modify(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return brlink_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return brlink_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"bridge link help\".\n", *argv); + exit(-1); +} diff --git a/bridge/mdb.c b/bridge/mdb.c new file mode 100644 index 0000000..928ae56 --- /dev/null +++ b/bridge/mdb.c @@ -0,0 +1,462 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Get mdb table with netlink + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libnetlink.h" +#include "br_common.h" +#include "rt_names.h" +#include "utils.h" +#include "json_print.h" + +#ifndef MDBA_RTA +#define MDBA_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg)))) +#endif + +static unsigned int filter_index, filter_vlan; + +static void usage(void) +{ + fprintf(stderr, + "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n" + " bridge mdb {show} [ dev DEV ] [ vid VID ]\n"); + exit(-1); +} + +static bool is_temp_mcast_rtr(__u8 type) +{ + return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP; +} + +static const char *format_timer(__u32 ticks) +{ + struct timeval tv; + static char tbuf[32]; + + __jiffies_to_tv(&tv, ticks); + snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu", + (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_usec / 10000); + + return tbuf; +} + +static void __print_router_port_stats(FILE *f, struct rtattr *pattr) +{ + struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1]; + + parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)), + RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t))); + + if (tb[MDBA_ROUTER_PATTR_TIMER]) { + __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]); + + print_string(PRINT_ANY, "timer", " %s", + format_timer(timer)); + } + + if (tb[MDBA_ROUTER_PATTR_TYPE]) { + __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); + + print_string(PRINT_ANY, "type", " %s", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); + } +} + +static void br_print_router_ports(FILE *f, struct rtattr *attr, + const char *brifname) +{ + int rem = RTA_PAYLOAD(attr); + struct rtattr *i; + + if (is_json_context()) + open_json_array(PRINT_JSON, brifname); + else if (!show_stats) + fprintf(f, "router ports on %s: ", brifname); + + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + uint32_t *port_ifindex = RTA_DATA(i); + const char *port_ifname = ll_index_to_name(*port_ifindex); + + if (is_json_context()) { + open_json_object(NULL); + print_string(PRINT_JSON, "port", NULL, port_ifname); + + if (show_stats) + __print_router_port_stats(f, i); + close_json_object(); + } else if (show_stats) { + fprintf(f, "router ports on %s: %s", + brifname, port_ifname); + + __print_router_port_stats(f, i); + fprintf(f, "\n"); + } else { + fprintf(f, "%s ", port_ifname); + } + } + + if (!show_stats) + print_nl(); + + close_json_array(PRINT_JSON, NULL); +} + +static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e, + struct nlmsghdr *n, struct rtattr **tb) +{ + SPRINT_BUF(abuf); + const char *dev; + const void *src; + int af; + + if (filter_vlan && e->vid != filter_vlan) + return; + + af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; + src = af == AF_INET ? (const void *)&e->addr.u.ip4 : + (const void *)&e->addr.u.ip6; + dev = ll_index_to_name(ifindex); + + open_json_object(NULL); + + print_int(PRINT_JSON, "index", NULL, ifindex); + print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev); + print_string(PRINT_ANY, "port", " port %s", + ll_index_to_name(e->ifindex)); + + print_color_string(PRINT_ANY, ifa_family_color(af), + "grp", " grp %s", + inet_ntop(af, src, abuf, sizeof(abuf))); + + print_string(PRINT_ANY, "state", " %s", + (e->state & MDB_PERMANENT) ? "permanent" : "temp"); + + open_json_array(PRINT_JSON, "flags"); + if (e->flags & MDB_FLAGS_OFFLOAD) + print_string(PRINT_ANY, NULL, " %s", "offload"); + close_json_array(PRINT_JSON, NULL); + + if (e->vid) + print_uint(PRINT_ANY, "vid", " vid %u", e->vid); + + if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { + __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]); + + print_string(PRINT_ANY, "timer", " %s", + format_timer(timer)); + } + + print_nl(); + close_json_object(); +} + +static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, + struct nlmsghdr *n) +{ + struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1]; + struct br_mdb_entry *e; + struct rtattr *i; + int rem; + + rem = RTA_PAYLOAD(attr); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + e = RTA_DATA(i); + parse_rtattr(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)), + RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e))); + print_mdb_entry(f, ifindex, e, n, etb); + } +} + +static void print_mdb_entries(FILE *fp, struct nlmsghdr *n, + int ifindex, struct rtattr *mdb) +{ + int rem = RTA_PAYLOAD(mdb); + struct rtattr *i; + + for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) + br_print_mdb_entry(fp, ifindex, i, n); +} + +static void print_router_entries(FILE *fp, struct nlmsghdr *n, + int ifindex, struct rtattr *router) +{ + const char *brifname = ll_index_to_name(ifindex); + + if (n->nlmsg_type == RTM_GETMDB) { + if (show_details) + br_print_router_ports(fp, router, brifname); + } else { + struct rtattr *i = RTA_DATA(router); + uint32_t *port_ifindex = RTA_DATA(i); + const char *port_name = ll_index_to_name(*port_ifindex); + + if (is_json_context()) { + open_json_array(PRINT_JSON, brifname); + open_json_object(NULL); + + print_string(PRINT_JSON, "port", NULL, + port_name); + close_json_object(); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(fp, "router port dev %s master %s\n", + port_name, brifname); + } + } +} + +static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb) +{ + struct br_port_msg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + + if (n->nlmsg_type != RTM_GETMDB && + n->nlmsg_type != RTM_NEWMDB && + n->nlmsg_type != RTM_DELMDB) { + fprintf(stderr, + "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter_index && filter_index != r->ifindex) + return 0; + + parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + return 1; +} + +static int print_mdbs(struct nlmsghdr *n, void *arg) +{ + struct br_port_msg *r = NLMSG_DATA(n); + struct rtattr *tb[MDBA_MAX+1]; + FILE *fp = arg; + int ret; + + ret = __parse_mdb_nlmsg(n, tb); + if (ret != 1) + return ret; + + if (tb[MDBA_MDB]) + print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]); + + return 0; +} + +static int print_rtrs(struct nlmsghdr *n, void *arg) +{ + struct br_port_msg *r = NLMSG_DATA(n); + struct rtattr *tb[MDBA_MAX+1]; + FILE *fp = arg; + int ret; + + ret = __parse_mdb_nlmsg(n, tb); + if (ret != 1) + return ret; + + if (tb[MDBA_ROUTER]) + print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]); + + return 0; +} + +int print_mdb_mon(struct nlmsghdr *n, void *arg) +{ + struct br_port_msg *r = NLMSG_DATA(n); + struct rtattr *tb[MDBA_MAX+1]; + FILE *fp = arg; + int ret; + + ret = __parse_mdb_nlmsg(n, tb); + if (ret != 1) + return ret; + + if (n->nlmsg_type == RTM_DELMDB) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[MDBA_MDB]) + print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]); + + if (tb[MDBA_ROUTER]) + print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]); + + return 0; +} + +static int mdb_show(int argc, char **argv) +{ + char *filter_dev = NULL; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } else if (strcmp(*argv, "vid") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vid", *argv); + filter_vlan = atoi(*argv); + } + argc--; argv++; + } + + if (filter_dev) { + filter_index = ll_name_to_index(filter_dev); + if (!filter_index) + return nodev(filter_dev); + } + + new_json_obj(json); + open_json_object(NULL); + + /* get mdb entries */ + if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) { + perror("Cannot send dump request"); + return -1; + } + + open_json_array(PRINT_JSON, "mdb"); + if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + close_json_array(PRINT_JSON, NULL); + + /* get router ports */ + if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) { + perror("Cannot send dump request"); + return -1; + } + + open_json_object("router"); + if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + close_json_object(); + + close_json_object(); + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int mdb_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct br_port_msg bpm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .bpm.family = PF_BRIDGE, + }; + struct br_mdb_entry entry = {}; + char *d = NULL, *p = NULL, *grp = NULL; + short vid = 0; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "grp") == 0) { + NEXT_ARG(); + grp = *argv; + } else if (strcmp(*argv, "port") == 0) { + NEXT_ARG(); + p = *argv; + } else if (strcmp(*argv, "permanent") == 0) { + if (cmd == RTM_NEWMDB) + entry.state |= MDB_PERMANENT; + } else if (strcmp(*argv, "temp") == 0) { + ;/* nothing */ + } else if (strcmp(*argv, "vid") == 0) { + NEXT_ARG(); + vid = atoi(*argv); + } else { + if (matches(*argv, "help") == 0) + usage(); + } + argc--; argv++; + } + + if (d == NULL || grp == NULL || p == NULL) { + fprintf(stderr, "Device, group address and port name are required arguments.\n"); + return -1; + } + + req.bpm.ifindex = ll_name_to_index(d); + if (!req.bpm.ifindex) + return nodev(d); + + entry.ifindex = ll_name_to_index(p); + if (!entry.ifindex) + return nodev(p); + + if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) { + if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) { + fprintf(stderr, "Invalid address \"%s\"\n", grp); + return -1; + } else + entry.addr.proto = htons(ETH_P_IPV6); + } else + entry.addr.proto = htons(ETH_P_IP); + + entry.vid = vid; + addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry)); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -1; + + return 0; +} + +int do_mdb(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1); + + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return mdb_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return mdb_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv); + exit(-1); +} diff --git a/bridge/monitor.c b/bridge/monitor.c new file mode 100644 index 0000000..08439a6 --- /dev/null +++ b/bridge/monitor.c @@ -0,0 +1,143 @@ +/* + * brmonitor.c "bridge monitor" + * + * 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. + * + * Authors: Stephen Hemminger + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "br_common.h" + + +static void usage(void) __attribute__((noreturn)); +static int prefix_banner; + +static void usage(void) +{ + fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | all]\n"); + exit(-1); +} + +static int accept_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + + if (timestamp) + print_timestamp(fp); + + switch (n->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + if (prefix_banner) + fprintf(fp, "[LINK]"); + + return print_linkinfo(n, arg); + + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + if (prefix_banner) + fprintf(fp, "[NEIGH]"); + return print_fdb(n, arg); + + case RTM_NEWMDB: + case RTM_DELMDB: + if (prefix_banner) + fprintf(fp, "[MDB]"); + return print_mdb_mon(n, arg); + + case NLMSG_TSTAMP: + print_nlmsg_timestamp(fp, n); + return 0; + + default: + return 0; + } +} + +int do_monitor(int argc, char **argv) +{ + char *file = NULL; + unsigned int groups = ~RTMGRP_TC; + int llink = 0; + int lneigh = 0; + int lmdb = 0; + + rtnl_close(&rth); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (matches(*argv, "link") == 0) { + llink = 1; + groups = 0; + } else if (matches(*argv, "fdb") == 0) { + lneigh = 1; + groups = 0; + } else if (matches(*argv, "mdb") == 0) { + lmdb = 1; + groups = 0; + } else if (strcmp(*argv, "all") == 0) { + groups = ~RTMGRP_TC; + prefix_banner = 1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"bridge monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + if (llink) + groups |= nl_mgrp(RTNLGRP_LINK); + + if (lneigh) { + groups |= nl_mgrp(RTNLGRP_NEIGH); + } + + if (lmdb) { + groups |= nl_mgrp(RTNLGRP_MDB); + } + + if (file) { + FILE *fp; + int err; + + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + err = rtnl_from_file(fp, accept_msg, stdout); + fclose(fp); + return err; + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + ll_init_map(&rth); + + if (rtnl_listen(&rth, accept_msg, stdout) < 0) + exit(2); + + return 0; +} diff --git a/bridge/vlan.c b/bridge/vlan.c new file mode 100644 index 0000000..0d142bc --- /dev/null +++ b/bridge/vlan.c @@ -0,0 +1,678 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json_print.h" +#include "libnetlink.h" +#include "br_common.h" +#include "utils.h" + +static unsigned int filter_index, filter_vlan; + +enum vlan_show_subject { + VLAN_SHOW_VLAN, + VLAN_SHOW_TUNNELINFO, +}; + +#define VLAN_ID_LEN 9 + +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +static void usage(void) +{ + fprintf(stderr, + "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n" + " [ pvid ] [ untagged ]\n" + " [ self ] [ master ]\n" + " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n" + " bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"); + exit(-1); +} + +static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start, + __u32 *tun_id_end) +{ + char **argv = *argvp; + int argc = *argcp; + char *t; + + NEXT_ARG(); + if (!matches(*argv, "id")) { + NEXT_ARG(); + t = strchr(*argv, '-'); + if (t) { + *t = '\0'; + if (get_u32(tun_id_start, *argv, 0) || + *tun_id_start >= 1u << 24) + invarg("invalid tun id", *argv); + if (get_u32(tun_id_end, t + 1, 0) || + *tun_id_end >= 1u << 24) + invarg("invalid tun id", *argv); + + } else { + if (get_u32(tun_id_start, *argv, 0) || + *tun_id_start >= 1u << 24) + invarg("invalid tun id", *argv); + } + } else { + invarg("tunnel id expected", *argv); + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int add_tunnel_info(struct nlmsghdr *n, int reqsize, + __u16 vid, __u32 tun_id, __u16 flags) +{ + struct rtattr *tinfo; + + tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO); + addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id); + addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid); + addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags); + + addattr_nest_end(n, tinfo); + + return 0; +} + +static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize, + __u16 vid_start, int16_t vid_end, + __u32 tun_id_start, __u32 tun_id_end) +{ + if (vid_end != -1 && (vid_end - vid_start) > 0) { + add_tunnel_info(n, reqsize, vid_start, tun_id_start, + BRIDGE_VLAN_INFO_RANGE_BEGIN); + + add_tunnel_info(n, reqsize, vid_end, tun_id_end, + BRIDGE_VLAN_INFO_RANGE_END); + } else { + add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0); + } + + return 0; +} + +static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start, + int16_t vid_end, __u16 flags) +{ + struct bridge_vlan_info vinfo = {}; + + vinfo.flags = flags; + vinfo.vid = vid_start; + if (vid_end != -1) { + /* send vlan range start */ + addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, + sizeof(vinfo)); + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + + /* Now send the vlan range end */ + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; + vinfo.vid = vid_end; + addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, + sizeof(vinfo)); + } else { + addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, + sizeof(vinfo)); + } + + return 0; +} + +static int vlan_modify(int cmd, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg ifm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = cmd, + .ifm.ifi_family = PF_BRIDGE, + }; + char *d = NULL; + short vid = -1; + short vid_end = -1; + struct rtattr *afspec; + struct bridge_vlan_info vinfo = {}; + bool tunnel_info_set = false; + unsigned short flags = 0; + __u32 tun_id_start = 0; + __u32 tun_id_end = 0; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "vid") == 0) { + char *p; + + NEXT_ARG(); + p = strchr(*argv, '-'); + if (p) { + *p = '\0'; + p++; + vid = atoi(*argv); + vid_end = atoi(p); + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; + } else { + vid = atoi(*argv); + } + } else if (strcmp(*argv, "self") == 0) { + flags |= BRIDGE_FLAGS_SELF; + } else if (strcmp(*argv, "master") == 0) { + flags |= BRIDGE_FLAGS_MASTER; + } else if (strcmp(*argv, "pvid") == 0) { + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + } else if (strcmp(*argv, "untagged") == 0) { + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + } else if (strcmp(*argv, "tunnel_info") == 0) { + if (parse_tunnel_info(&argc, &argv, + &tun_id_start, + &tun_id_end)) + return -1; + tunnel_info_set = true; + } else { + if (matches(*argv, "help") == 0) + NEXT_ARG(); + } + argc--; argv++; + } + + if (d == NULL || vid == -1) { + fprintf(stderr, "Device and VLAN ID are required arguments.\n"); + return -1; + } + + req.ifm.ifi_index = ll_name_to_index(d); + if (req.ifm.ifi_index == 0) { + fprintf(stderr, "Cannot find bridge device \"%s\"\n", d); + return -1; + } + + if (vid >= 4096) { + fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid); + return -1; + } + + if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) { + fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n", + vid, vid_end); + return -1; + } + if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) { + fprintf(stderr, + "pvid cannot be configured for a vlan range\n"); + return -1; + } + } + + afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); + + if (flags) + addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags); + + if (tunnel_info_set) + add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end, + tun_id_start, tun_id_end); + else + add_vlan_info_range(&req.n, sizeof(req), vid, vid_end, + vinfo.flags); + + addattr_nest_end(&req.n, afspec); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -1; + + return 0; +} + +/* In order to use this function for both filtering and non-filtering cases + * we need to make it a tristate: + * return -1 - if filtering we've gone over so don't continue + * return 0 - skip entry and continue (applies to range start or to entries + * which are less than filter_vlan) + * return 1 - print the entry and continue + */ +static int filter_vlan_check(__u16 vid, __u16 flags) +{ + /* if we're filtering we should stop on the first greater entry */ + if (filter_vlan && vid > filter_vlan && + !(flags & BRIDGE_VLAN_INFO_RANGE_END)) + return -1; + if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) || + vid < filter_vlan) + return 0; + + return 1; +} + +static void open_vlan_port(int ifi_index, enum vlan_show_subject subject) +{ + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", + "%-" __stringify(IFNAMSIZ) "s ", + ll_index_to_name(ifi_index)); + open_json_array(PRINT_JSON, + subject == VLAN_SHOW_VLAN ? "vlans": "tunnels"); +} + +static void close_vlan_port(void) +{ + close_json_array(PRINT_JSON, NULL); + close_json_object(); +} + +static unsigned int print_range(const char *name, __u32 start, __u32 id) +{ + char end[64]; + int width; + + snprintf(end, sizeof(end), "%sEnd", name); + + width = print_uint(PRINT_ANY, name, "%u", start); + if (start != id) + width += print_uint(PRINT_ANY, end, "-%u", id); + + return width; +} + +static void print_vlan_tunnel_info(struct rtattr *tb, int ifindex) +{ + struct rtattr *i, *list = tb; + int rem = RTA_PAYLOAD(list); + __u16 last_vid_start = 0; + __u32 last_tunid_start = 0; + bool opened = false; + + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1]; + __u32 tunnel_id = 0; + __u16 tunnel_vid = 0; + __u16 tunnel_flags = 0; + unsigned int width; + int vcheck_ret; + + if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO) + continue; + + parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, + RTA_DATA(i), RTA_PAYLOAD(i)); + + if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]) + tunnel_vid = + rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]); + else + continue; + + if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]) + tunnel_id = + rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]); + + if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]) + tunnel_flags = + rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]); + + if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) { + last_vid_start = tunnel_vid; + last_tunid_start = tunnel_id; + } + + vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags); + if (vcheck_ret == -1) + break; + else if (vcheck_ret == 0) + continue; + + if (!opened) { + open_vlan_port(ifindex, VLAN_SHOW_TUNNELINFO); + opened = true; + } else { + print_string(PRINT_FP, NULL, + "%-" __stringify(IFNAMSIZ) "s ", ""); + } + + open_json_object(NULL); + width = print_range("vlan", last_vid_start, tunnel_vid); + if (width <= VLAN_ID_LEN) { + char buf[VLAN_ID_LEN + 1]; + + snprintf(buf, sizeof(buf), "%-*s", + VLAN_ID_LEN - width, ""); + print_string(PRINT_FP, NULL, "%s ", buf); + } else { + fprintf(stderr, "BUG: vlan range too wide, %u\n", + width); + } + print_range("tunid", last_tunid_start, tunnel_id); + close_json_object(); + print_nl(); + } + + if (opened) + close_vlan_port(); +} + +static int print_vlan(struct nlmsghdr *n, void *arg) +{ + enum vlan_show_subject *subject = arg; + struct ifinfomsg *ifm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK) { + fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*ifm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (ifm->ifi_family != AF_BRIDGE) + return 0; + + if (filter_index && filter_index != ifm->ifi_index) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len); + if (!tb[IFLA_AF_SPEC]) + return 0; + + switch (*subject) { + case VLAN_SHOW_VLAN: + print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index); + break; + case VLAN_SHOW_TUNNELINFO: + print_vlan_tunnel_info(tb[IFLA_AF_SPEC], ifm->ifi_index); + break; + } + + return 0; +} + +static void print_vlan_flags(__u16 flags) +{ + if (flags == 0) + return; + + open_json_array(PRINT_JSON, "flags"); + if (flags & BRIDGE_VLAN_INFO_PVID) + print_string(PRINT_ANY, NULL, " %s", "PVID"); + + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) + print_string(PRINT_ANY, NULL, " %s", "Egress Untagged"); + close_json_array(PRINT_JSON, NULL); +} + +static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) +{ + open_json_object(NULL); + + print_hu(PRINT_ANY, "vid", "%hu", vstats->vid); + print_vlan_flags(vstats->flags); + print_nl(); + + print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); + print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes", + vstats->rx_bytes); + print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n", + vstats->rx_packets); + + print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); + print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes", + vstats->tx_bytes); + print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n", + vstats->tx_packets); + + close_json_object(); +} + +static void print_vlan_stats_attr(struct rtattr *attr, int ifindex) +{ + struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; + struct rtattr *i, *list; + bool found_vlan = false; + int rem; + + parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) + return; + + list = brtb[LINK_XSTATS_TYPE_BRIDGE]; + rem = RTA_PAYLOAD(list); + + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + const struct bridge_vlan_xstats *vstats = RTA_DATA(i); + + if (i->rta_type != BRIDGE_XSTATS_VLAN) + continue; + + if (filter_vlan && filter_vlan != vstats->vid) + continue; + + /* skip pure port entries, they'll be dumped via the slave stats call */ + if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) && + !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY)) + continue; + + /* found vlan stats, first time print the interface name */ + if (!found_vlan) { + open_vlan_port(ifindex, VLAN_SHOW_VLAN); + found_vlan = true; + } else { + print_string(PRINT_FP, NULL, + "%-" __stringify(IFNAMSIZ) "s ", ""); + } + print_one_vlan_stats(vstats); + } + + /* vlan_port is opened only if there are any vlan stats */ + if (found_vlan) + close_vlan_port(); +} + +static int print_vlan_stats(struct nlmsghdr *n, void *arg) +{ + struct if_stats_msg *ifsm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_STATS_MAX+1]; + int len = n->nlmsg_len; + FILE *fp = arg; + + len -= NLMSG_LENGTH(sizeof(*ifsm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter_index && filter_index != ifsm->ifindex) + return 0; + + parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); + + /* We have to check if any of the two attrs are usable */ + if (tb[IFLA_STATS_LINK_XSTATS]) + print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS], + ifsm->ifindex); + + if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) + print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE], + ifsm->ifindex); + + fflush(fp); + return 0; +} + +static int vlan_show(int argc, char **argv, int subject) +{ + char *filter_dev = NULL; + int ret = 0; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } else if (strcmp(*argv, "vid") == 0) { + NEXT_ARG(); + if (filter_vlan) + duparg("vid", *argv); + filter_vlan = atoi(*argv); + } + argc--; argv++; + } + + if (filter_dev) { + filter_index = ll_name_to_index(filter_dev); + if (!filter_index) + return nodev(filter_dev); + } + + new_json_obj(json); + + if (!show_stats) { + if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE, + (compress_vlans ? + RTEXT_FILTER_BRVLAN_COMPRESSED : + RTEXT_FILTER_BRVLAN)) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (!is_json_context()) { + printf("%-" __stringify(IFNAMSIZ) "s %-" + __stringify(VLAN_ID_LEN) "s", "port", + "vlan-id"); + if (subject == VLAN_SHOW_TUNNELINFO) + printf(" tunnel-id"); + printf("\n"); + } + + ret = rtnl_dump_filter(&rth, print_vlan, &subject); + if (ret < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } else { + __u32 filt_mask; + + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS); + if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (!is_json_context()) + printf("%-" __stringify(IFNAMSIZ) "s vlan-id\n", + "port"); + + if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE); + if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { + perror("Cannot send slave dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + delete_json_obj(); + fflush(stdout); + return 0; +} + +void print_vlan_info(struct rtattr *tb, int ifindex) +{ + struct rtattr *i, *list = tb; + int rem = RTA_PAYLOAD(list); + __u16 last_vid_start = 0; + bool opened = false; + + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + struct bridge_vlan_info *vinfo; + int vcheck_ret; + + if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) + continue; + + vinfo = RTA_DATA(i); + + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + last_vid_start = vinfo->vid; + vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags); + if (vcheck_ret == -1) + break; + else if (vcheck_ret == 0) + continue; + + if (!opened) { + open_vlan_port(ifindex, VLAN_SHOW_VLAN); + opened = true; + } else { + print_string(PRINT_FP, NULL, "%-" + __stringify(IFNAMSIZ) "s ", ""); + } + + open_json_object(NULL); + print_range("vlan", last_vid_start, vinfo->vid); + + print_vlan_flags(vinfo->flags); + close_json_object(); + print_nl(); + } + + if (opened) + close_vlan_port(); +} + +int do_vlan(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return vlan_modify(RTM_SETLINK, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return vlan_modify(RTM_DELLINK, argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return vlan_show(argc-1, argv+1, VLAN_SHOW_VLAN); + if (matches(*argv, "tunnelshow") == 0) { + return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO); + } + if (matches(*argv, "help") == 0) + usage(); + } else { + return vlan_show(0, NULL, VLAN_SHOW_VLAN); + } + + fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv); + exit(-1); +} diff --git a/configure b/configure new file mode 100755 index 0000000..307912a --- /dev/null +++ b/configure @@ -0,0 +1,405 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# This is not an autoconf generated configure +# +INCLUDE=${1:-"$PWD/include"} + +# Output file which is input to Makefile +CONFIG=config.mk + +# Make a temp directory in build tree. +TMPDIR=$(mktemp -d config.XXXXXX) +trap 'status=$?; rm -rf $TMPDIR; exit $status' EXIT HUP INT QUIT TERM + +check_toolchain() +{ + : ${PKG_CONFIG:=pkg-config} + : ${AR=ar} + : ${CC=gcc} + : ${YACC=bison} + echo "PKG_CONFIG:=${PKG_CONFIG}" >>$CONFIG + echo "AR:=${AR}" >>$CONFIG + echo "CC:=${CC}" >>$CONFIG + echo "YACC:=${YACC}" >>$CONFIG +} + +check_atm() +{ + cat >$TMPDIR/atmtest.c < +int main(int argc, char **argv) { + struct atm_qos qos; + (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); + return 0; +} +EOF + + if $CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1; then + echo "TC_CONFIG_ATM:=y" >>$CONFIG + echo yes + else + echo no + fi + rm -f $TMPDIR/atmtest.c $TMPDIR/atmtest +} + +check_xtables() +{ + if ! ${PKG_CONFIG} xtables --exists; then + echo "TC_CONFIG_NO_XT:=y" >>$CONFIG + fi +} + +check_xt() +{ + #check if we have xtables from iptables >= 1.4.5. + cat >$TMPDIR/ipttest.c < +#include +static struct xtables_globals test_globals = { + .option_offset = 0, + .program_name = "tc-ipt", + .program_version = XTABLES_VERSION, + .orig_opts = NULL, + .opts = NULL, + .exit_err = NULL, +}; + +int main(int argc, char **argv) +{ + xtables_init_all(&test_globals, NFPROTO_IPV4); + return 0; +} +EOF + + if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL \ + $(${PKG_CONFIG} xtables --cflags --libs) -ldl >/dev/null 2>&1; then + echo "TC_CONFIG_XT:=y" >>$CONFIG + echo "using xtables" + fi + rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest +} + +check_xt_old() +{ + # bail if previous XT checks has already succeeded. + grep -q TC_CONFIG_XT $CONFIG && return + + #check if we don't need our internal header .. + cat >$TMPDIR/ipttest.c < +char *lib_dir; +unsigned int global_option_offset = 0; +const char *program_version = XTABLES_VERSION; +const char *program_name = "tc-ipt"; +struct afinfo afinfo = { + .libprefix = "libxt_", +}; + +void exit_error(enum exittype status, const char *msg, ...) +{ +} + +int main(int argc, char **argv) { + + return 0; +} + +EOF + + if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1; then + echo "TC_CONFIG_XT_OLD:=y" >>$CONFIG + echo "using old xtables (no need for xt-internal.h)" + fi + rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest +} + +check_xt_old_internal_h() +{ + # bail if previous XT checks has already succeeded. + grep -q TC_CONFIG_XT $CONFIG && return + + #check if we need our own internal.h + cat >$TMPDIR/ipttest.c < +#include "xt-internal.h" +char *lib_dir; +unsigned int global_option_offset = 0; +const char *program_version = XTABLES_VERSION; +const char *program_name = "tc-ipt"; +struct afinfo afinfo = { + .libprefix = "libxt_", +}; + +void exit_error(enum exittype status, const char *msg, ...) +{ +} + +int main(int argc, char **argv) { + + return 0; +} + +EOF + if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1; then + echo "using old xtables with xt-internal.h" + echo "TC_CONFIG_XT_OLD_H:=y" >>$CONFIG + fi + rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest +} + +check_ipt() +{ + if ! grep TC_CONFIG_XT $CONFIG > /dev/null; then + echo "using iptables" + fi +} + +check_ipt_lib_dir() +{ + IPT_LIB_DIR=$(${PKG_CONFIG} --variable=xtlibdir xtables) + if [ -n "$IPT_LIB_DIR" ]; then + echo $IPT_LIB_DIR + echo "IPT_LIB_DIR:=$IPT_LIB_DIR" >> $CONFIG + return + fi + + for dir in /lib /usr/lib /usr/local/lib; do + for file in "xtables" "iptables"; do + file="$dir/$file/lib*t_*so" + if [ -f $file ]; then + echo ${file%/*} + echo "IPT_LIB_DIR:=${file%/*}" >> $CONFIG + return + fi + done + done + echo "not found!" +} + +check_setns() +{ + cat >$TMPDIR/setnstest.c < +int main(int argc, char **argv) +{ + (void)setns(0,0); + return 0; +} +EOF + if $CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1; then + echo "IP_CONFIG_SETNS:=y" >>$CONFIG + echo "yes" + echo "CFLAGS += -DHAVE_SETNS" >>$CONFIG + else + echo "no" + fi + rm -f $TMPDIR/setnstest.c $TMPDIR/setnstest +} + +check_ipset() +{ + cat >$TMPDIR/ipsettest.c < +#ifndef IP_SET_INVALID +#define IPSET_DIM_MAX 3 +typedef unsigned short ip_set_id_t; +#endif +#include + +struct xt_set_info info; +#if IPSET_PROTOCOL == 6 || IPSET_PROTOCOL == 7 +int main(void) +{ + return IPSET_MAXNAMELEN; +} +#else +#error unknown ipset version +#endif +EOF + + if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1; then + echo "TC_CONFIG_IPSET:=y" >>$CONFIG + echo "yes" + else + echo "no" + fi + rm -f $TMPDIR/ipsettest.c $TMPDIR/ipsettest +} + +check_elf() +{ + if ${PKG_CONFIG} libelf --exists; then + echo "HAVE_ELF:=y" >>$CONFIG + echo "yes" + + echo 'CFLAGS += -DHAVE_ELF' `${PKG_CONFIG} libelf --cflags` >> $CONFIG + echo 'LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG + else + echo "no" + fi +} + +check_selinux() +# SELinux is a compile time option in the ss utility +{ + if ${PKG_CONFIG} libselinux --exists; then + echo "HAVE_SELINUX:=y" >>$CONFIG + echo "yes" + + echo 'LDLIBS +=' `${PKG_CONFIG} --libs libselinux` >>$CONFIG + echo 'CFLAGS += -DHAVE_SELINUX' `${PKG_CONFIG} --cflags libselinux` >>$CONFIG + else + echo "no" + fi +} + +check_mnl() +{ + if ${PKG_CONFIG} libmnl --exists; then + echo "HAVE_MNL:=y" >>$CONFIG + echo "yes" + + echo 'CFLAGS += -DHAVE_LIBMNL' `${PKG_CONFIG} libmnl --cflags` >>$CONFIG + echo 'LDLIBS +=' `${PKG_CONFIG} libmnl --libs` >> $CONFIG + else + echo "no" + fi +} + +check_berkeley_db() +{ + cat >$TMPDIR/dbtest.c < +#include +#include +int main(int argc, char **argv) { + dbopen("/tmp/xxx_test_db.db", O_CREAT|O_RDWR, 0644, DB_HASH, NULL); + return 0; +} +EOF + if $CC -I$INCLUDE -o $TMPDIR/dbtest $TMPDIR/dbtest.c -ldb >/dev/null 2>&1; then + echo "HAVE_BERKELEY_DB:=y" >>$CONFIG + echo "yes" + else + echo "no" + fi + rm -f $TMPDIR/dbtest.c $TMPDIR/dbtest +} + +check_strlcpy() +{ + cat >$TMPDIR/strtest.c < +int main(int argc, char **argv) { + char dst[10]; + strlcpy(dst, "test", sizeof(dst)); + return 0; +} +EOF + if $CC -I$INCLUDE -o $TMPDIR/strtest $TMPDIR/strtest.c >/dev/null 2>&1; then + echo "no" + else + if ${PKG_CONFIG} libbsd --exists; then + echo 'CFLAGS += -DHAVE_LIBBSD' `${PKG_CONFIG} libbsd --cflags` >>$CONFIG + echo 'LDLIBS +=' `${PKG_CONFIG} libbsd --libs` >> $CONFIG + echo "no" + else + echo 'CFLAGS += -DNEED_STRLCPY' >>$CONFIG + echo "yes" + fi + fi + rm -f $TMPDIR/strtest.c $TMPDIR/strtest +} + +check_cap() +{ + if ${PKG_CONFIG} libcap --exists; then + echo "HAVE_CAP:=y" >>$CONFIG + echo "yes" + + echo 'CFLAGS += -DHAVE_LIBCAP' `${PKG_CONFIG} libcap --cflags` >>$CONFIG + echo 'LDLIBS +=' `${PKG_CONFIG} libcap --libs` >> $CONFIG + else + echo "no" + fi +} + +quiet_config() +{ + cat <$CONFIG +quiet_config >> $CONFIG + +check_toolchain + +echo "TC schedulers" + +echo -n " ATM " +check_atm + +check_xtables +if ! grep -q TC_CONFIG_NO_XT $CONFIG; then + echo -n " IPT " + check_xt + check_xt_old + check_xt_old_internal_h + check_ipt + + echo -n " IPSET " + check_ipset +fi + +echo +if ! grep -q TC_CONFIG_NO_XT $CONFIG; then + echo -n "iptables modules directory: " + check_ipt_lib_dir +fi + +echo -n "libc has setns: " +check_setns + +echo -n "SELinux support: " +check_selinux + +echo -n "ELF support: " +check_elf + +echo -n "libmnl support: " +check_mnl + +echo -n "Berkeley DB: " +check_berkeley_db + +echo -n "need for strlcpy: " +check_strlcpy + +echo -n "libcap support: " +check_cap + +echo >> $CONFIG +echo "%.o: %.c" >> $CONFIG +echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CPPFLAGS) -c -o $@ $<' >> $CONFIG diff --git a/devlink/.gitignore b/devlink/.gitignore new file mode 100644 index 0000000..08d175f --- /dev/null +++ b/devlink/.gitignore @@ -0,0 +1 @@ +devlink diff --git a/devlink/Makefile b/devlink/Makefile new file mode 100644 index 0000000..7da7d1f --- /dev/null +++ b/devlink/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../config.mk + +TARGETS := + +ifeq ($(HAVE_MNL),y) + +DEVLINKOBJ = devlink.o mnlg.o +TARGETS += devlink + +endif + +all: $(TARGETS) $(LIBS) + +devlink: $(DEVLINKOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + for i in $(TARGETS); \ + do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \ + done + +clean: + rm -f $(DEVLINKOBJ) $(TARGETS) diff --git a/devlink/devlink.c b/devlink/devlink.c new file mode 100644 index 0000000..007677a --- /dev/null +++ b/devlink/devlink.c @@ -0,0 +1,7884 @@ +/* + * devlink.c Devlink tool + * + * 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. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _LINUX_SYSINFO_H /* avoid collision with musl header */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "list.h" +#include "mnlg.h" +#include "json_print.h" +#include "utils.h" +#include "namespace.h" + +#define ESWITCH_MODE_LEGACY "legacy" +#define ESWITCH_MODE_SWITCHDEV "switchdev" +#define ESWITCH_INLINE_MODE_NONE "none" +#define ESWITCH_INLINE_MODE_LINK "link" +#define ESWITCH_INLINE_MODE_NETWORK "network" +#define ESWITCH_INLINE_MODE_TRANSPORT "transport" + +#define ESWITCH_ENCAP_MODE_NONE "none" +#define ESWITCH_ENCAP_MODE_BASIC "basic" + +#define PARAM_CMODE_RUNTIME_STR "runtime" +#define PARAM_CMODE_DRIVERINIT_STR "driverinit" +#define PARAM_CMODE_PERMANENT_STR "permanent" +#define DL_ARGS_REQUIRED_MAX_ERR_LEN 80 + +#define HEALTH_REPORTER_STATE_HEALTHY_STR "healthy" +#define HEALTH_REPORTER_STATE_ERROR_STR "error" +#define HEALTH_REPORTER_TIMESTAMP_FMT_LEN 80 + +static int g_new_line_count; +static int g_indent_level; +static bool g_indent_newline; + +#define INDENT_STR_STEP 2 +#define INDENT_STR_MAXLEN 32 +static char g_indent_str[INDENT_STR_MAXLEN + 1] = ""; + +static void __attribute__((format(printf, 1, 2))) +pr_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void __attribute__((format(printf, 1, 2))) +pr_out(const char *fmt, ...) +{ + va_list ap; + + if (g_indent_newline) { + printf("%s", g_indent_str); + g_indent_newline = false; + } + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + g_new_line_count = 0; +} + +static void __attribute__((format(printf, 2, 3))) +pr_out_sp(unsigned int num, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vprintf(fmt, ap); + va_end(ap); + + if (ret < num) + printf("%*s", num - ret, ""); + g_new_line_count = 0; \ +} + +static void __attribute__((format(printf, 1, 2))) +pr_out_tty(const char *fmt, ...) +{ + va_list ap; + + if (!isatty(STDOUT_FILENO)) + return; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static void __pr_out_indent_inc(void) +{ + if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN) + return; + g_indent_level += INDENT_STR_STEP; + memset(g_indent_str, ' ', sizeof(g_indent_str)); + g_indent_str[g_indent_level] = '\0'; +} + +static void __pr_out_indent_dec(void) +{ + if (g_indent_level - INDENT_STR_STEP < 0) + return; + g_indent_level -= INDENT_STR_STEP; + g_indent_str[g_indent_level] = '\0'; +} + +static void __pr_out_newline(void) +{ + if (g_new_line_count < 1) { + pr_out("\n"); + g_indent_newline = true; + } + g_new_line_count++; +} + +static int _mnlg_socket_recv_run(struct mnlg_socket *nlg, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = mnlg_socket_recv_run(nlg, data_cb, data); + if (err < 0) { + pr_err("devlink answers: %s\n", strerror(errno)); + return -errno; + } + return 0; +} + +static void dummy_signal_handler(int signum) +{ +} + +static int _mnlg_socket_recv_run_intr(struct mnlg_socket *nlg, + mnl_cb_t data_cb, void *data) +{ + struct sigaction act, oact; + int err; + + act.sa_handler = dummy_signal_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER; + + sigaction(SIGINT, &act, &oact); + err = mnlg_socket_recv_run(nlg, data_cb, data); + sigaction(SIGINT, &oact, NULL); + if (err < 0 && errno != EINTR) { + pr_err("devlink answers: %s\n", strerror(errno)); + return -errno; + } + return 0; +} + +static int _mnlg_socket_send(struct mnlg_socket *nlg, + const struct nlmsghdr *nlh) +{ + int err; + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) { + pr_err("Failed to call mnlg_socket_send\n"); + return -errno; + } + return 0; +} + +static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg, + const struct nlmsghdr *nlh, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = _mnlg_socket_send(nlg, nlh); + if (err) + return err; + return _mnlg_socket_recv_run(nlg, data_cb, data); +} + +static int _mnlg_socket_group_add(struct mnlg_socket *nlg, + const char *group_name) +{ + int err; + + err = mnlg_socket_group_add(nlg, group_name); + if (err < 0) { + pr_err("Failed to call mnlg_socket_group_add\n"); + return -errno; + } + return 0; +} + +struct ifname_map { + struct list_head list; + char *bus_name; + char *dev_name; + uint32_t port_index; + char *ifname; +}; + +static struct ifname_map *ifname_map_alloc(const char *bus_name, + const char *dev_name, + uint32_t port_index, + const char *ifname) +{ + struct ifname_map *ifname_map; + + ifname_map = calloc(1, sizeof(*ifname_map)); + if (!ifname_map) + return NULL; + ifname_map->bus_name = strdup(bus_name); + ifname_map->dev_name = strdup(dev_name); + ifname_map->port_index = port_index; + ifname_map->ifname = strdup(ifname); + if (!ifname_map->bus_name || !ifname_map->dev_name || + !ifname_map->ifname) { + free(ifname_map->ifname); + free(ifname_map->dev_name); + free(ifname_map->bus_name); + free(ifname_map); + return NULL; + } + return ifname_map; +} + +static void ifname_map_free(struct ifname_map *ifname_map) +{ + free(ifname_map->ifname); + free(ifname_map->dev_name); + free(ifname_map->bus_name); + free(ifname_map); +} + +#define DL_OPT_HANDLE BIT(0) +#define DL_OPT_HANDLEP BIT(1) +#define DL_OPT_PORT_TYPE BIT(2) +#define DL_OPT_PORT_COUNT BIT(3) +#define DL_OPT_SB BIT(4) +#define DL_OPT_SB_POOL BIT(5) +#define DL_OPT_SB_SIZE BIT(6) +#define DL_OPT_SB_TYPE BIT(7) +#define DL_OPT_SB_THTYPE BIT(8) +#define DL_OPT_SB_TH BIT(9) +#define DL_OPT_SB_TC BIT(10) +#define DL_OPT_ESWITCH_MODE BIT(11) +#define DL_OPT_ESWITCH_INLINE_MODE BIT(12) +#define DL_OPT_DPIPE_TABLE_NAME BIT(13) +#define DL_OPT_DPIPE_TABLE_COUNTERS BIT(14) +#define DL_OPT_ESWITCH_ENCAP_MODE BIT(15) +#define DL_OPT_RESOURCE_PATH BIT(16) +#define DL_OPT_RESOURCE_SIZE BIT(17) +#define DL_OPT_PARAM_NAME BIT(18) +#define DL_OPT_PARAM_VALUE BIT(19) +#define DL_OPT_PARAM_CMODE BIT(20) +#define DL_OPT_HANDLE_REGION BIT(21) +#define DL_OPT_REGION_SNAPSHOT_ID BIT(22) +#define DL_OPT_REGION_ADDRESS BIT(23) +#define DL_OPT_REGION_LENGTH BIT(24) +#define DL_OPT_FLASH_FILE_NAME BIT(25) +#define DL_OPT_FLASH_COMPONENT BIT(26) +#define DL_OPT_HEALTH_REPORTER_NAME BIT(27) +#define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD BIT(28) +#define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER BIT(29) +#define DL_OPT_TRAP_NAME BIT(30) +#define DL_OPT_TRAP_ACTION BIT(31) +#define DL_OPT_TRAP_GROUP_NAME BIT(32) +#define DL_OPT_NETNS BIT(33) +#define DL_OPT_TRAP_POLICER_ID BIT(34) +#define DL_OPT_TRAP_POLICER_RATE BIT(35) +#define DL_OPT_TRAP_POLICER_BURST BIT(36) +#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37) +#define DL_OPT_PORT_FUNCTION_HW_ADDR BIT(38) + +struct dl_opts { + uint64_t present; /* flags of present items */ + char *bus_name; + char *dev_name; + uint32_t port_index; + enum devlink_port_type port_type; + uint32_t port_count; + uint32_t sb_index; + uint16_t sb_pool_index; + uint32_t sb_pool_size; + enum devlink_sb_pool_type sb_pool_type; + enum devlink_sb_threshold_type sb_pool_thtype; + uint32_t sb_threshold; + uint16_t sb_tc_index; + enum devlink_eswitch_mode eswitch_mode; + enum devlink_eswitch_inline_mode eswitch_inline_mode; + const char *dpipe_table_name; + bool dpipe_counters_enabled; + enum devlink_eswitch_encap_mode eswitch_encap_mode; + const char *resource_path; + uint64_t resource_size; + uint32_t resource_id; + bool resource_id_valid; + const char *param_name; + const char *param_value; + enum devlink_param_cmode cmode; + char *region_name; + uint32_t region_snapshot_id; + uint64_t region_address; + uint64_t region_length; + const char *flash_file_name; + const char *flash_component; + const char *reporter_name; + uint64_t reporter_graceful_period; + bool reporter_auto_recover; + bool reporter_auto_dump; + const char *trap_name; + const char *trap_group_name; + enum devlink_trap_action trap_action; + bool netns_is_pid; + uint32_t netns; + uint32_t trap_policer_id; + uint64_t trap_policer_rate; + uint64_t trap_policer_burst; + char port_function_hw_addr[MAX_ADDR_LEN]; + uint32_t port_function_hw_addr_len; +}; + +struct dl { + struct mnlg_socket *nlg; + struct list_head ifname_map_list; + int argc; + char **argv; + bool no_nice_names; + struct dl_opts opts; + bool json_output; + bool pretty_output; + bool verbose; + bool stats; + struct { + bool present; + char *bus_name; + char *dev_name; + uint32_t port_index; + } arr_last; +}; + +static int dl_argc(struct dl *dl) +{ + return dl->argc; +} + +static char *dl_argv(struct dl *dl) +{ + if (dl_argc(dl) == 0) + return NULL; + return *dl->argv; +} + +static void dl_arg_inc(struct dl *dl) +{ + if (dl_argc(dl) == 0) + return; + dl->argc--; + dl->argv++; +} + +static void dl_arg_dec(struct dl *dl) +{ + dl->argc++; + dl->argv--; +} + +static char *dl_argv_next(struct dl *dl) +{ + char *ret; + + if (dl_argc(dl) == 0) + return NULL; + + ret = *dl->argv; + dl_arg_inc(dl); + return ret; +} + +static char *dl_argv_index(struct dl *dl, unsigned int index) +{ + if (index >= dl_argc(dl)) + return NULL; + return dl->argv[index]; +} + +static int strcmpx(const char *str1, const char *str2) +{ + if (strlen(str1) > strlen(str2)) + return -1; + return strncmp(str1, str2, strlen(str1)); +} + +static bool dl_argv_match(struct dl *dl, const char *pattern) +{ + if (dl_argc(dl) == 0) + return false; + return strcmpx(dl_argv(dl), pattern) == 0; +} + +static bool dl_no_arg(struct dl *dl) +{ + return dl_argc(dl) == 0; +} + +static void __pr_out_indent_newline(struct dl *dl) +{ + if (!g_indent_newline && !dl->json_output) + pr_out(" "); +} + +static bool is_binary_eol(int i) +{ + return !(i%16); +} + +static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len) +{ + int i = 0; + + while (i < len) { + if (dl->json_output) + print_int(PRINT_JSON, NULL, NULL, data[i]); + else + pr_out("%02x ", data[i]); + i++; + if (!dl->json_output && is_binary_eol(i)) + __pr_out_newline(); + } + if (!dl->json_output && !is_binary_eol(i)) + __pr_out_newline(); +} + +static void pr_out_name(struct dl *dl, const char *name) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + print_string(PRINT_JSON, name, NULL, NULL); + else + pr_out("%s:", name); +} + +static void pr_out_u64(struct dl *dl, const char *name, uint64_t val) +{ + __pr_out_indent_newline(dl); + if (val == (uint64_t) -1) + return print_string_name_value(name, "unlimited"); + + if (dl->json_output) + print_u64(PRINT_JSON, name, NULL, val); + else + pr_out("%s %"PRIu64, name, val); +} + +static void pr_out_section_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + open_json_object(NULL); + open_json_object(name); + } +} + +static void pr_out_section_end(struct dl *dl) +{ + if (dl->json_output) { + if (dl->arr_last.present) + close_json_array(PRINT_JSON, NULL); + close_json_object(); + close_json_object(); + } +} + +static void pr_out_array_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + open_json_array(PRINT_JSON, name); + } else { + __pr_out_indent_inc(); + __pr_out_newline(); + pr_out("%s:", name); + __pr_out_indent_inc(); + __pr_out_newline(); + } +} + +static void pr_out_array_end(struct dl *dl) +{ + if (dl->json_output) { + close_json_array(PRINT_JSON, NULL); + } else { + __pr_out_indent_dec(); + __pr_out_indent_dec(); + } +} + +static void pr_out_object_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + open_json_object(name); + } else { + __pr_out_indent_inc(); + __pr_out_newline(); + pr_out("%s:", name); + __pr_out_indent_inc(); + __pr_out_newline(); + } +} + +static void pr_out_object_end(struct dl *dl) +{ + if (dl->json_output) { + close_json_object(); + } else { + __pr_out_indent_dec(); + __pr_out_indent_dec(); + } +} + +static void pr_out_entry_start(struct dl *dl) +{ + if (dl->json_output) + open_json_object(NULL); +} + +static void pr_out_entry_end(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + __pr_out_newline(); +} + +static void check_indent_newline(struct dl *dl) +{ + __pr_out_indent_newline(dl); + + if (g_indent_newline && !is_json_context()) { + printf("%s", g_indent_str); + g_indent_newline = false; + } + g_new_line_count = 0; +} + +static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = { + [DEVLINK_ATTR_BUS_NAME] = MNL_TYPE_NUL_STRING, + [DEVLINK_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, + [DEVLINK_ATTR_PORT_INDEX] = MNL_TYPE_U32, + [DEVLINK_ATTR_PORT_TYPE] = MNL_TYPE_U16, + [DEVLINK_ATTR_PORT_DESIRED_TYPE] = MNL_TYPE_U16, + [DEVLINK_ATTR_PORT_NETDEV_IFINDEX] = MNL_TYPE_U32, + [DEVLINK_ATTR_PORT_NETDEV_NAME] = MNL_TYPE_NUL_STRING, + [DEVLINK_ATTR_PORT_IBDEV_NAME] = MNL_TYPE_NUL_STRING, + [DEVLINK_ATTR_PORT_LANES] = MNL_TYPE_U32, + [DEVLINK_ATTR_PORT_SPLITTABLE] = MNL_TYPE_U8, + [DEVLINK_ATTR_SB_INDEX] = MNL_TYPE_U32, + [DEVLINK_ATTR_SB_SIZE] = MNL_TYPE_U32, + [DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_INGRESS_TC_COUNT] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_EGRESS_TC_COUNT] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_POOL_INDEX] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_POOL_TYPE] = MNL_TYPE_U8, + [DEVLINK_ATTR_SB_POOL_SIZE] = MNL_TYPE_U32, + [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = MNL_TYPE_U8, + [DEVLINK_ATTR_SB_THRESHOLD] = MNL_TYPE_U32, + [DEVLINK_ATTR_SB_TC_INDEX] = MNL_TYPE_U16, + [DEVLINK_ATTR_SB_OCC_CUR] = MNL_TYPE_U32, + [DEVLINK_ATTR_SB_OCC_MAX] = MNL_TYPE_U32, + [DEVLINK_ATTR_ESWITCH_MODE] = MNL_TYPE_U16, + [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = MNL_TYPE_U8, + [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = MNL_TYPE_U8, + [DEVLINK_ATTR_DPIPE_TABLES] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_TABLE] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_TABLE_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_DPIPE_TABLE_SIZE] = MNL_TYPE_U64, + [DEVLINK_ATTR_DPIPE_TABLE_MATCHES] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = MNL_TYPE_U8, + [DEVLINK_ATTR_DPIPE_ENTRIES] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ENTRY] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ENTRY_INDEX] = MNL_TYPE_U64, + [DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ENTRY_COUNTER] = MNL_TYPE_U64, + [DEVLINK_ATTR_DPIPE_MATCH] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_MATCH_VALUE] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_MATCH_TYPE] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_ACTION] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ACTION_VALUE] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_ACTION_TYPE] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_VALUE_MAPPING] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_HEADERS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_HEADER] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_HEADER_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_DPIPE_HEADER_ID] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_HEADER_FIELDS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_HEADER_GLOBAL] = MNL_TYPE_U8, + [DEVLINK_ATTR_DPIPE_HEADER_INDEX] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_FIELD] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_DPIPE_FIELD_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_DPIPE_FIELD_ID] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] = MNL_TYPE_U32, + [DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE] = MNL_TYPE_U32, + [DEVLINK_ATTR_PARAM] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_PARAM_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_PARAM_TYPE] = MNL_TYPE_U8, + [DEVLINK_ATTR_PARAM_VALUES_LIST] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_PARAM_VALUE] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_PARAM_VALUE_CMODE] = MNL_TYPE_U8, + [DEVLINK_ATTR_REGION_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_REGION_SIZE] = MNL_TYPE_U64, + [DEVLINK_ATTR_REGION_SNAPSHOTS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_REGION_SNAPSHOT] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = MNL_TYPE_U32, + [DEVLINK_ATTR_REGION_CHUNKS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_REGION_CHUNK] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_REGION_CHUNK_DATA] = MNL_TYPE_BINARY, + [DEVLINK_ATTR_REGION_CHUNK_ADDR] = MNL_TYPE_U64, + [DEVLINK_ATTR_REGION_CHUNK_LEN] = MNL_TYPE_U64, + [DEVLINK_ATTR_INFO_DRIVER_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_INFO_SERIAL_NUMBER] = MNL_TYPE_STRING, + [DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER] = MNL_TYPE_STRING, + [DEVLINK_ATTR_INFO_VERSION_FIXED] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_INFO_VERSION_RUNNING] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_INFO_VERSION_STORED] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_INFO_VERSION_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_INFO_VERSION_VALUE] = MNL_TYPE_STRING, + [DEVLINK_ATTR_HEALTH_REPORTER] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_HEALTH_REPORTER_STATE] = MNL_TYPE_U8, + [DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] = MNL_TYPE_U64, + [DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] = MNL_TYPE_U64, + [DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64, + [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64, + [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = MNL_TYPE_STRING, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG] = MNL_TYPE_STRING, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] = MNL_TYPE_U64, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_TRAP_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_TRAP_ACTION] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_TYPE] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_GENERIC] = MNL_TYPE_FLAG, + [DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_POLICER_ID] = MNL_TYPE_U32, + [DEVLINK_ATTR_TRAP_POLICER_RATE] = MNL_TYPE_U64, + [DEVLINK_ATTR_TRAP_POLICER_BURST] = MNL_TYPE_U64, +}; + +static const enum mnl_attr_data_type +devlink_stats_policy[DEVLINK_ATTR_STATS_MAX + 1] = { + [DEVLINK_ATTR_STATS_RX_PACKETS] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS_RX_BYTES] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS_RX_DROPPED] = MNL_TYPE_U64, +}; + +static int attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + if (mnl_attr_type_valid(attr, DEVLINK_ATTR_MAX) < 0) + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, devlink_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +static int attr_stats_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + /* Allow the tool to work on top of newer kernels that might contain + * more attributes. + */ + if (mnl_attr_type_valid(attr, DEVLINK_ATTR_STATS_MAX) < 0) + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, devlink_stats_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +static const enum mnl_attr_data_type +devlink_function_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = { + [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR ] = MNL_TYPE_BINARY, +}; + +static int function_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + /* Allow the tool to work on top of newer kernels that might contain + * more attributes. + */ + if (mnl_attr_type_valid(attr, DEVLINK_PORT_FUNCTION_ATTR_MAX) < 0) + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, devlink_function_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +static int ifname_map_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct dl *dl = data; + struct ifname_map *ifname_map; + const char *bus_name; + const char *dev_name; + uint32_t port_ifindex; + const char *port_ifname; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + + if (!tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) + return MNL_CB_OK; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_ifindex = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + port_ifname = mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]); + ifname_map = ifname_map_alloc(bus_name, dev_name, + port_ifindex, port_ifname); + if (!ifname_map) + return MNL_CB_ERROR; + list_add(&ifname_map->list, &dl->ifname_map_list); + + return MNL_CB_OK; +} + +static void ifname_map_fini(struct dl *dl) +{ + struct ifname_map *ifname_map, *tmp; + + list_for_each_entry_safe(ifname_map, tmp, + &dl->ifname_map_list, list) { + list_del(&ifname_map->list); + ifname_map_free(ifname_map); + } +} + +static int ifname_map_init(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + INIT_LIST_HEAD(&dl->ifname_map_list); + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, ifname_map_cb, dl); + if (err) { + ifname_map_fini(dl); + return err; + } + return 0; +} + +static int ifname_map_lookup(struct dl *dl, const char *ifname, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + struct ifname_map *ifname_map; + + list_for_each_entry(ifname_map, &dl->ifname_map_list, list) { + if (strcmp(ifname, ifname_map->ifname) == 0) { + *p_bus_name = ifname_map->bus_name; + *p_dev_name = ifname_map->dev_name; + *p_port_index = ifname_map->port_index; + return 0; + } + } + return -ENOENT; +} + +static int ifname_map_rev_lookup(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index, + char **p_ifname) +{ + struct ifname_map *ifname_map; + + list_for_each_entry(ifname_map, &dl->ifname_map_list, list) { + if (strcmp(bus_name, ifname_map->bus_name) == 0 && + strcmp(dev_name, ifname_map->dev_name) == 0 && + port_index == ifname_map->port_index) { + *p_ifname = ifname_map->ifname; + return 0; + } + } + return -ENOENT; +} + +static unsigned int strslashcount(char *str) +{ + unsigned int count = 0; + char *pos = str; + + while ((pos = strchr(pos, '/'))) { + count++; + pos++; + } + return count; +} + +static int strslashrsplit(char *str, char **before, char **after) +{ + char *slash; + + slash = strrchr(str, '/'); + if (!slash) + return -EINVAL; + *slash = '\0'; + *before = str; + *after = slash + 1; + return 0; +} + +static int strtouint64_t(const char *str, uint64_t *p_val) +{ + char *endptr; + unsigned long long int val; + + val = strtoull(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > ULONG_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int strtouint32_t(const char *str, uint32_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > UINT_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int strtouint16_t(const char *str, uint16_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > USHRT_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int strtouint8_t(const char *str, uint8_t *p_val) +{ + char *endptr; + unsigned long int val; + + val = strtoul(str, &endptr, 10); + if (endptr == str || *endptr != '\0') + return -EINVAL; + if (val > UCHAR_MAX) + return -ERANGE; + *p_val = val; + return 0; +} + +static int strtobool(const char *str, bool *p_val) +{ + bool val; + + if (!strcmp(str, "true") || !strcmp(str, "1") || + !strcmp(str, "enable")) + val = true; + else if (!strcmp(str, "false") || !strcmp(str, "0") || + !strcmp(str, "disable")) + val = false; + else + return -EINVAL; + *p_val = val; + return 0; +} + +static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name) +{ + strslashrsplit(str, p_bus_name, p_dev_name); + return 0; +} + +static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name) +{ + char *str = dl_argv_next(dl); + + if (!str) { + pr_err("Devlink identification (\"bus_name/dev_name\") expected\n"); + return -EINVAL; + } + if (strslashcount(str) != 1) { + pr_err("Wrong devlink identification string format.\n"); + pr_err("Expected \"bus_name/dev_name\".\n"); + return -EINVAL; + } + return __dl_argv_handle(str, p_bus_name, p_dev_name); +} + +static int __dl_argv_handle_port(char *str, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + char *handlestr; + char *portstr; + int err; + + err = strslashrsplit(str, &handlestr, &portstr); + if (err) { + pr_err("Port identification \"%s\" is invalid\n", str); + return err; + } + err = strtouint32_t(portstr, p_port_index); + if (err) { + pr_err("Port index \"%s\" is not a number or not within range\n", + portstr); + return err; + } + err = strslashrsplit(handlestr, p_bus_name, p_dev_name); + if (err) { + pr_err("Port identification \"%s\" is invalid\n", str); + return err; + } + return 0; +} + +static int __dl_argv_handle_port_ifname(struct dl *dl, char *str, + char **p_bus_name, char **p_dev_name, + uint32_t *p_port_index) +{ + int err; + + err = ifname_map_lookup(dl, str, p_bus_name, p_dev_name, + p_port_index); + if (err) { + pr_err("Netdevice \"%s\" not found\n", str); + return err; + } + return 0; +} + +static int dl_argv_handle_port(struct dl *dl, char **p_bus_name, + char **p_dev_name, uint32_t *p_port_index) +{ + char *str = dl_argv_next(dl); + unsigned int slash_count; + + if (!str) { + pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n"); + return -EINVAL; + } + slash_count = strslashcount(str); + switch (slash_count) { + case 0: + return __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + case 2: + return __dl_argv_handle_port(str, p_bus_name, + p_dev_name, p_port_index); + default: + pr_err("Wrong port identification string format.\n"); + pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); + return -EINVAL; + } +} + +static int dl_argv_handle_both(struct dl *dl, char **p_bus_name, + char **p_dev_name, uint32_t *p_port_index, + uint64_t *p_handle_bit) +{ + char *str = dl_argv_next(dl); + unsigned int slash_count; + int err; + + if (!str) { + pr_err("One of following identifications expected:\n" + "Devlink identification (\"bus_name/dev_name\")\n" + "Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\")\n"); + return -EINVAL; + } + slash_count = strslashcount(str); + if (slash_count == 1) { + err = __dl_argv_handle(str, p_bus_name, p_dev_name); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLE; + } else if (slash_count == 2) { + err = __dl_argv_handle_port(str, p_bus_name, + p_dev_name, p_port_index); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLEP; + } else if (slash_count == 0) { + err = __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + if (err) + return err; + *p_handle_bit = DL_OPT_HANDLEP; + } else { + pr_err("Wrong port identification string format.\n"); + pr_err("Expected \"bus_name/dev_name\" or \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); + return -EINVAL; + } + return 0; +} + +static int __dl_argv_handle_region(char *str, char **p_bus_name, + char **p_dev_name, char **p_region) +{ + char *handlestr; + int err; + + err = strslashrsplit(str, &handlestr, p_region); + if (err) { + pr_err("Region identification \"%s\" is invalid\n", str); + return err; + } + err = strslashrsplit(handlestr, p_bus_name, p_dev_name); + if (err) { + pr_err("Region identification \"%s\" is invalid\n", str); + return err; + } + return 0; +} + +static int dl_argv_handle_region(struct dl *dl, char **p_bus_name, + char **p_dev_name, char **p_region) +{ + char *str = dl_argv_next(dl); + unsigned int slash_count; + + if (!str) { + pr_err("Expected \"bus_name/dev_name/region\" identification.\n"); + return -EINVAL; + } + + slash_count = strslashcount(str); + if (slash_count != 2) { + pr_err("Wrong region identification string format.\n"); + pr_err("Expected \"bus_name/dev_name/region\" identification.\n"".\n"); + return -EINVAL; + } + + return __dl_argv_handle_region(str, p_bus_name, p_dev_name, p_region); +} + +static int dl_argv_uint64_t(struct dl *dl, uint64_t *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint64_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint32_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Unsigned number argument expected\n"); + return -EINVAL; + } + + err = strtouint16_t(str, p_val); + if (err) { + pr_err("\"%s\" is not a number or not within range\n", str); + return err; + } + return 0; +} + +static int dl_argv_bool(struct dl *dl, bool *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Boolean argument expected\n"); + return -EINVAL; + } + + err = strtobool(str, p_val); + if (err) { + pr_err("\"%s\" is not a valid boolean value\n", str); + return err; + } + return 0; +} + +static int dl_argv_str(struct dl *dl, const char **p_str) +{ + const char *str = dl_argv_next(dl); + + if (!str) { + pr_err("String parameter expected\n"); + return -EINVAL; + } + *p_str = str; + return 0; +} + +static int port_type_get(const char *typestr, enum devlink_port_type *p_type) +{ + if (strcmp(typestr, "auto") == 0) { + *p_type = DEVLINK_PORT_TYPE_AUTO; + } else if (strcmp(typestr, "eth") == 0) { + *p_type = DEVLINK_PORT_TYPE_ETH; + } else if (strcmp(typestr, "ib") == 0) { + *p_type = DEVLINK_PORT_TYPE_IB; + } else { + pr_err("Unknown port type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type) +{ + if (strcmp(typestr, "ingress") == 0) { + *p_type = DEVLINK_SB_POOL_TYPE_INGRESS; + } else if (strcmp(typestr, "egress") == 0) { + *p_type = DEVLINK_SB_POOL_TYPE_EGRESS; + } else { + pr_err("Unknown pool type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int threshold_type_get(const char *typestr, + enum devlink_sb_threshold_type *p_type) +{ + if (strcmp(typestr, "static") == 0) { + *p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC; + } else if (strcmp(typestr, "dynamic") == 0) { + *p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC; + } else { + pr_err("Unknown threshold type \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int eswitch_mode_get(const char *typestr, + enum devlink_eswitch_mode *p_mode) +{ + if (strcmp(typestr, ESWITCH_MODE_LEGACY) == 0) { + *p_mode = DEVLINK_ESWITCH_MODE_LEGACY; + } else if (strcmp(typestr, ESWITCH_MODE_SWITCHDEV) == 0) { + *p_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + } else { + pr_err("Unknown eswitch mode \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int eswitch_inline_mode_get(const char *typestr, + enum devlink_eswitch_inline_mode *p_mode) +{ + if (strcmp(typestr, ESWITCH_INLINE_MODE_NONE) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NONE; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_LINK) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_LINK; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_NETWORK) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_TRANSPORT) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT; + } else { + pr_err("Unknown eswitch inline mode \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int +eswitch_encap_mode_get(const char *typestr, + enum devlink_eswitch_encap_mode *p_encap_mode) +{ + /* The initial implementation incorrectly accepted "enable"/"disable". + * Carry it to maintain backward compatibility. + */ + if (strcmp(typestr, "disable") == 0 || + strcmp(typestr, ESWITCH_ENCAP_MODE_NONE) == 0) { + *p_encap_mode = DEVLINK_ESWITCH_ENCAP_MODE_NONE; + } else if (strcmp(typestr, "enable") == 0 || + strcmp(typestr, ESWITCH_ENCAP_MODE_BASIC) == 0) { + *p_encap_mode = DEVLINK_ESWITCH_ENCAP_MODE_BASIC; + } else { + pr_err("Unknown eswitch encap mode \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int param_cmode_get(const char *cmodestr, + enum devlink_param_cmode *cmode) +{ + if (strcmp(cmodestr, PARAM_CMODE_RUNTIME_STR) == 0) { + *cmode = DEVLINK_PARAM_CMODE_RUNTIME; + } else if (strcmp(cmodestr, PARAM_CMODE_DRIVERINIT_STR) == 0) { + *cmode = DEVLINK_PARAM_CMODE_DRIVERINIT; + } else if (strcmp(cmodestr, PARAM_CMODE_PERMANENT_STR) == 0) { + *cmode = DEVLINK_PARAM_CMODE_PERMANENT; + } else { + pr_err("Unknown configuration mode \"%s\"\n", cmodestr); + return -EINVAL; + } + return 0; +} + +static int trap_action_get(const char *actionstr, + enum devlink_trap_action *p_action) +{ + if (strcmp(actionstr, "drop") == 0) { + *p_action = DEVLINK_TRAP_ACTION_DROP; + } else if (strcmp(actionstr, "trap") == 0) { + *p_action = DEVLINK_TRAP_ACTION_TRAP; + } else if (strcmp(actionstr, "mirror") == 0) { + *p_action = DEVLINK_TRAP_ACTION_MIRROR; + } else { + pr_err("Unknown trap action \"%s\"\n", actionstr); + return -EINVAL; + } + return 0; +} + +static int hw_addr_parse(const char *addrstr, char *hw_addr, uint32_t *len) +{ + int alen; + + alen = ll_addr_a2n(hw_addr, MAX_ADDR_LEN, addrstr); + if (alen < 0) + return -EINVAL; + *len = alen; + return 0; +} + +struct dl_args_metadata { + uint64_t o_flag; + char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN]; +}; + +static const struct dl_args_metadata dl_args_required[] = { + {DL_OPT_PORT_TYPE, "Port type not set."}, + {DL_OPT_PORT_COUNT, "Port split count option expected."}, + {DL_OPT_SB_POOL, "Pool index option expected."}, + {DL_OPT_SB_SIZE, "Pool size option expected."}, + {DL_OPT_SB_TYPE, "Pool type option expected."}, + {DL_OPT_SB_THTYPE, "Pool threshold type option expected."}, + {DL_OPT_SB_TH, "Threshold option expected."}, + {DL_OPT_SB_TC, "TC index option expected."}, + {DL_OPT_ESWITCH_MODE, "E-Switch mode option expected."}, + {DL_OPT_ESWITCH_INLINE_MODE, "E-Switch inline-mode option expected."}, + {DL_OPT_DPIPE_TABLE_NAME, "Dpipe table name expected."}, + {DL_OPT_DPIPE_TABLE_COUNTERS, "Dpipe table counter state expected."}, + {DL_OPT_ESWITCH_ENCAP_MODE, "E-Switch encapsulation option expected."}, + {DL_OPT_RESOURCE_PATH, "Resource path expected."}, + {DL_OPT_RESOURCE_SIZE, "Resource size expected."}, + {DL_OPT_PARAM_NAME, "Parameter name expected."}, + {DL_OPT_PARAM_VALUE, "Value to set expected."}, + {DL_OPT_PARAM_CMODE, "Configuration mode expected."}, + {DL_OPT_REGION_SNAPSHOT_ID, "Region snapshot id expected."}, + {DL_OPT_REGION_ADDRESS, "Region address value expected."}, + {DL_OPT_REGION_LENGTH, "Region length value expected."}, + {DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."}, + {DL_OPT_TRAP_NAME, "Trap's name is expected."}, + {DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."}, + {DL_OPT_PORT_FUNCTION_HW_ADDR, "Port function's hardware address is expected."}, +}; + +static int dl_args_finding_required_validate(uint64_t o_required, + uint64_t o_found) +{ + uint64_t o_flag; + int i; + + for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) { + o_flag = dl_args_required[i].o_flag; + if ((o_required & o_flag) && !(o_found & o_flag)) { + pr_err("%s\n", dl_args_required[i].err_msg); + return -EINVAL; + } + } + if (o_required & ~o_found) { + pr_err("BUG: unknown argument required but not found\n"); + return -EINVAL; + } + return 0; +} + +static int dl_argv_parse(struct dl *dl, uint64_t o_required, + uint64_t o_optional) +{ + struct dl_opts *opts = &dl->opts; + uint64_t o_all = o_required | o_optional; + uint64_t o_found = 0; + int err; + + if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { + uint64_t handle_bit; + + err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name, + &opts->port_index, &handle_bit); + if (err) + return err; + o_required &= ~(DL_OPT_HANDLE | DL_OPT_HANDLEP) | handle_bit; + o_found |= handle_bit; + } else if (o_required & DL_OPT_HANDLE) { + err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name); + if (err) + return err; + o_found |= DL_OPT_HANDLE; + } else if (o_required & DL_OPT_HANDLEP) { + err = dl_argv_handle_port(dl, &opts->bus_name, &opts->dev_name, + &opts->port_index); + if (err) + return err; + o_found |= DL_OPT_HANDLEP; + } else if (o_required & DL_OPT_HANDLE_REGION) { + err = dl_argv_handle_region(dl, &opts->bus_name, + &opts->dev_name, + &opts->region_name); + if (err) + return err; + o_found |= DL_OPT_HANDLE_REGION; + } + + while (dl_argc(dl)) { + if (dl_argv_match(dl, "type") && + (o_all & DL_OPT_PORT_TYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = port_type_get(typestr, &opts->port_type); + if (err) + return err; + o_found |= DL_OPT_PORT_TYPE; + } else if (dl_argv_match(dl, "count") && + (o_all & DL_OPT_PORT_COUNT)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->port_count); + if (err) + return err; + o_found |= DL_OPT_PORT_COUNT; + } else if (dl_argv_match(dl, "sb") && + (o_all & DL_OPT_SB)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_index); + if (err) + return err; + o_found |= DL_OPT_SB; + } else if (dl_argv_match(dl, "pool") && + (o_all & DL_OPT_SB_POOL)) { + dl_arg_inc(dl); + err = dl_argv_uint16_t(dl, &opts->sb_pool_index); + if (err) + return err; + o_found |= DL_OPT_SB_POOL; + } else if (dl_argv_match(dl, "size") && + (o_all & DL_OPT_SB_SIZE)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_pool_size); + if (err) + return err; + o_found |= DL_OPT_SB_SIZE; + } else if (dl_argv_match(dl, "type") && + (o_all & DL_OPT_SB_TYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = pool_type_get(typestr, &opts->sb_pool_type); + if (err) + return err; + o_found |= DL_OPT_SB_TYPE; + } else if (dl_argv_match(dl, "thtype") && + (o_all & DL_OPT_SB_THTYPE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = threshold_type_get(typestr, + &opts->sb_pool_thtype); + if (err) + return err; + o_found |= DL_OPT_SB_THTYPE; + } else if (dl_argv_match(dl, "th") && + (o_all & DL_OPT_SB_TH)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->sb_threshold); + if (err) + return err; + o_found |= DL_OPT_SB_TH; + } else if (dl_argv_match(dl, "tc") && + (o_all & DL_OPT_SB_TC)) { + dl_arg_inc(dl); + err = dl_argv_uint16_t(dl, &opts->sb_tc_index); + if (err) + return err; + o_found |= DL_OPT_SB_TC; + } else if (dl_argv_match(dl, "mode") && + (o_all & DL_OPT_ESWITCH_MODE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = eswitch_mode_get(typestr, &opts->eswitch_mode); + if (err) + return err; + o_found |= DL_OPT_ESWITCH_MODE; + } else if (dl_argv_match(dl, "inline-mode") && + (o_all & DL_OPT_ESWITCH_INLINE_MODE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = eswitch_inline_mode_get( + typestr, &opts->eswitch_inline_mode); + if (err) + return err; + o_found |= DL_OPT_ESWITCH_INLINE_MODE; + } else if (dl_argv_match(dl, "name") && + (o_all & DL_OPT_DPIPE_TABLE_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->dpipe_table_name); + if (err) + return err; + o_found |= DL_OPT_DPIPE_TABLE_NAME; + } else if ((dl_argv_match(dl, "counters") || + dl_argv_match(dl, "counters_enabled")) && + (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) { + dl_arg_inc(dl); + err = dl_argv_bool(dl, &opts->dpipe_counters_enabled); + if (err) + return err; + o_found |= DL_OPT_DPIPE_TABLE_COUNTERS; + } else if ((dl_argv_match(dl, "encap") || /* Original incorrect implementation */ + dl_argv_match(dl, "encap-mode")) && + (o_all & DL_OPT_ESWITCH_ENCAP_MODE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = eswitch_encap_mode_get(typestr, + &opts->eswitch_encap_mode); + if (err) + return err; + o_found |= DL_OPT_ESWITCH_ENCAP_MODE; + } else if (dl_argv_match(dl, "path") && + (o_all & DL_OPT_RESOURCE_PATH)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->resource_path); + if (err) + return err; + o_found |= DL_OPT_RESOURCE_PATH; + } else if (dl_argv_match(dl, "size") && + (o_all & DL_OPT_RESOURCE_SIZE)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->resource_size); + if (err) + return err; + o_found |= DL_OPT_RESOURCE_SIZE; + } else if (dl_argv_match(dl, "name") && + (o_all & DL_OPT_PARAM_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->param_name); + if (err) + return err; + o_found |= DL_OPT_PARAM_NAME; + } else if (dl_argv_match(dl, "value") && + (o_all & DL_OPT_PARAM_VALUE)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->param_value); + if (err) + return err; + o_found |= DL_OPT_PARAM_VALUE; + } else if (dl_argv_match(dl, "cmode") && + (o_all & DL_OPT_PARAM_CMODE)) { + const char *cmodestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &cmodestr); + if (err) + return err; + err = param_cmode_get(cmodestr, &opts->cmode); + if (err) + return err; + o_found |= DL_OPT_PARAM_CMODE; + } else if (dl_argv_match(dl, "snapshot") && + (o_all & DL_OPT_REGION_SNAPSHOT_ID)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->region_snapshot_id); + if (err) + return err; + o_found |= DL_OPT_REGION_SNAPSHOT_ID; + } else if (dl_argv_match(dl, "address") && + (o_all & DL_OPT_REGION_ADDRESS)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->region_address); + if (err) + return err; + o_found |= DL_OPT_REGION_ADDRESS; + } else if (dl_argv_match(dl, "length") && + (o_all & DL_OPT_REGION_LENGTH)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->region_length); + if (err) + return err; + o_found |= DL_OPT_REGION_LENGTH; + } else if (dl_argv_match(dl, "file") && + (o_all & DL_OPT_FLASH_FILE_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->flash_file_name); + if (err) + return err; + o_found |= DL_OPT_FLASH_FILE_NAME; + } else if (dl_argv_match(dl, "component") && + (o_all & DL_OPT_FLASH_COMPONENT)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->flash_component); + if (err) + return err; + o_found |= DL_OPT_FLASH_COMPONENT; + } else if (dl_argv_match(dl, "reporter") && + (o_all & DL_OPT_HEALTH_REPORTER_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->reporter_name); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_NAME; + } else if (dl_argv_match(dl, "grace_period") && + (o_all & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, + &opts->reporter_graceful_period); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD; + } else if (dl_argv_match(dl, "auto_recover") && + (o_all & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)) { + dl_arg_inc(dl); + err = dl_argv_bool(dl, &opts->reporter_auto_recover); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER; + } else if (dl_argv_match(dl, "auto_dump") && + (o_all & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)) { + dl_arg_inc(dl); + err = dl_argv_bool(dl, &opts->reporter_auto_dump); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_AUTO_DUMP; + } else if (dl_argv_match(dl, "trap") && + (o_all & DL_OPT_TRAP_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->trap_name); + if (err) + return err; + o_found |= DL_OPT_TRAP_NAME; + } else if (dl_argv_match(dl, "group") && + (o_all & DL_OPT_TRAP_GROUP_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->trap_group_name); + if (err) + return err; + o_found |= DL_OPT_TRAP_GROUP_NAME; + } else if (dl_argv_match(dl, "action") && + (o_all & DL_OPT_TRAP_ACTION)) { + const char *actionstr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &actionstr); + if (err) + return err; + err = trap_action_get(actionstr, &opts->trap_action); + if (err) + return err; + o_found |= DL_OPT_TRAP_ACTION; + } else if (dl_argv_match(dl, "netns") && + (o_all & DL_OPT_NETNS)) { + const char *netns_str; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &netns_str); + if (err) + return err; + opts->netns = netns_get_fd(netns_str); + if ((int)opts->netns < 0) { + dl_arg_dec(dl); + err = dl_argv_uint32_t(dl, &opts->netns); + if (err) + return err; + opts->netns_is_pid = true; + } + o_found |= DL_OPT_NETNS; + } else if (dl_argv_match(dl, "policer") && + (o_all & DL_OPT_TRAP_POLICER_ID)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->trap_policer_id); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_ID; + } else if (dl_argv_match(dl, "nopolicer") && + (o_all & DL_OPT_TRAP_POLICER_ID)) { + dl_arg_inc(dl); + opts->trap_policer_id = 0; + o_found |= DL_OPT_TRAP_POLICER_ID; + } else if (dl_argv_match(dl, "rate") && + (o_all & DL_OPT_TRAP_POLICER_RATE)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->trap_policer_rate); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_RATE; + } else if (dl_argv_match(dl, "burst") && + (o_all & DL_OPT_TRAP_POLICER_BURST)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->trap_policer_burst); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_BURST; + } else if (dl_argv_match(dl, "hw_addr") && + (o_all & DL_OPT_PORT_FUNCTION_HW_ADDR)) { + const char *addrstr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &addrstr); + if (err) + return err; + err = hw_addr_parse(addrstr, opts->port_function_hw_addr, + &opts->port_function_hw_addr_len); + if (err) + return err; + o_found |= DL_OPT_PORT_FUNCTION_HW_ADDR; + + } else { + pr_err("Unknown option \"%s\"\n", dl_argv(dl)); + return -EINVAL; + } + } + + opts->present = o_found; + + if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) { + opts->sb_index = 0; + opts->present |= DL_OPT_SB; + } + + return dl_args_finding_required_validate(o_required, o_found); +} + +static void +dl_function_attr_put(struct nlmsghdr *nlh, const struct dl_opts *opts) +{ + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, DEVLINK_ATTR_PORT_FUNCTION); + mnl_attr_put(nlh, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, + opts->port_function_hw_addr_len, + opts->port_function_hw_addr); + mnl_attr_nest_end(nlh, nest); +} + +static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) +{ + struct dl_opts *opts = &dl->opts; + + if (opts->present & DL_OPT_HANDLE) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name); + } else if (opts->present & DL_OPT_HANDLEP) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name); + mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX, + opts->port_index); + } else if (opts->present & DL_OPT_HANDLE_REGION) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_REGION_NAME, + opts->region_name); + } + if (opts->present & DL_OPT_PORT_TYPE) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE, + opts->port_type); + if (opts->present & DL_OPT_PORT_COUNT) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT, + opts->port_count); + if (opts->present & DL_OPT_SB) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX, + opts->sb_index); + if (opts->present & DL_OPT_SB_POOL) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX, + opts->sb_pool_index); + if (opts->present & DL_OPT_SB_SIZE) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE, + opts->sb_pool_size); + if (opts->present & DL_OPT_SB_TYPE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE, + opts->sb_pool_type); + if (opts->present & DL_OPT_SB_THTYPE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, + opts->sb_pool_thtype); + if (opts->present & DL_OPT_SB_TH) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD, + opts->sb_threshold); + if (opts->present & DL_OPT_SB_TC) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX, + opts->sb_tc_index); + if (opts->present & DL_OPT_ESWITCH_MODE) + mnl_attr_put_u16(nlh, DEVLINK_ATTR_ESWITCH_MODE, + opts->eswitch_mode); + if (opts->present & DL_OPT_ESWITCH_INLINE_MODE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE, + opts->eswitch_inline_mode); + if (opts->present & DL_OPT_DPIPE_TABLE_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DPIPE_TABLE_NAME, + opts->dpipe_table_name); + if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, + opts->dpipe_counters_enabled); + if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, + opts->eswitch_encap_mode); + if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID, + opts->resource_id); + if (opts->present & DL_OPT_RESOURCE_SIZE) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE, + opts->resource_size); + if (opts->present & DL_OPT_PARAM_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_NAME, + opts->param_name); + if (opts->present & DL_OPT_PARAM_CMODE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_CMODE, + opts->cmode); + if (opts->present & DL_OPT_REGION_SNAPSHOT_ID) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_REGION_SNAPSHOT_ID, + opts->region_snapshot_id); + if (opts->present & DL_OPT_REGION_ADDRESS) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_ADDR, + opts->region_address); + if (opts->present & DL_OPT_REGION_LENGTH) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_LEN, + opts->region_length); + if (opts->present & DL_OPT_FLASH_FILE_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME, + opts->flash_file_name); + if (opts->present & DL_OPT_FLASH_COMPONENT) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, + opts->flash_component); + if (opts->present & DL_OPT_HEALTH_REPORTER_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_HEALTH_REPORTER_NAME, + opts->reporter_name); + if (opts->present & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD) + mnl_attr_put_u64(nlh, + DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD, + opts->reporter_graceful_period); + if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, + opts->reporter_auto_recover); + if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_DUMP) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, + opts->reporter_auto_dump); + if (opts->present & DL_OPT_TRAP_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME, + opts->trap_name); + if (opts->present & DL_OPT_TRAP_GROUP_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_GROUP_NAME, + opts->trap_group_name); + if (opts->present & DL_OPT_TRAP_ACTION) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_TRAP_ACTION, + opts->trap_action); + if (opts->present & DL_OPT_NETNS) + mnl_attr_put_u32(nlh, + opts->netns_is_pid ? DEVLINK_ATTR_NETNS_PID : + DEVLINK_ATTR_NETNS_FD, + opts->netns); + if (opts->present & DL_OPT_TRAP_POLICER_ID) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_TRAP_POLICER_ID, + opts->trap_policer_id); + if (opts->present & DL_OPT_TRAP_POLICER_RATE) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_RATE, + opts->trap_policer_rate); + if (opts->present & DL_OPT_TRAP_POLICER_BURST) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_BURST, + opts->trap_policer_burst); + if (opts->present & DL_OPT_PORT_FUNCTION_HW_ADDR) + dl_function_attr_put(nlh, opts); +} + +static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, + uint64_t o_required, uint64_t o_optional) +{ + int err; + + err = dl_argv_parse(dl, o_required, o_optional); + if (err) + return err; + dl_opts_put(nlh, dl); + return 0; +} + +static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) +{ + struct dl_opts *opts = &dl->opts; + struct nlattr *attr_bus_name = tb[DEVLINK_ATTR_BUS_NAME]; + struct nlattr *attr_dev_name = tb[DEVLINK_ATTR_DEV_NAME]; + struct nlattr *attr_port_index = tb[DEVLINK_ATTR_PORT_INDEX]; + struct nlattr *attr_sb_index = tb[DEVLINK_ATTR_SB_INDEX]; + + if (opts->present & DL_OPT_HANDLE && + attr_bus_name && attr_dev_name) { + const char *bus_name = mnl_attr_get_str(attr_bus_name); + const char *dev_name = mnl_attr_get_str(attr_dev_name); + + if (strcmp(bus_name, opts->bus_name) != 0 || + strcmp(dev_name, opts->dev_name) != 0) + return false; + } + if (opts->present & DL_OPT_HANDLEP && + attr_bus_name && attr_dev_name && attr_port_index) { + const char *bus_name = mnl_attr_get_str(attr_bus_name); + const char *dev_name = mnl_attr_get_str(attr_dev_name); + uint32_t port_index = mnl_attr_get_u32(attr_port_index); + + if (strcmp(bus_name, opts->bus_name) != 0 || + strcmp(dev_name, opts->dev_name) != 0 || + port_index != opts->port_index) + return false; + } + if (opts->present & DL_OPT_SB && attr_sb_index) { + uint32_t sb_index = mnl_attr_get_u32(attr_sb_index); + + if (sb_index != opts->sb_index) + return false; + } + return true; +} + +static void cmd_dev_help(void) +{ + pr_err("Usage: devlink dev show [ DEV ]\n"); + pr_err(" devlink dev eswitch set DEV [ mode { legacy | switchdev } ]\n"); + pr_err(" [ inline-mode { none | link | network | transport } ]\n"); + pr_err(" [ encap-mode { none | basic } ]\n"); + pr_err(" devlink dev eswitch show DEV\n"); + pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n"); + pr_err(" devlink dev param show [DEV name PARAMETER]\n"); + pr_err(" devlink dev reload DEV [ netns { PID | NAME | ID } ]\n"); + pr_err(" devlink dev info [ DEV ]\n"); + pr_err(" devlink dev flash DEV file PATH [ component NAME ]\n"); +} + +static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + if (!dl->arr_last.present) + return false; + return strcmp(dl->arr_last.bus_name, bus_name) == 0 && + strcmp(dl->arr_last.dev_name, dev_name) == 0; +} + +static void arr_last_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + dl->arr_last.present = true; + free(dl->arr_last.dev_name); + free(dl->arr_last.bus_name); + dl->arr_last.bus_name = strdup(bus_name); + dl->arr_last.dev_name = strdup(dev_name); +} + +static bool should_arr_last_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + return !cmp_arr_last_handle(dl, bus_name, dev_name); +} + +static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name, + const char *dev_name) +{ + return dl->arr_last.present && + !cmp_arr_last_handle(dl, bus_name, dev_name); +} + +static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, + bool content, bool array) +{ + const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + char buf[64]; + + sprintf(buf, "%s/%s", bus_name, dev_name); + + if (dl->json_output) { + if (array) { + if (should_arr_last_handle_end(dl, bus_name, dev_name)) + close_json_array(PRINT_JSON, NULL); + if (should_arr_last_handle_start(dl, bus_name, + dev_name)) { + open_json_array(PRINT_JSON, buf); + open_json_object(NULL); + arr_last_handle_set(dl, bus_name, dev_name); + } else { + open_json_object(NULL); + } + } else { + open_json_object(buf); + } + } else { + if (array) { + if (should_arr_last_handle_end(dl, bus_name, dev_name)) + __pr_out_indent_dec(); + if (should_arr_last_handle_start(dl, bus_name, + dev_name)) { + pr_out("%s%s", buf, content ? ":" : ""); + __pr_out_newline(); + __pr_out_indent_inc(); + arr_last_handle_set(dl, bus_name, dev_name); + } + } else { + pr_out("%s%s", buf, content ? ":" : ""); + } + } +} + +static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, true); +} + +static void pr_out_handle_end(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + __pr_out_newline(); +} + +static void pr_out_handle(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, false, false); + pr_out_handle_end(dl); +} + +static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + return cmp_arr_last_handle(dl, bus_name, dev_name) && + dl->arr_last.port_index == port_index; +} + +static void arr_last_port_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + arr_last_handle_set(dl, bus_name, dev_name); + dl->arr_last.port_index = port_index; +} + +static bool should_arr_last_port_handle_start(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static bool should_arr_last_port_handle_end(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return dl->arr_last.present && + !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name, + uint32_t port_index, bool try_nice, + bool array) +{ + static char buf[64]; + char *ifname = NULL; + + if (dl->no_nice_names || !try_nice || + ifname_map_rev_lookup(dl, bus_name, dev_name, + port_index, &ifname) != 0) + sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index); + else + sprintf(buf, "%s", ifname); + + if (dl->json_output) { + if (array) { + if (should_arr_last_port_handle_end(dl, bus_name, + dev_name, + port_index)) + close_json_array(PRINT_JSON, NULL); + if (should_arr_last_port_handle_start(dl, bus_name, + dev_name, + port_index)) { + open_json_array(PRINT_JSON, buf); + open_json_object(NULL); + arr_last_port_handle_set(dl, bus_name, dev_name, + port_index); + } else { + open_json_object(NULL); + } + } else { + open_json_object(buf); + } + } else { + if (array) { + if (should_arr_last_port_handle_end(dl, bus_name, dev_name, port_index)) + __pr_out_indent_dec(); + if (should_arr_last_port_handle_start(dl, bus_name, + dev_name, port_index)) { + pr_out("%s:", buf); + __pr_out_newline(); + __pr_out_indent_inc(); + arr_last_port_handle_set(dl, bus_name, dev_name, port_index); + } + } else { + pr_out("%s:", buf); + } + } +} + +static void pr_out_port_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice) +{ + const char *bus_name; + const char *dev_name; + uint32_t port_index; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, false); +} + +static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool try_nice) +{ + const char *bus_name; + const char *dev_name; + uint32_t port_index; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, true); +} + +static void pr_out_port_handle_end(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + pr_out("\n"); +} + +static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr) +{ + if (dl->json_output) { + print_uint(PRINT_JSON, "address", NULL, addr); + open_json_array(PRINT_JSON, "data"); + } +} + +static void pr_out_region_chunk_end(struct dl *dl) +{ + if (dl->json_output) + close_json_array(PRINT_JSON, NULL); +} + +static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len, + uint64_t addr) +{ + static uint64_t align_val; + uint32_t i = 0; + + pr_out_region_chunk_start(dl, addr); + while (i < len) { + if (!dl->json_output) + if (!(align_val % 16)) + pr_out("%s%016"PRIx64" ", + align_val ? "\n" : "", + addr); + + align_val++; + + if (dl->json_output) + print_int(PRINT_JSON, NULL, NULL, data[i]); + else + pr_out("%02x ", data[i]); + + addr++; + i++; + } + pr_out_region_chunk_end(dl); +} + +static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats) +{ + struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {}; + int err; + + if (!dl->stats) + return; + + err = mnl_attr_parse_nested(nla_stats, attr_stats_cb, tb); + if (err != MNL_CB_OK) + return; + + pr_out_object_start(dl, "stats"); + pr_out_object_start(dl, "rx"); + if (tb[DEVLINK_ATTR_STATS_RX_BYTES]) + pr_out_u64(dl, "bytes", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_BYTES])); + if (tb[DEVLINK_ATTR_STATS_RX_PACKETS]) + pr_out_u64(dl, "packets", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_PACKETS])); + if (tb[DEVLINK_ATTR_STATS_RX_DROPPED]) + pr_out_u64(dl, "dropped", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_DROPPED])); + pr_out_object_end(dl); + pr_out_object_end(dl); +} + +static const char *param_cmode_name(uint8_t cmode) +{ + switch (cmode) { + case DEVLINK_PARAM_CMODE_RUNTIME: + return PARAM_CMODE_RUNTIME_STR; + case DEVLINK_PARAM_CMODE_DRIVERINIT: + return PARAM_CMODE_DRIVERINIT_STR; + case DEVLINK_PARAM_CMODE_PERMANENT: + return PARAM_CMODE_PERMANENT_STR; + default: return ""; + } +} + +static const char *eswitch_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_MODE_LEGACY: return ESWITCH_MODE_LEGACY; + case DEVLINK_ESWITCH_MODE_SWITCHDEV: return ESWITCH_MODE_SWITCHDEV; + default: return ""; + } +} + +static const char *eswitch_inline_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_INLINE_MODE_NONE: + return ESWITCH_INLINE_MODE_NONE; + case DEVLINK_ESWITCH_INLINE_MODE_LINK: + return ESWITCH_INLINE_MODE_LINK; + case DEVLINK_ESWITCH_INLINE_MODE_NETWORK: + return ESWITCH_INLINE_MODE_NETWORK; + case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: + return ESWITCH_INLINE_MODE_TRANSPORT; + default: + return ""; + } +} + +static const char *eswitch_encap_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_ENCAP_MODE_NONE: + return ESWITCH_ENCAP_MODE_NONE; + case DEVLINK_ESWITCH_ENCAP_MODE_BASIC: + return ESWITCH_ENCAP_MODE_BASIC; + default: + return ""; + } +} + +static void pr_out_eswitch(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, false); + + if (tb[DEVLINK_ATTR_ESWITCH_MODE]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "mode", "mode %s", + eswitch_mode_name(mnl_attr_get_u16( + tb[DEVLINK_ATTR_ESWITCH_MODE]))); + } + if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "inline-mode", "inline-mode %s", + eswitch_inline_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]))); + } + if (tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "encap-mode", "encap-mode %s", + eswitch_encap_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]))); + } + + pr_out_handle_end(dl); +} + +static int cmd_dev_eswitch_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_eswitch(dl, tb); + return MNL_CB_OK; +} + +static int cmd_dev_eswitch_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_GET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + + pr_out_section_start(dl, "dev"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_eswitch_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_dev_eswitch_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, + DL_OPT_ESWITCH_MODE | + DL_OPT_ESWITCH_INLINE_MODE | + DL_OPT_ESWITCH_ENCAP_MODE); + + if (err) + return err; + + if (dl->opts.present == 1) { + pr_err("Need to set at least one option\n"); + return -ENOENT; + } + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_dev_eswitch(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dev_help(); + return 0; + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_dev_eswitch_set(dl); + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_dev_eswitch_show(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +struct param_val_conv { + const char *name; + const char *vstr; + uint32_t vuint; +}; + +static bool param_val_conv_exists(const struct param_val_conv *param_val_conv, + uint32_t len, const char *name) +{ + uint32_t i; + + for (i = 0; i < len; i++) + if (!strcmp(param_val_conv[i].name, name)) + return true; + + return false; +} + +static int +param_val_conv_uint_get(const struct param_val_conv *param_val_conv, + uint32_t len, const char *name, const char *vstr, + uint32_t *vuint) +{ + uint32_t i; + + for (i = 0; i < len; i++) + if (!strcmp(param_val_conv[i].name, name) && + !strcmp(param_val_conv[i].vstr, vstr)) { + *vuint = param_val_conv[i].vuint; + return 0; + } + + return -ENOENT; +} + +static int +param_val_conv_str_get(const struct param_val_conv *param_val_conv, + uint32_t len, const char *name, uint32_t vuint, + const char **vstr) +{ + uint32_t i; + + for (i = 0; i < len; i++) + if (!strcmp(param_val_conv[i].name, name) && + param_val_conv[i].vuint == vuint) { + *vstr = param_val_conv[i].vstr; + return 0; + } + + return -ENOENT; +} + +static const struct param_val_conv param_val_conv[] = { + { + .name = "fw_load_policy", + .vstr = "driver", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER, + }, + { + .name = "fw_load_policy", + .vstr = "flash", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH, + }, + { + .name = "fw_load_policy", + .vstr = "disk", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "unknown", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN, + }, + { + .name = "fw_load_policy", + .vstr = "unknown", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "always", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "never", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "disk", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK, + }, +}; + +#define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv) + +static void pr_out_param_value(struct dl *dl, const char *nla_name, + int nla_type, struct nlattr *nl) +{ + struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *val_attr; + const char *vstr; + bool conv_exists; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_value); + if (err != MNL_CB_OK) + return; + + if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] || + (nla_type != MNL_TYPE_FLAG && + !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA])) + return; + + check_indent_newline(dl); + print_string(PRINT_ANY, "cmode", "cmode %s", + param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]))); + + val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]; + + conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN, + nla_name); + + switch (nla_type) { + case MNL_TYPE_U8: + if (conv_exists) { + err = param_val_conv_str_get(param_val_conv, + PARAM_VAL_CONV_LEN, + nla_name, + mnl_attr_get_u8(val_attr), + &vstr); + if (err) + return; + print_string(PRINT_ANY, "value", " value %s", vstr); + } else { + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u8(val_attr)); + } + break; + case MNL_TYPE_U16: + if (conv_exists) { + err = param_val_conv_str_get(param_val_conv, + PARAM_VAL_CONV_LEN, + nla_name, + mnl_attr_get_u16(val_attr), + &vstr); + if (err) + return; + print_string(PRINT_ANY, "value", " value %s", vstr); + } else { + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u16(val_attr)); + } + break; + case MNL_TYPE_U32: + if (conv_exists) { + err = param_val_conv_str_get(param_val_conv, + PARAM_VAL_CONV_LEN, + nla_name, + mnl_attr_get_u32(val_attr), + &vstr); + if (err) + return; + print_string(PRINT_ANY, "value", " value %s", vstr); + } else { + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u32(val_attr)); + } + break; + case MNL_TYPE_STRING: + print_string(PRINT_ANY, "value", " value %s", + mnl_attr_get_str(val_attr)); + break; + case MNL_TYPE_FLAG: + print_bool(PRINT_ANY, "value", " value %s", val_attr); + break; + } +} + +static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array) +{ + struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *param_value_attr; + const char *nla_name; + int nla_type; + int err; + + err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param); + if (err != MNL_CB_OK) + return; + if (!nla_param[DEVLINK_ATTR_PARAM_NAME] || + !nla_param[DEVLINK_ATTR_PARAM_TYPE] || + !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) + return; + + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]); + + nla_name = mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]); + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s ", nla_name); + if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC]) + print_string(PRINT_ANY, "type", "type %s", "driver-specific"); + else + print_string(PRINT_ANY, "type", "type %s", "generic"); + + pr_out_array_start(dl, "values"); + mnl_attr_for_each_nested(param_value_attr, + nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) { + pr_out_entry_start(dl); + pr_out_param_value(dl, nla_name, nla_type, param_value_attr); + pr_out_entry_end(dl); + } + pr_out_array_end(dl); + pr_out_handle_end(dl); +} + +static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PARAM]) + return MNL_CB_ERROR; + pr_out_param(dl, tb, true); + return MNL_CB_OK; +} + +struct param_ctx { + struct dl *dl; + int nla_type; + union { + uint8_t vu8; + uint16_t vu16; + uint32_t vu32; + const char *vstr; + bool vbool; + } value; +}; + +static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *param_value_attr; + enum devlink_param_cmode cmode; + struct param_ctx *ctx = data; + struct dl *dl = ctx->dl; + int nla_type; + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PARAM]) + return MNL_CB_ERROR; + + err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param); + if (err != MNL_CB_OK) + return MNL_CB_ERROR; + + if (!nla_param[DEVLINK_ATTR_PARAM_TYPE] || + !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) + return MNL_CB_ERROR; + + nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]); + mnl_attr_for_each_nested(param_value_attr, + nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) { + struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *val_attr; + + err = mnl_attr_parse_nested(param_value_attr, + attr_cb, nla_value); + if (err != MNL_CB_OK) + return MNL_CB_ERROR; + + if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] || + (nla_type != MNL_TYPE_FLAG && + !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA])) + return MNL_CB_ERROR; + + cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]); + if (cmode == dl->opts.cmode) { + val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]; + switch (nla_type) { + case MNL_TYPE_U8: + ctx->value.vu8 = mnl_attr_get_u8(val_attr); + break; + case MNL_TYPE_U16: + ctx->value.vu16 = mnl_attr_get_u16(val_attr); + break; + case MNL_TYPE_U32: + ctx->value.vu32 = mnl_attr_get_u32(val_attr); + break; + case MNL_TYPE_STRING: + ctx->value.vstr = mnl_attr_get_str(val_attr); + break; + case MNL_TYPE_FLAG: + ctx->value.vbool = val_attr ? true : false; + break; + } + break; + } + } + ctx->nla_type = nla_type; + return MNL_CB_OK; +} + +static int cmd_dev_param_set(struct dl *dl) +{ + struct param_ctx ctx = {}; + struct nlmsghdr *nlh; + bool conv_exists; + uint32_t val_u32; + uint16_t val_u16; + uint8_t val_u8; + bool val_bool; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE | + DL_OPT_PARAM_NAME | + DL_OPT_PARAM_VALUE | + DL_OPT_PARAM_CMODE, 0); + if (err) + return err; + + /* Get value type */ + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET, + NLM_F_REQUEST | NLM_F_ACK); + dl_opts_put(nlh, dl); + + ctx.dl = dl; + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_set_cb, &ctx); + if (err) + return err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_SET, + NLM_F_REQUEST | NLM_F_ACK); + dl_opts_put(nlh, dl); + + conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN, + dl->opts.param_name); + + mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type); + switch (ctx.nla_type) { + case MNL_TYPE_U8: + if (conv_exists) { + err = param_val_conv_uint_get(param_val_conv, + PARAM_VAL_CONV_LEN, + dl->opts.param_name, + dl->opts.param_value, + &val_u32); + val_u8 = val_u32; + } else { + err = strtouint8_t(dl->opts.param_value, &val_u8); + } + if (err) + goto err_param_value_parse; + if (val_u8 == ctx.value.vu8) + return 0; + mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8); + break; + case MNL_TYPE_U16: + if (conv_exists) { + err = param_val_conv_uint_get(param_val_conv, + PARAM_VAL_CONV_LEN, + dl->opts.param_name, + dl->opts.param_value, + &val_u32); + val_u16 = val_u32; + } else { + err = strtouint16_t(dl->opts.param_value, &val_u16); + } + if (err) + goto err_param_value_parse; + if (val_u16 == ctx.value.vu16) + return 0; + mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16); + break; + case MNL_TYPE_U32: + if (conv_exists) + err = param_val_conv_uint_get(param_val_conv, + PARAM_VAL_CONV_LEN, + dl->opts.param_name, + dl->opts.param_value, + &val_u32); + else + err = strtouint32_t(dl->opts.param_value, &val_u32); + if (err) + goto err_param_value_parse; + if (val_u32 == ctx.value.vu32) + return 0; + mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32); + break; + case MNL_TYPE_FLAG: + err = strtobool(dl->opts.param_value, &val_bool); + if (err) + goto err_param_value_parse; + if (val_bool == ctx.value.vbool) + return 0; + if (val_bool) + mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, + 0, NULL); + break; + case MNL_TYPE_STRING: + mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, + dl->opts.param_value); + if (!strcmp(dl->opts.param_value, ctx.value.vstr)) + return 0; + break; + default: + printf("Value type not supported\n"); + return -ENOTSUP; + } + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); + +err_param_value_parse: + pr_err("Value \"%s\" is not a number or not within range\n", + dl->opts.param_value); + return err; +} + +static int cmd_dev_param_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | + DL_OPT_PARAM_NAME, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "param"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_dev_param(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_dev_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_dev_param_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_dev_param_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} +static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + uint8_t reload_failed = 0; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + + if (tb[DEVLINK_ATTR_RELOAD_FAILED]) + reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]); + + if (reload_failed) { + __pr_out_handle_start(dl, tb, true, false); + check_indent_newline(dl); + print_bool(PRINT_ANY, "reload_failed", "reload_failed %s", true); + pr_out_handle_end(dl); + } else { + pr_out_handle(dl, tb); + } + + return MNL_CB_OK; +} + +static int cmd_dev_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "dev"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_dev_reload(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dev_help(); + return 0; + } + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_NETNS); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh, + const char *name, int type) +{ + struct nlattr *version; + + mnl_attr_for_each(version, nlh, sizeof(struct genlmsghdr)) { + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + const char *ver_value; + const char *ver_name; + int err; + + if (mnl_attr_get_type(version) != type) + continue; + + err = mnl_attr_parse_nested(version, attr_cb, tb); + if (err != MNL_CB_OK) + continue; + + if (!tb[DEVLINK_ATTR_INFO_VERSION_NAME] || + !tb[DEVLINK_ATTR_INFO_VERSION_VALUE]) + continue; + + if (name) { + pr_out_object_start(dl, name); + name = NULL; + } + + ver_name = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_NAME]); + ver_value = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_VALUE]); + + check_indent_newline(dl); + print_string_name_value(ver_name, ver_value); + if (!dl->json_output) + __pr_out_newline(); + } + + if (!name) + pr_out_object_end(dl); +} + +static void pr_out_info(struct dl *dl, const struct nlmsghdr *nlh, + struct nlattr **tb, bool has_versions) +{ + __pr_out_handle_start(dl, tb, true, false); + + __pr_out_indent_inc(); + if (tb[DEVLINK_ATTR_INFO_DRIVER_NAME]) { + struct nlattr *nla_drv = tb[DEVLINK_ATTR_INFO_DRIVER_NAME]; + + if (!dl->json_output) + __pr_out_newline(); + check_indent_newline(dl); + print_string(PRINT_ANY, "driver", "driver %s", + mnl_attr_get_str(nla_drv)); + } + + if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]) { + struct nlattr *nla_sn = tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]; + + if (!dl->json_output) + __pr_out_newline(); + check_indent_newline(dl); + print_string(PRINT_ANY, "serial_number", "serial_number %s", + mnl_attr_get_str(nla_sn)); + } + + if (tb[DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER]) { + struct nlattr *nla_bsn = tb[DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER]; + + if (!dl->json_output) + __pr_out_newline(); + check_indent_newline(dl); + print_string(PRINT_ANY, "board.serial_number", "board.serial_number %s", + mnl_attr_get_str(nla_bsn)); + } + __pr_out_indent_dec(); + + if (has_versions) { + pr_out_object_start(dl, "versions"); + + pr_out_versions_single(dl, nlh, "fixed", + DEVLINK_ATTR_INFO_VERSION_FIXED); + pr_out_versions_single(dl, nlh, "running", + DEVLINK_ATTR_INFO_VERSION_RUNNING); + pr_out_versions_single(dl, nlh, "stored", + DEVLINK_ATTR_INFO_VERSION_STORED); + + pr_out_object_end(dl); + } + + pr_out_handle_end(dl); +} + +static int cmd_versions_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + bool has_versions, has_info; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + + has_versions = tb[DEVLINK_ATTR_INFO_VERSION_FIXED] || + tb[DEVLINK_ATTR_INFO_VERSION_RUNNING] || + tb[DEVLINK_ATTR_INFO_VERSION_STORED]; + has_info = tb[DEVLINK_ATTR_INFO_DRIVER_NAME] || + tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER] || + tb[DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER] || + has_versions; + + if (has_info) + pr_out_info(dl, nlh, tb, has_versions); + + return MNL_CB_OK; +} + +static int cmd_dev_info(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argv_match(dl, "help")) { + cmd_dev_help(); + return 0; + } + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_INFO_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "info"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_versions_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +struct cmd_dev_flash_status_ctx { + struct dl *dl; + char *last_msg; + char *last_component; + uint8_t not_first:1, + last_pc:1, + received_end:1, + flash_done:1; +}; + +static int nullstrcmp(const char *str1, const char *str2) +{ + if (str1 && str2) + return strcmp(str1, str2); + if (!str1 && !str2) + return 0; + return str1 ? 1 : -1; +} + +static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data) +{ + struct cmd_dev_flash_status_ctx *ctx = data; + struct dl_opts *opts = &ctx->dl->opts; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + const char *component = NULL; + uint64_t done = 0, total = 0; + const char *msg = NULL; + const char *bus_name; + const char *dev_name; + + if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS && + genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END) + return MNL_CB_STOP; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + if (strcmp(bus_name, opts->bus_name) || + strcmp(dev_name, opts->dev_name)) + return MNL_CB_ERROR; + + if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) { + pr_out("\n"); + free(ctx->last_msg); + free(ctx->last_component); + ctx->received_end = 1; + return MNL_CB_STOP; + } + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]) + msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) + component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]) + done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]) + total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]); + + if (!nullstrcmp(msg, ctx->last_msg) && + !nullstrcmp(component, ctx->last_component) && + ctx->last_pc && ctx->not_first) { + pr_out_tty("\b\b\b\b\b"); /* clean percentage */ + } else { + if (ctx->not_first) + pr_out("\n"); + if (component) { + pr_out("[%s] ", component); + free(ctx->last_component); + ctx->last_component = strdup(component); + } + if (msg) { + pr_out("%s", msg); + free(ctx->last_msg); + ctx->last_msg = strdup(msg); + } + } + if (total) { + pr_out_tty(" %3lu%%", (done * 100) / total); + ctx->last_pc = 1; + } else { + ctx->last_pc = 0; + } + fflush(stdout); + ctx->not_first = 1; + + return MNL_CB_STOP; +} + +static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx, + struct mnlg_socket *nlg_ntf, + int pipe_r) +{ + int nlfd = mnlg_socket_get_fd(nlg_ntf); + fd_set fds[3]; + int fdmax; + int i; + int err; + int err2; + + for (i = 0; i < 3; i++) + FD_ZERO(&fds[i]); + FD_SET(pipe_r, &fds[0]); + fdmax = pipe_r + 1; + FD_SET(nlfd, &fds[0]); + if (nlfd >= fdmax) + fdmax = nlfd + 1; + + while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) { + if (errno == EINTR) + continue; + pr_err("select() failed\n"); + return -errno; + } + if (FD_ISSET(nlfd, &fds[0])) { + err = _mnlg_socket_recv_run(nlg_ntf, + cmd_dev_flash_status_cb, ctx); + if (err) + return err; + } + if (FD_ISSET(pipe_r, &fds[0])) { + err = read(pipe_r, &err2, sizeof(err2)); + if (err == -1) { + pr_err("Failed to read pipe\n"); + return -errno; + } + if (err2) + return err2; + ctx->flash_done = 1; + } + return 0; +} + + +static int cmd_dev_flash(struct dl *dl) +{ + struct cmd_dev_flash_status_ctx ctx = {.dl = dl,}; + struct mnlg_socket *nlg_ntf; + struct nlmsghdr *nlh; + int pipe_r, pipe_w; + int pipe_fds[2]; + pid_t pid; + int err; + + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dev_help(); + return 0; + } + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME, + DL_OPT_FLASH_COMPONENT); + if (err) + return err; + + nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION); + if (!nlg_ntf) + return err; + + err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME); + if (err) + return err; + + err = pipe(pipe_fds); + if (err == -1) + return -errno; + pipe_r = pipe_fds[0]; + pipe_w = pipe_fds[1]; + + pid = fork(); + if (pid == -1) { + close(pipe_r); + close(pipe_w); + return -errno; + } else if (!pid) { + /* In child, just execute the flash and pass returned + * value through pipe once it is done. + */ + int cc; + + close(pipe_r); + err = _mnlg_socket_send(dl->nlg, nlh); + cc = write(pipe_w, &err, sizeof(err)); + close(pipe_w); + exit(cc != sizeof(err)); + } + close(pipe_w); + + do { + err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r); + if (err) + goto out; + } while (!ctx.flash_done || (ctx.not_first && !ctx.received_end)); + + err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL); +out: + close(pipe_r); + mnlg_socket_close(nlg_ntf); + return err; +} + +static int cmd_dev(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_dev_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_dev_show(dl); + } else if (dl_argv_match(dl, "eswitch")) { + dl_arg_inc(dl); + return cmd_dev_eswitch(dl); + } else if (dl_argv_match(dl, "reload")) { + dl_arg_inc(dl); + return cmd_dev_reload(dl); + } else if (dl_argv_match(dl, "param")) { + dl_arg_inc(dl); + return cmd_dev_param(dl); + } else if (dl_argv_match(dl, "info")) { + dl_arg_inc(dl); + return cmd_dev_info(dl); + } else if (dl_argv_match(dl, "flash")) { + dl_arg_inc(dl); + return cmd_dev_flash(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void cmd_port_help(void) +{ + pr_err("Usage: devlink port show [ DEV/PORT_INDEX ]\n"); + pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); + pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n"); + pr_err(" devlink port unsplit DEV/PORT_INDEX\n"); + pr_err(" devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n"); + pr_err(" devlink port health show [ DEV/PORT_INDEX reporter REPORTER_NAME ]\n"); +} + +static const char *port_type_name(uint32_t type) +{ + switch (type) { + case DEVLINK_PORT_TYPE_NOTSET: return "notset"; + case DEVLINK_PORT_TYPE_AUTO: return "auto"; + case DEVLINK_PORT_TYPE_ETH: return "eth"; + case DEVLINK_PORT_TYPE_IB: return "ib"; + default: return ""; + } +} + +static const char *port_flavour_name(uint16_t flavour) +{ + switch (flavour) { + case DEVLINK_PORT_FLAVOUR_PHYSICAL: + return "physical"; + case DEVLINK_PORT_FLAVOUR_CPU: + return "cpu"; + case DEVLINK_PORT_FLAVOUR_DSA: + return "dsa"; + case DEVLINK_PORT_FLAVOUR_PCI_PF: + return "pcipf"; + case DEVLINK_PORT_FLAVOUR_PCI_VF: + return "pcivf"; + case DEVLINK_PORT_FLAVOUR_VIRTUAL: + return "virtual"; + default: + return ""; + } +} + +static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb) +{ + uint16_t fn_num; + + if (tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) { + fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]); + print_uint(PRINT_ANY, "pfnum", " pfnum %u", fn_num); + } + if (tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]) { + fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]); + print_uint(PRINT_ANY, "vfnum", " vfnum %u", fn_num); + } +} + +static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port) +{ + struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {}; + unsigned char *data; + SPRINT_BUF(hw_addr); + uint32_t len; + int err; + + if (!tb_port[DEVLINK_ATTR_PORT_FUNCTION]) + return; + + err = mnl_attr_parse_nested(tb_port[DEVLINK_ATTR_PORT_FUNCTION], + function_attr_cb, tb); + if (err != MNL_CB_OK) + return; + + if (!tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]) + return; + + len = mnl_attr_get_payload_len(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]); + data = mnl_attr_get_payload(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]); + + pr_out_object_start(dl, "function"); + check_indent_newline(dl); + print_string(PRINT_ANY, "hw_addr", "hw_addr %s", + ll_addr_n2a(data, len, 0, hw_addr, sizeof(hw_addr))); + if (!dl->json_output) + __pr_out_indent_dec(); + pr_out_object_end(dl); +} + +static void pr_out_port(struct dl *dl, struct nlattr **tb) +{ + struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; + struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; + + pr_out_port_handle_start(dl, tb, false); + check_indent_newline(dl); + if (pt_attr) { + uint16_t port_type = mnl_attr_get_u16(pt_attr); + + print_string(PRINT_ANY, "type", "type %s", + port_type_name(port_type)); + if (dpt_attr) { + uint16_t des_port_type = mnl_attr_get_u16(dpt_attr); + + if (port_type != des_port_type) + print_string(PRINT_ANY, "des_type", " des_type %s", + port_type_name(des_port_type)); + } + } + if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) { + print_string(PRINT_ANY, "netdev", " netdev %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); + } + if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) { + print_string(PRINT_ANY, "ibdev", " ibdev %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); + } + if (tb[DEVLINK_ATTR_PORT_FLAVOUR]) { + uint16_t port_flavour = + mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_FLAVOUR]); + + print_string(PRINT_ANY, "flavour", " flavour %s", + port_flavour_name(port_flavour)); + + switch (port_flavour) { + case DEVLINK_PORT_FLAVOUR_PCI_PF: + case DEVLINK_PORT_FLAVOUR_PCI_VF: + pr_out_port_pfvf_num(dl, tb); + break; + default: + break; + } + } + if (tb[DEVLINK_ATTR_PORT_NUMBER]) { + uint32_t port_number; + + port_number = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_NUMBER]); + print_uint(PRINT_ANY, "port", " port %u", port_number); + } + if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]) + print_uint(PRINT_ANY, "split_group", " split_group %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); + if (tb[DEVLINK_ATTR_PORT_SPLITTABLE]) + print_bool(PRINT_ANY, "splittable", " splittable %s", + mnl_attr_get_u8(tb[DEVLINK_ATTR_PORT_SPLITTABLE])); + if (tb[DEVLINK_ATTR_PORT_LANES]) + print_uint(PRINT_ANY, "lanes", " lanes %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_LANES])); + + pr_out_port_function(dl, tb); + pr_out_port_handle_end(dl); +} + +static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + pr_out_port(dl, tb); + return MNL_CB_OK; +} + +static int cmd_port_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "port"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_port_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_TYPE, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port_split(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SPLIT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_COUNT, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port_unsplit(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_UNSPLIT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static void cmd_port_function_help(void) +{ + pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n"); +} + +static int cmd_port_function_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET, NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_FUNCTION_HW_ADDR, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_port_function(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_port_function_help(); + return 0; + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_port_function_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_health(struct dl *dl); +static int __cmd_health_show(struct dl *dl, bool show_device, bool show_port); + +static int cmd_port(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_port_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_port_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_port_set(dl); + } else if (dl_argv_match(dl, "split")) { + dl_arg_inc(dl); + return cmd_port_split(dl); + } else if (dl_argv_match(dl, "unsplit")) { + dl_arg_inc(dl); + return cmd_port_unsplit(dl); + } else if (dl_argv_match(dl, "function")) { + dl_arg_inc(dl); + return cmd_port_function(dl); + } else if (dl_argv_match(dl, "health")) { + dl_arg_inc(dl); + if (dl_argv_match(dl, "list") || dl_no_arg(dl) + || (dl_argv_match(dl, "show") && dl_argc(dl) == 1)) { + dl_arg_inc(dl); + return __cmd_health_show(dl, false, true); + } else { + return cmd_health(dl); + } + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void cmd_sb_help(void) +{ + pr_err("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n"); + pr_err(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n"); + pr_err(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n"); + pr_err(" size POOL_SIZE thtype { static | dynamic }\n"); + pr_err(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX ]\n"); + pr_err(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX th THRESHOLD\n"); + pr_err(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } ]\n"); + pr_err(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } pool POOL_INDEX\n"); + pr_err(" th THRESHOLD\n"); + pr_err(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n"); +} + +static void pr_out_sb(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle_start_arr(dl, tb); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "size", " size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); + print_uint(PRINT_ANY, "ing_pools", " ing_pools %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); + print_uint(PRINT_ANY, "eg_pools", " eg_pools %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); + print_uint(PRINT_ANY, "ing_tcs", " ing_tcs %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); + print_uint(PRINT_ANY, "eg_tcs", " eg_tcs %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); + pr_out_handle_end(dl); +} + +static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] || + !tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] || + !tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] || + !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] || + !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]) + return MNL_CB_ERROR; + pr_out_sb(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "sb"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static const char *pool_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress"; + case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress"; + default: return ""; + } +} + +static const char *threshold_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static"; + case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic"; + default: return ""; + } +} + +static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle_start_arr(dl, tb); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_string(PRINT_ANY, "type", " type %s", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + print_uint(PRINT_ANY, "size", " size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); + print_string(PRINT_ANY, "thtype", " thtype %s", + threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + if (tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE]) + print_uint(PRINT_ANY, "cell_size", " cell size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE])); + pr_out_handle_end(dl); +} + +static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] || + !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]) + return MNL_CB_ERROR; + pr_out_sb_pool(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_pool_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL, + DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_sb_pool_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL | + DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_pool(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_pool_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_pool_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb) +{ + pr_out_port_handle_start_arr(dl, tb, true); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_uint(PRINT_ANY, "threshold", " threshold %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); +} + +static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD]) + return MNL_CB_ERROR; + pr_out_sb_port_pool(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_port_pool_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLEP | DL_OPT_SB_POOL, + DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "port_pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl); + pr_out_section_end(dl); + return 0; +} + +static int cmd_sb_port_pool_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL | + DL_OPT_SB_TH, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_port_pool(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_port_pool_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_port_pool_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb_port(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "pool")) { + dl_arg_inc(dl); + return cmd_sb_port_pool(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb) +{ + pr_out_port_handle_start_arr(dl, tb, true); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "tc", " tc %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); + print_string(PRINT_ANY, "type", " type %s", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_uint(PRINT_ANY, "threshold", " threshold %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); +} + +static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD]) + return MNL_CB_ERROR; + pr_out_sb_tc_bind(dl, tb); + return MNL_CB_OK; +} + +static int cmd_sb_tc_bind_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC | + DL_OPT_SB_TYPE, DL_OPT_SB); + if (err) + return err; + } + + pr_out_section_start(dl, "tc_bind"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_sb_tc_bind_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC | + DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH, + DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_tc_bind(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_tc_bind_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_sb_tc_bind_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb_tc(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "bind")) { + dl_arg_inc(dl); + return cmd_sb_tc_bind(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +struct occ_item { + struct list_head list; + uint32_t index; + uint32_t cur; + uint32_t max; + uint32_t bound_pool_index; +}; + +struct occ_port { + struct list_head list; + char *bus_name; + char *dev_name; + uint32_t port_index; + uint32_t sb_index; + struct list_head pool_list; + struct list_head ing_tc_list; + struct list_head eg_tc_list; +}; + +struct occ_show { + struct dl *dl; + int err; + struct list_head port_list; +}; + +static struct occ_item *occ_item_alloc(void) +{ + return calloc(1, sizeof(struct occ_item)); +} + +static void occ_item_free(struct occ_item *occ_item) +{ + free(occ_item); +} + +static struct occ_port *occ_port_alloc(uint32_t port_index) +{ + struct occ_port *occ_port; + + occ_port = calloc(1, sizeof(*occ_port)); + if (!occ_port) + return NULL; + occ_port->port_index = port_index; + INIT_LIST_HEAD(&occ_port->pool_list); + INIT_LIST_HEAD(&occ_port->ing_tc_list); + INIT_LIST_HEAD(&occ_port->eg_tc_list); + return occ_port; +} + +static void occ_port_free(struct occ_port *occ_port) +{ + struct occ_item *occ_item, *tmp; + + list_for_each_entry_safe(occ_item, tmp, &occ_port->pool_list, list) + occ_item_free(occ_item); + list_for_each_entry_safe(occ_item, tmp, &occ_port->ing_tc_list, list) + occ_item_free(occ_item); + list_for_each_entry_safe(occ_item, tmp, &occ_port->eg_tc_list, list) + occ_item_free(occ_item); +} + +static struct occ_show *occ_show_alloc(struct dl *dl) +{ + struct occ_show *occ_show; + + occ_show = calloc(1, sizeof(*occ_show)); + if (!occ_show) + return NULL; + occ_show->dl = dl; + INIT_LIST_HEAD(&occ_show->port_list); + return occ_show; +} + +static void occ_show_free(struct occ_show *occ_show) +{ + struct occ_port *occ_port, *tmp; + + list_for_each_entry_safe(occ_port, tmp, &occ_show->port_list, list) + occ_port_free(occ_port); +} + +static struct occ_port *occ_port_get(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + uint32_t port_index; + + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + + list_for_each_entry_reverse(occ_port, &occ_show->port_list, list) { + if (occ_port->port_index == port_index) + return occ_port; + } + occ_port = occ_port_alloc(port_index); + if (!occ_port) + return NULL; + list_add_tail(&occ_port->list, &occ_show->port_list); + return occ_port; +} + +static void pr_out_occ_show_item_list(const char *label, struct list_head *list, + bool bound_pool) +{ + struct occ_item *occ_item; + int i = 1; + + pr_out_sp(7, " %s:", label); + list_for_each_entry(occ_item, list, list) { + if ((i - 1) % 4 == 0 && i != 1) + pr_out_sp(7, " "); + if (bound_pool) + pr_out_sp(7, "%2u(%u):", occ_item->index, + occ_item->bound_pool_index); + else + pr_out_sp(7, "%2u:", occ_item->index); + pr_out_sp(21, "%10u/%u", occ_item->cur, occ_item->max); + if (i++ % 4 == 0) + pr_out("\n"); + } + if ((i - 1) % 4 != 0) + pr_out("\n"); +} + +static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label, + struct list_head *list, + bool bound_pool) +{ + struct occ_item *occ_item; + char buf[32]; + + open_json_object(label); + list_for_each_entry(occ_item, list, list) { + sprintf(buf, "%u", occ_item->index); + open_json_object(buf); + if (bound_pool) + print_uint(PRINT_JSON, "bound_pool", NULL, + occ_item->bound_pool_index); + print_uint(PRINT_JSON, "current", NULL, occ_item->cur); + print_uint(PRINT_JSON, "max", NULL, occ_item->max); + close_json_object(); + } + close_json_object(); +} + +static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port) +{ + if (dl->json_output) { + pr_out_json_occ_show_item_list(dl, "pool", + &occ_port->pool_list, false); + pr_out_json_occ_show_item_list(dl, "itc", + &occ_port->ing_tc_list, true); + pr_out_json_occ_show_item_list(dl, "etc", + &occ_port->eg_tc_list, true); + } else { + pr_out("\n"); + pr_out_occ_show_item_list("pool", &occ_port->pool_list, false); + pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true); + pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true); + } +} + +static void pr_out_occ_show(struct occ_show *occ_show) +{ + struct dl *dl = occ_show->dl; + struct dl_opts *opts = &dl->opts; + struct occ_port *occ_port; + + list_for_each_entry(occ_port, &occ_show->port_list, list) { + __pr_out_port_handle_start(dl, opts->bus_name, opts->dev_name, + occ_port->port_index, true, false); + pr_out_occ_show_port(dl, occ_port); + pr_out_port_handle_end(dl); + } +} + +static void cmd_sb_occ_port_pool_process(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + struct occ_item *occ_item; + + if (occ_show->err || !dl_dump_filter(occ_show->dl, tb)) + return; + + occ_port = occ_port_get(occ_show, tb); + if (!occ_port) { + occ_show->err = -ENOMEM; + return; + } + + occ_item = occ_item_alloc(); + if (!occ_item) { + occ_show->err = -ENOMEM; + return; + } + occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]); + occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]); + occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]); + list_add_tail(&occ_item->list, &occ_port->pool_list); +} + +static int cmd_sb_occ_port_pool_process_cb(const struct nlmsghdr *nlh, void *data) +{ + struct occ_show *occ_show = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX]) + return MNL_CB_ERROR; + cmd_sb_occ_port_pool_process(occ_show, tb); + return MNL_CB_OK; +} + +static void cmd_sb_occ_tc_pool_process(struct occ_show *occ_show, + struct nlattr **tb) +{ + struct occ_port *occ_port; + struct occ_item *occ_item; + uint8_t pool_type; + + if (occ_show->err || !dl_dump_filter(occ_show->dl, tb)) + return; + + occ_port = occ_port_get(occ_show, tb); + if (!occ_port) { + occ_show->err = -ENOMEM; + return; + } + + occ_item = occ_item_alloc(); + if (!occ_item) { + occ_show->err = -ENOMEM; + return; + } + occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]); + occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]); + occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]); + occ_item->bound_pool_index = + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]); + pool_type = mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]); + if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS) + list_add_tail(&occ_item->list, &occ_port->ing_tc_list); + else if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS) + list_add_tail(&occ_item->list, &occ_port->eg_tc_list); + else + occ_item_free(occ_item); +} + +static int cmd_sb_occ_tc_pool_process_cb(const struct nlmsghdr *nlh, void *data) +{ + struct occ_show *occ_show = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] || + !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] || + !tb[DEVLINK_ATTR_SB_POOL_INDEX] || + !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX]) + return MNL_CB_ERROR; + cmd_sb_occ_tc_pool_process(occ_show, tb); + return MNL_CB_OK; +} + +static int cmd_sb_occ_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct occ_show *occ_show; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP, DL_OPT_SB); + if (err) + return err; + + occ_show = occ_show_alloc(dl); + if (!occ_show) + return -ENOMEM; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, + cmd_sb_occ_port_pool_process_cb, occ_show); + if (err) + goto out; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, + cmd_sb_occ_tc_pool_process_cb, occ_show); + if (err) + goto out; + + pr_out_section_start(dl, "occupancy"); + pr_out_occ_show(occ_show); + pr_out_section_end(dl); + +out: + occ_show_free(occ_show); + return err; +} + +static int cmd_sb_occ_snapshot(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_SNAPSHOT, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_occ_clearmax(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_MAX_CLEAR, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_sb_occ(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list")) { + dl_arg_inc(dl); + return cmd_sb_occ_show(dl); + } else if (dl_argv_match(dl, "snapshot")) { + dl_arg_inc(dl); + return cmd_sb_occ_snapshot(dl); + } else if (dl_argv_match(dl, "clearmax")) { + dl_arg_inc(dl); + return cmd_sb_occ_clearmax(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_sb(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_sb_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_sb_show(dl); + } else if (dl_argv_match(dl, "pool")) { + dl_arg_inc(dl); + return cmd_sb_pool(dl); + } else if (dl_argv_match(dl, "port")) { + dl_arg_inc(dl); + return cmd_sb_port(dl); + } else if (dl_argv_match(dl, "tc")) { + dl_arg_inc(dl); + return cmd_sb_tc(dl); + } else if (dl_argv_match(dl, "occupancy")) { + dl_arg_inc(dl); + return cmd_sb_occ(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static const char *cmd_name(uint8_t cmd) +{ + switch (cmd) { + case DEVLINK_CMD_UNSPEC: return "unspec"; + case DEVLINK_CMD_GET: return "get"; + case DEVLINK_CMD_SET: return "set"; + case DEVLINK_CMD_NEW: return "new"; + case DEVLINK_CMD_DEL: return "del"; + case DEVLINK_CMD_PORT_GET: return "get"; + case DEVLINK_CMD_PORT_SET: return "set"; + case DEVLINK_CMD_PORT_NEW: return "new"; + case DEVLINK_CMD_PORT_DEL: return "del"; + case DEVLINK_CMD_PARAM_GET: return "get"; + case DEVLINK_CMD_PARAM_SET: return "set"; + case DEVLINK_CMD_PARAM_NEW: return "new"; + case DEVLINK_CMD_PARAM_DEL: return "del"; + case DEVLINK_CMD_REGION_GET: return "get"; + case DEVLINK_CMD_REGION_SET: return "set"; + case DEVLINK_CMD_REGION_NEW: return "new"; + case DEVLINK_CMD_REGION_DEL: return "del"; + case DEVLINK_CMD_FLASH_UPDATE: return "begin"; + case DEVLINK_CMD_FLASH_UPDATE_END: return "end"; + case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status"; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: return "status"; + case DEVLINK_CMD_TRAP_GET: return "get"; + case DEVLINK_CMD_TRAP_SET: return "set"; + case DEVLINK_CMD_TRAP_NEW: return "new"; + case DEVLINK_CMD_TRAP_DEL: return "del"; + case DEVLINK_CMD_TRAP_GROUP_GET: return "get"; + case DEVLINK_CMD_TRAP_GROUP_SET: return "set"; + case DEVLINK_CMD_TRAP_GROUP_NEW: return "new"; + case DEVLINK_CMD_TRAP_GROUP_DEL: return "del"; + case DEVLINK_CMD_TRAP_POLICER_GET: return "get"; + case DEVLINK_CMD_TRAP_POLICER_SET: return "set"; + case DEVLINK_CMD_TRAP_POLICER_NEW: return "new"; + case DEVLINK_CMD_TRAP_POLICER_DEL: return "del"; + default: return ""; + } +} + +static const char *cmd_obj(uint8_t cmd) +{ + switch (cmd) { + case DEVLINK_CMD_UNSPEC: return "unspec"; + case DEVLINK_CMD_GET: + case DEVLINK_CMD_SET: + case DEVLINK_CMD_NEW: + case DEVLINK_CMD_DEL: + return "dev"; + case DEVLINK_CMD_PORT_GET: + case DEVLINK_CMD_PORT_SET: + case DEVLINK_CMD_PORT_NEW: + case DEVLINK_CMD_PORT_DEL: + return "port"; + case DEVLINK_CMD_PARAM_GET: + case DEVLINK_CMD_PARAM_SET: + case DEVLINK_CMD_PARAM_NEW: + case DEVLINK_CMD_PARAM_DEL: + return "param"; + case DEVLINK_CMD_REGION_GET: + case DEVLINK_CMD_REGION_SET: + case DEVLINK_CMD_REGION_NEW: + case DEVLINK_CMD_REGION_DEL: + return "region"; + case DEVLINK_CMD_FLASH_UPDATE: + case DEVLINK_CMD_FLASH_UPDATE_END: + case DEVLINK_CMD_FLASH_UPDATE_STATUS: + return "flash"; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: + return "health"; + case DEVLINK_CMD_TRAP_GET: + case DEVLINK_CMD_TRAP_SET: + case DEVLINK_CMD_TRAP_NEW: + case DEVLINK_CMD_TRAP_DEL: + return "trap"; + case DEVLINK_CMD_TRAP_GROUP_GET: + case DEVLINK_CMD_TRAP_GROUP_SET: + case DEVLINK_CMD_TRAP_GROUP_NEW: + case DEVLINK_CMD_TRAP_GROUP_DEL: + return "trap-group"; + case DEVLINK_CMD_TRAP_POLICER_GET: + case DEVLINK_CMD_TRAP_POLICER_SET: + case DEVLINK_CMD_TRAP_POLICER_NEW: + case DEVLINK_CMD_TRAP_POLICER_DEL: + return "trap-policer"; + default: return ""; + } +} + +static void pr_out_mon_header(uint8_t cmd) +{ + if (!is_json_context()) { + pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd)); + } else { + open_json_object(NULL); + print_string(PRINT_JSON, "command", NULL, cmd_name(cmd)); + open_json_object(cmd_obj(cmd)); + } +} + +static void pr_out_mon_footer(void) +{ + if (is_json_context()) { + close_json_object(); + close_json_object(); + } +} + +static bool cmd_filter_check(struct dl *dl, uint8_t cmd) +{ + const char *obj = cmd_obj(cmd); + unsigned int index = 0; + const char *cur_obj; + + if (dl_no_arg(dl)) + return true; + while ((cur_obj = dl_argv_index(dl, index++))) { + if (strcmp(cur_obj, obj) == 0 || strcmp(cur_obj, "all") == 0) + return true; + } + return false; +} + +static void pr_out_flash_update(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, false); + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "msg", "msg %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])); + } + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "component", "component %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])); + } + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]) + pr_out_u64(dl, "done", + mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])); + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]) + pr_out_u64(dl, "total", + mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])); + + pr_out_handle_end(dl); +} + +static void pr_out_region(struct dl *dl, struct nlattr **tb); +static void pr_out_health(struct dl *dl, struct nlattr **tb_health, + bool show_device, bool show_port); +static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array); +static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array); +static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array); + +static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dl *dl = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + uint8_t cmd = genl->cmd; + + if (!cmd_filter_check(dl, cmd)) + return MNL_CB_OK; + + switch (cmd) { + case DEVLINK_CMD_GET: /* fall through */ + case DEVLINK_CMD_SET: /* fall through */ + case DEVLINK_CMD_NEW: /* fall through */ + case DEVLINK_CMD_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_handle(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_PORT_GET: /* fall through */ + case DEVLINK_CMD_PORT_SET: /* fall through */ + case DEVLINK_CMD_PORT_NEW: /* fall through */ + case DEVLINK_CMD_PORT_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PORT_INDEX]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_port(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_PARAM_GET: /* fall through */ + case DEVLINK_CMD_PARAM_SET: /* fall through */ + case DEVLINK_CMD_PARAM_NEW: /* fall through */ + case DEVLINK_CMD_PARAM_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_PARAM]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_param(dl, tb, false); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_REGION_GET: /* fall through */ + case DEVLINK_CMD_REGION_SET: /* fall through */ + case DEVLINK_CMD_REGION_NEW: /* fall through */ + case DEVLINK_CMD_REGION_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_REGION_NAME]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_region(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_FLASH_UPDATE: /* fall through */ + case DEVLINK_CMD_FLASH_UPDATE_END: /* fall through */ + case DEVLINK_CMD_FLASH_UPDATE_STATUS: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_flash_update(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_health(dl, tb, true, true); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_GET: /* fall through */ + case DEVLINK_CMD_TRAP_SET: /* fall through */ + case DEVLINK_CMD_TRAP_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_NAME] || + !tb[DEVLINK_ATTR_TRAP_TYPE] || + !tb[DEVLINK_ATTR_TRAP_ACTION] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_TRAP_METADATA] || + !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap(dl, tb, false); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_GROUP_GET: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_SET: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap_group(dl, tb, false); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_POLICER_GET: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_SET: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_DEL: /* fall through */ + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_POLICER_ID] || + !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] || + !tb[DEVLINK_ATTR_TRAP_POLICER_BURST]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap_policer(dl, tb, false); + break; + } + fflush(stdout); + return MNL_CB_OK; +} + +static int cmd_mon_show(struct dl *dl) +{ + int err; + unsigned int index = 0; + const char *cur_obj; + + while ((cur_obj = dl_argv_index(dl, index++))) { + if (strcmp(cur_obj, "all") != 0 && + strcmp(cur_obj, "dev") != 0 && + strcmp(cur_obj, "port") != 0 && + strcmp(cur_obj, "health") != 0 && + strcmp(cur_obj, "trap") != 0 && + strcmp(cur_obj, "trap-group") != 0 && + strcmp(cur_obj, "trap-policer") != 0) { + pr_err("Unknown object \"%s\"\n", cur_obj); + return -EINVAL; + } + } + err = _mnlg_socket_group_add(dl->nlg, DEVLINK_GENL_MCGRP_CONFIG_NAME); + if (err) + return err; + open_json_object(NULL); + open_json_array(PRINT_JSON, "mon"); + err = _mnlg_socket_recv_run_intr(dl->nlg, cmd_mon_show_cb, dl); + close_json_array(PRINT_JSON, NULL); + close_json_object(); + if (err) + return err; + return 0; +} + +static void cmd_mon_help(void) +{ + pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" + "where OBJECT-LIST := { dev | port | health | trap | trap-group | trap-policer }\n"); +} + +static int cmd_mon(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_mon_help(); + return 0; + } + return cmd_mon_show(dl); +} + +struct dpipe_field { + char *name; + unsigned int id; + unsigned int bitwidth; + enum devlink_dpipe_field_mapping_type mapping_type; +}; + +struct dpipe_header { + struct list_head list; + char *name; + unsigned int id; + struct dpipe_field *fields; + unsigned int fields_count; +}; + +struct dpipe_table { + struct list_head list; + char *name; + unsigned int resource_id; + bool resource_valid; +}; + +struct dpipe_tables { + struct list_head table_list; +}; + +struct resource { + char *name; + uint64_t size; + uint64_t size_new; + uint64_t size_min; + uint64_t size_max; + uint64_t size_gran; + enum devlink_resource_unit unit; + bool size_valid; + uint64_t size_occ; + bool occ_valid; + uint64_t id; + struct list_head list; + struct list_head resource_list; + struct resource *parent; +}; + +struct resources { + struct list_head resource_list; +}; + +struct resource_ctx { + struct dl *dl; + int err; + struct resources *resources; + struct dpipe_tables *tables; + bool print_resources; + bool pending_change; +}; + +static struct resource *resource_alloc(void) +{ + struct resource *resource; + + resource = calloc(1, sizeof(struct resource)); + if (!resource) + return NULL; + INIT_LIST_HEAD(&resource->resource_list); + return resource; +} + +static void resource_free(struct resource *resource) +{ + struct resource *child_resource, *tmp; + + list_for_each_entry_safe(child_resource, tmp, &resource->resource_list, + list) { + free(child_resource->name); + resource_free(child_resource); + } + free(resource); +} + +static struct resources *resources_alloc(void) +{ + struct resources *resources; + + resources = calloc(1, sizeof(struct resources)); + if (!resources) + return NULL; + INIT_LIST_HEAD(&resources->resource_list); + return resources; +} + +static void resources_free(struct resources *resources) +{ + struct resource *resource, *tmp; + + list_for_each_entry_safe(resource, tmp, &resources->resource_list, list) + resource_free(resource); +} + +static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl) +{ + ctx->resources = resources_alloc(); + if (!ctx->resources) + return -ENOMEM; + ctx->dl = dl; + return 0; +} + +static void resource_ctx_fini(struct resource_ctx *ctx) +{ + resources_free(ctx->resources); +} + +struct dpipe_ctx { + struct dl *dl; + int err; + struct list_head global_headers; + struct list_head local_headers; + struct dpipe_tables *tables; + struct resources *resources; + bool print_headers; + bool print_tables; +}; + +static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count) +{ + struct dpipe_header *header; + + header = calloc(1, sizeof(struct dpipe_header)); + if (!header) + return NULL; + header->fields = calloc(fields_count, sizeof(struct dpipe_field)); + if (!header->fields) + goto err_fields_alloc; + header->fields_count = fields_count; + return header; + +err_fields_alloc: + free(header); + return NULL; +} + +static void dpipe_header_free(struct dpipe_header *header) +{ + free(header->fields); + free(header); +} + +static void dpipe_header_clear(struct dpipe_header *header) +{ + struct dpipe_field *field; + int i; + + for (i = 0; i < header->fields_count; i++) { + field = &header->fields[i]; + free(field->name); + } + free(header->name); +} + +static void dpipe_header_add(struct dpipe_ctx *ctx, + struct dpipe_header *header, bool global) +{ + if (global) + list_add(&header->list, &ctx->global_headers); + else + list_add(&header->list, &ctx->local_headers); +} + +static void dpipe_header_del(struct dpipe_header *header) +{ + list_del(&header->list); +} + +static struct dpipe_table *dpipe_table_alloc(void) +{ + return calloc(1, sizeof(struct dpipe_table)); +} + +static void dpipe_table_free(struct dpipe_table *table) +{ + free(table); +} + +static struct dpipe_tables *dpipe_tables_alloc(void) +{ + struct dpipe_tables *tables; + + tables = calloc(1, sizeof(struct dpipe_tables)); + if (!tables) + return NULL; + INIT_LIST_HEAD(&tables->table_list); + return tables; +} + +static void dpipe_tables_free(struct dpipe_tables *tables) +{ + struct dpipe_table *table, *tmp; + + list_for_each_entry_safe(table, tmp, &tables->table_list, list) + dpipe_table_free(table); + free(tables); +} + +static int dpipe_ctx_init(struct dpipe_ctx *ctx, struct dl *dl) +{ + ctx->tables = dpipe_tables_alloc(); + if (!ctx->tables) + return -ENOMEM; + + ctx->dl = dl; + INIT_LIST_HEAD(&ctx->global_headers); + INIT_LIST_HEAD(&ctx->local_headers); + return 0; +} + +static void dpipe_ctx_fini(struct dpipe_ctx *ctx) +{ + struct dpipe_header *header, *tmp; + + list_for_each_entry_safe(header, tmp, &ctx->global_headers, + list) { + dpipe_header_del(header); + dpipe_header_clear(header); + dpipe_header_free(header); + } + list_for_each_entry_safe(header, tmp, &ctx->local_headers, + list) { + dpipe_header_del(header); + dpipe_header_clear(header); + dpipe_header_free(header); + } + dpipe_tables_free(ctx->tables); +} + +static const char *dpipe_header_id2s(struct dpipe_ctx *ctx, + uint32_t header_id, bool global) +{ + struct list_head *header_list; + struct dpipe_header *header; + + if (global) + header_list = &ctx->global_headers; + else + header_list = &ctx->local_headers; + list_for_each_entry(header, header_list, list) { + if (header->id != header_id) + continue; + return header->name; + } + return NULL; +} + +static const char *dpipe_field_id2s(struct dpipe_ctx *ctx, + uint32_t header_id, + uint32_t field_id, bool global) +{ + struct list_head *header_list; + struct dpipe_header *header; + + if (global) + header_list = &ctx->global_headers; + else + header_list = &ctx->local_headers; + list_for_each_entry(header, header_list, list) { + if (header->id != header_id) + continue; + return header->fields[field_id].name; + } + return NULL; +} + +static const char * +dpipe_field_mapping_e2s(enum devlink_dpipe_field_mapping_type mapping_type) +{ + switch (mapping_type) { + case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE: + return NULL; + case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX: + return "ifindex"; + default: + return ""; + } +} + +static const char * +dpipe_mapping_get(struct dpipe_ctx *ctx, uint32_t header_id, + uint32_t field_id, bool global) +{ + enum devlink_dpipe_field_mapping_type mapping_type; + struct list_head *header_list; + struct dpipe_header *header; + + if (global) + header_list = &ctx->global_headers; + else + header_list = &ctx->local_headers; + list_for_each_entry(header, header_list, list) { + if (header->id != header_id) + continue; + mapping_type = header->fields[field_id].mapping_type; + return dpipe_field_mapping_e2s(mapping_type); + } + return NULL; +} + +static void pr_out_dpipe_fields(struct dpipe_ctx *ctx, + struct dpipe_field *fields, + unsigned int field_count) +{ + struct dpipe_field *field; + int i; + + for (i = 0; i < field_count; i++) { + field = &fields[i]; + pr_out_entry_start(ctx->dl); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", field->name); + if (ctx->dl->verbose) + print_uint(PRINT_ANY, "id", " id %u", field->id); + print_uint(PRINT_ANY, "bitwidth", " bitwidth %u", field->bitwidth); + if (field->mapping_type) { + print_string(PRINT_ANY, "mapping_type", " mapping_type %s", + dpipe_field_mapping_e2s(field->mapping_type)); + } + pr_out_entry_end(ctx->dl); + } +} + +static void +pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb, + struct dpipe_header *header, bool global) +{ + pr_out_handle_start_arr(ctx->dl, tb); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", header->name); + if (ctx->dl->verbose) { + print_uint(PRINT_ANY, "id", " id %u", header->id); + print_bool(PRINT_ANY, "global", " global %s", global); + } + pr_out_array_start(ctx->dl, "field"); + pr_out_dpipe_fields(ctx, header->fields, + header->fields_count); + pr_out_array_end(ctx->dl); + pr_out_handle_end(ctx->dl); +} + +static void pr_out_dpipe_headers(struct dpipe_ctx *ctx, + struct nlattr **tb) +{ + struct dpipe_header *header; + + list_for_each_entry(header, &ctx->local_headers, list) + pr_out_dpipe_header(ctx, tb, header, false); + + list_for_each_entry(header, &ctx->global_headers, list) + pr_out_dpipe_header(ctx, tb, header, true); +} + +static int dpipe_header_field_get(struct nlattr *nl, struct dpipe_field *field) +{ + struct nlattr *nla_field[DEVLINK_ATTR_MAX + 1] = {}; + const char *name; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_field); + if (err != MNL_CB_OK) + return -EINVAL; + if (!nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID] || + !nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME] || + !nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] || + !nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]) + return -EINVAL; + + name = mnl_attr_get_str(nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME]); + field->id = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID]); + field->bitwidth = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH]); + field->name = strdup(name); + if (!field->name) + return -ENOMEM; + field->mapping_type = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]); + return 0; +} + +static int dpipe_header_fields_get(struct nlattr *nla_fields, + struct dpipe_field *fields) +{ + struct nlattr *nla_field; + int count = 0; + int err; + + mnl_attr_for_each_nested(nla_field, nla_fields) { + err = dpipe_header_field_get(nla_field, &fields[count]); + if (err) + return err; + count++; + } + return 0; +} + +static unsigned int dpipe_header_field_count_get(struct nlattr *nla_fields) +{ + struct nlattr *nla_field; + unsigned int count = 0; + + mnl_attr_for_each_nested(nla_field, nla_fields) + count++; + return count; +} + +static int dpipe_header_get(struct dpipe_ctx *ctx, struct nlattr *nl) +{ + struct nlattr *nla_header[DEVLINK_ATTR_MAX + 1] = {}; + struct dpipe_header *header; + unsigned int fields_count; + const char *header_name; + bool global; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_header); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME] || + !nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID] || + !nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]) + return -EINVAL; + + fields_count = dpipe_header_field_count_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]); + header = dpipe_header_alloc(fields_count); + if (!header) + return -ENOMEM; + + header_name = mnl_attr_get_str(nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME]); + header->name = strdup(header_name); + header->id = mnl_attr_get_u32(nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID]); + header->fields_count = fields_count; + global = !!mnl_attr_get_u8(nla_header[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]); + + err = dpipe_header_fields_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS], + header->fields); + if (err) + goto err_field_get; + dpipe_header_add(ctx, header, global); + return 0; + +err_field_get: + dpipe_header_free(header); + return err; +} + +static int dpipe_headers_get(struct dpipe_ctx *ctx, struct nlattr **tb) +{ + struct nlattr *nla_headers = tb[DEVLINK_ATTR_DPIPE_HEADERS]; + struct nlattr *nla_header; + int err; + + mnl_attr_for_each_nested(nla_header, nla_headers) { + err = dpipe_header_get(ctx, nla_header); + if (err) + return err; + } + return 0; +} + +static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dpipe_ctx *ctx = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_DPIPE_HEADERS]) + return MNL_CB_ERROR; + err = dpipe_headers_get(ctx, tb); + if (err) { + ctx->err = err; + return MNL_CB_ERROR; + } + + if (ctx->print_headers) + pr_out_dpipe_headers(ctx, tb); + return MNL_CB_OK; +} + +static int cmd_dpipe_headers_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct dpipe_ctx ctx = {}; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + if (err) + return err; + + err = dpipe_ctx_init(&ctx, dl); + if (err) + return err; + + ctx.print_headers = true; + + pr_out_section_start(dl, "header"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx); + if (err) + pr_err("error get headers %s\n", strerror(ctx.err)); + pr_out_section_end(dl); + + dpipe_ctx_fini(&ctx); + return err; +} + +static void cmd_dpipe_help(void) +{ + pr_err("Usage: devlink dpipe table show DEV [ name TABLE_NAME ]\n"); + pr_err(" devlink dpipe table set DEV name TABLE_NAME\n"); + pr_err(" [ counters_enabled { true | false } ]\n"); + pr_err(" devlink dpipe table dump DEV name TABLE_NAME\n"); + pr_err(" devlink dpipe header show DEV\n"); +} + +static int cmd_dpipe_header(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dpipe_help(); + return 0; + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_dpipe_headers_show(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static const char +*dpipe_action_type_e2s(enum devlink_dpipe_action_type action_type) +{ + switch (action_type) { + case DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY: + return "field_modify"; + default: + return ""; + } +} + +struct dpipe_op_info { + uint32_t header_id; + uint32_t field_id; + bool header_global; +}; + +struct dpipe_action { + struct dpipe_op_info info; + uint32_t type; +}; + +static void pr_out_dpipe_action(struct dpipe_action *action, + struct dpipe_ctx *ctx) +{ + struct dpipe_op_info *op_info = &action->info; + const char *mapping; + + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "type", "type %s", + dpipe_action_type_e2s(action->type)); + print_string(PRINT_ANY, "header", " header %s", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + print_string(PRINT_ANY, "field", " field %s", + dpipe_field_id2s(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global)); + mapping = dpipe_mapping_get(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global); + if (mapping) + print_string(PRINT_ANY, "mapping", " mapping %s", mapping); +} + +static int dpipe_action_parse(struct dpipe_action *action, struct nlattr *nl) +{ + struct nlattr *nla_action[DEVLINK_ATTR_MAX + 1] = {}; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_action); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE] || + !nla_action[DEVLINK_ATTR_DPIPE_HEADER_INDEX] || + !nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID] || + !nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]) { + return -EINVAL; + } + + action->type = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE]); + action->info.header_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID]); + action->info.field_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]); + action->info.header_global = !!mnl_attr_get_u8(nla_action[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]); + + return 0; +} + +static int dpipe_table_actions_show(struct dpipe_ctx *ctx, + struct nlattr *nla_actions) +{ + struct nlattr *nla_action; + struct dpipe_action action; + + mnl_attr_for_each_nested(nla_action, nla_actions) { + pr_out_entry_start(ctx->dl); + if (dpipe_action_parse(&action, nla_action)) + goto err_action_parse; + pr_out_dpipe_action(&action, ctx); + pr_out_entry_end(ctx->dl); + } + return 0; + +err_action_parse: + pr_out_entry_end(ctx->dl); + return -EINVAL; +} + +static const char * +dpipe_match_type_e2s(enum devlink_dpipe_match_type match_type) +{ + switch (match_type) { + case DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT: + return "field_exact"; + default: + return ""; + } +} + +struct dpipe_match { + struct dpipe_op_info info; + uint32_t type; +}; + +static void pr_out_dpipe_match(struct dpipe_match *match, + struct dpipe_ctx *ctx) +{ + struct dpipe_op_info *op_info = &match->info; + const char *mapping; + + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "type", "type %s", + dpipe_match_type_e2s(match->type)); + print_string(PRINT_ANY, "header", " header %s", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + print_string(PRINT_ANY, "field", " field %s", + dpipe_field_id2s(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global)); + mapping = dpipe_mapping_get(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global); + if (mapping) + print_string(PRINT_ANY, "mapping", " mapping %s", mapping); +} + +static int dpipe_match_parse(struct dpipe_match *match, + struct nlattr *nl) + +{ + struct nlattr *nla_match[DEVLINK_ATTR_MAX + 1] = {}; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_match); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE] || + !nla_match[DEVLINK_ATTR_DPIPE_HEADER_INDEX] || + !nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID] || + !nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]) { + return -EINVAL; + } + + match->type = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE]); + match->info.header_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID]); + match->info.field_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]); + match->info.header_global = !!mnl_attr_get_u8(nla_match[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]); + + return 0; +} + +static int dpipe_table_matches_show(struct dpipe_ctx *ctx, + struct nlattr *nla_matches) +{ + struct nlattr *nla_match; + struct dpipe_match match; + + mnl_attr_for_each_nested(nla_match, nla_matches) { + pr_out_entry_start(ctx->dl); + if (dpipe_match_parse(&match, nla_match)) + goto err_match_parse; + pr_out_dpipe_match(&match, ctx); + pr_out_entry_end(ctx->dl); + } + return 0; + +err_match_parse: + pr_out_entry_end(ctx->dl); + return -EINVAL; +} + +static struct resource * +resource_find(struct resources *resources, struct resource *resource, + uint64_t resource_id) +{ + struct list_head *list_head; + + if (!resource) + list_head = &resources->resource_list; + else + list_head = &resource->resource_list; + + list_for_each_entry(resource, list_head, list) { + struct resource *child_resource; + + if (resource->id == resource_id) + return resource; + + child_resource = resource_find(resources, resource, + resource_id); + if (child_resource) + return child_resource; + } + return NULL; +} + +static void +resource_path_print(struct dl *dl, struct resources *resources, + uint64_t resource_id) +{ + struct resource *resource, *parent_resource; + const char del[] = "/"; + int path_len = 0; + char *path; + + resource = resource_find(resources, NULL, resource_id); + if (!resource) + return; + + for (parent_resource = resource; parent_resource; + parent_resource = parent_resource->parent) + path_len += strlen(parent_resource->name) + 1; + + path_len++; + path = calloc(1, path_len); + if (!path) + return; + + path += path_len - 1; + for (parent_resource = resource; parent_resource; + parent_resource = parent_resource->parent) { + path -= strlen(parent_resource->name); + memcpy(path, parent_resource->name, + strlen(parent_resource->name)); + path -= strlen(del); + memcpy(path, del, strlen(del)); + } + check_indent_newline(dl); + print_string(PRINT_ANY, "resource_path", "resource_path %s", path); + free(path); +} + +static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl) +{ + struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {}; + struct dpipe_table *table; + uint32_t resource_units; + bool counters_enabled; + bool resource_valid; + uint32_t size; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_table); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME] || + !nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE] || + !nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] || + !nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] || + !nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) { + return -EINVAL; + } + + table = dpipe_table_alloc(); + if (!table) + return -ENOMEM; + + table->name = strdup(mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME])); + size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]); + counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); + + resource_valid = nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID] && + ctx->resources; + if (resource_valid) { + table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]); + table->resource_valid = true; + } + + list_add_tail(&table->list, &ctx->tables->table_list); + if (!ctx->print_tables) + return 0; + + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", table->name); + print_uint(PRINT_ANY, "size", " size %u", size); + print_bool(PRINT_ANY, "counters_enabled", " counters_enabled %s", counters_enabled); + + if (resource_valid) { + resource_units = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS]); + resource_path_print(ctx->dl, ctx->resources, + table->resource_id); + print_uint(PRINT_ANY, "resource_units", " resource_units %u", + resource_units); + } + + pr_out_array_start(ctx->dl, "match"); + if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES])) + goto err_matches_show; + pr_out_array_end(ctx->dl); + + pr_out_array_start(ctx->dl, "action"); + if (dpipe_table_actions_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS])) + goto err_actions_show; + pr_out_array_end(ctx->dl); + + return 0; + +err_actions_show: +err_matches_show: + pr_out_array_end(ctx->dl); + return -EINVAL; +} + +static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb) +{ + struct nlattr *nla_tables = tb[DEVLINK_ATTR_DPIPE_TABLES]; + struct nlattr *nla_table; + + mnl_attr_for_each_nested(nla_table, nla_tables) { + if (ctx->print_tables) + pr_out_handle_start_arr(ctx->dl, tb); + if (dpipe_table_show(ctx, nla_table)) + goto err_table_show; + if (ctx->print_tables) + pr_out_handle_end(ctx->dl); + } + return 0; + +err_table_show: + if (ctx->print_tables) + pr_out_handle_end(ctx->dl); + return -EINVAL; +} + +static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dpipe_ctx *ctx = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_DPIPE_TABLES]) + return MNL_CB_ERROR; + + if (dpipe_tables_show(ctx, tb)) + return MNL_CB_ERROR; + return MNL_CB_OK; +} + +static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data); + +static int cmd_dpipe_table_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct dpipe_ctx dpipe_ctx = {}; + struct resource_ctx resource_ctx = {}; + uint16_t flags = NLM_F_REQUEST; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME); + if (err) + return err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags); + + err = dpipe_ctx_init(&dpipe_ctx, dl); + if (err) + return err; + + dpipe_ctx.print_tables = true; + + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, + &dpipe_ctx); + if (err) { + pr_err("error get headers %s\n", strerror(dpipe_ctx.err)); + goto err_headers_get; + } + + err = resource_ctx_init(&resource_ctx, dl); + if (err) + goto err_resource_ctx_init; + + resource_ctx.print_resources = false; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, flags); + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, + &resource_ctx); + if (!err) + dpipe_ctx.resources = resource_ctx.resources; + + flags = NLM_F_REQUEST | NLM_F_ACK; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags); + dl_opts_put(nlh, dl); + + pr_out_section_start(dl, "table"); + _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, &dpipe_ctx); + pr_out_section_end(dl); + + resource_ctx_fini(&resource_ctx); + dpipe_ctx_fini(&dpipe_ctx); + return 0; + +err_resource_ctx_init: +err_headers_get: + dpipe_ctx_fini(&dpipe_ctx); + return err; +} + +static int cmd_dpipe_table_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME | + DL_OPT_DPIPE_TABLE_COUNTERS, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +enum dpipe_value_type { + DPIPE_VALUE_TYPE_VALUE, + DPIPE_VALUE_TYPE_MASK, +}; + +static const char * +dpipe_value_type_e2s(enum dpipe_value_type type) +{ + switch (type) { + case DPIPE_VALUE_TYPE_VALUE: + return "value"; + case DPIPE_VALUE_TYPE_MASK: + return "value_mask"; + default: + return ""; + } +} + +struct dpipe_field_printer { + unsigned int field_id; + void (*printer)(struct dpipe_ctx *, enum dpipe_value_type, void *); +}; + +struct dpipe_header_printer { + struct dpipe_field_printer *printers; + unsigned int printers_count; + unsigned int header_id; +}; + +static void dpipe_field_printer_ipv4_addr(struct dpipe_ctx *ctx, + enum dpipe_value_type type, + void *value) +{ + struct in_addr ip_addr; + + ip_addr.s_addr = htonl(*(uint32_t *)value); + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), inet_ntoa(ip_addr)); +} + +static void +dpipe_field_printer_ethernet_addr(struct dpipe_ctx *ctx, + enum dpipe_value_type type, + void *value) +{ + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), + ether_ntoa((struct ether_addr *)value)); +} + +static void dpipe_field_printer_ipv6_addr(struct dpipe_ctx *ctx, + enum dpipe_value_type type, + void *value) +{ + char str[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, value, str, INET6_ADDRSTRLEN); + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), str); +} + +static struct dpipe_field_printer dpipe_field_printers_ipv4[] = { + { + .printer = dpipe_field_printer_ipv4_addr, + .field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP, + } +}; + +static struct dpipe_header_printer dpipe_header_printer_ipv4 = { + .printers = dpipe_field_printers_ipv4, + .printers_count = ARRAY_SIZE(dpipe_field_printers_ipv4), + .header_id = DEVLINK_DPIPE_HEADER_IPV4, +}; + +static struct dpipe_field_printer dpipe_field_printers_ethernet[] = { + { + .printer = dpipe_field_printer_ethernet_addr, + .field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, + }, +}; + +static struct dpipe_header_printer dpipe_header_printer_ethernet = { + .printers = dpipe_field_printers_ethernet, + .printers_count = ARRAY_SIZE(dpipe_field_printers_ethernet), + .header_id = DEVLINK_DPIPE_HEADER_ETHERNET, +}; + +static struct dpipe_field_printer dpipe_field_printers_ipv6[] = { + { + .printer = dpipe_field_printer_ipv6_addr, + .field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP, + } +}; + +static struct dpipe_header_printer dpipe_header_printer_ipv6 = { + .printers = dpipe_field_printers_ipv6, + .printers_count = ARRAY_SIZE(dpipe_field_printers_ipv6), + .header_id = DEVLINK_DPIPE_HEADER_IPV6, +}; + +static struct dpipe_header_printer *dpipe_header_printers[] = { + &dpipe_header_printer_ipv4, + &dpipe_header_printer_ethernet, + &dpipe_header_printer_ipv6, +}; + +static int dpipe_print_prot_header(struct dpipe_ctx *ctx, + struct dpipe_op_info *info, + enum dpipe_value_type type, + void *value) +{ + unsigned int header_printers_count = ARRAY_SIZE(dpipe_header_printers); + struct dpipe_header_printer *header_printer; + struct dpipe_field_printer *field_printer; + unsigned int field_printers_count; + int j; + int i; + + for (i = 0; i < header_printers_count; i++) { + header_printer = dpipe_header_printers[i]; + if (header_printer->header_id != info->header_id) + continue; + field_printers_count = header_printer->printers_count; + for (j = 0; j < field_printers_count; j++) { + field_printer = &header_printer->printers[j]; + if (field_printer->field_id != info->field_id) + continue; + field_printer->printer(ctx, type, value); + return 0; + } + } + + return -EINVAL; +} + +static void __pr_out_entry_value(struct dpipe_ctx *ctx, + void *value, + unsigned int value_len, + struct dpipe_op_info *info, + enum dpipe_value_type type) +{ + if (info->header_global && + !dpipe_print_prot_header(ctx, info, type, value)) + return; + + if (value_len == sizeof(uint32_t)) { + uint32_t *value_32 = value; + + check_indent_newline(ctx->dl); + print_uint_name_value(dpipe_value_type_e2s(type), *value_32); + } +} + +static void pr_out_dpipe_entry_value(struct dpipe_ctx *ctx, + struct nlattr **nla_match_value, + struct dpipe_op_info *info) +{ + void *value, *value_mask; + uint32_t value_mapping; + uint16_t value_len; + bool mask, mapping; + + mask = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK]; + mapping = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]; + + value_len = mnl_attr_get_payload_len(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]); + value = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]); + + if (mapping) { + value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]); + check_indent_newline(ctx->dl); + print_uint(PRINT_ANY, "mapping_value", "mapping_value %u", value_mapping); + } + + if (mask) { + value_mask = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]); + __pr_out_entry_value(ctx, value_mask, value_len, info, + DPIPE_VALUE_TYPE_MASK); + } + + __pr_out_entry_value(ctx, value, value_len, info, DPIPE_VALUE_TYPE_VALUE); +} + +static int dpipe_entry_match_value_show(struct dpipe_ctx *ctx, + struct nlattr *nl) +{ + struct nlattr *nla_match_value[DEVLINK_ATTR_MAX + 1] = {}; + struct dpipe_match match; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_match_value); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_match_value[DEVLINK_ATTR_DPIPE_MATCH] || + !nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]) { + return -EINVAL; + } + + pr_out_entry_start(ctx->dl); + if (dpipe_match_parse(&match, + nla_match_value[DEVLINK_ATTR_DPIPE_MATCH])) + goto err_match_parse; + pr_out_dpipe_match(&match, ctx); + pr_out_dpipe_entry_value(ctx, nla_match_value, &match.info); + pr_out_entry_end(ctx->dl); + + return 0; + +err_match_parse: + pr_out_entry_end(ctx->dl); + return -EINVAL; +} + +static int dpipe_entry_action_value_show(struct dpipe_ctx *ctx, + struct nlattr *nl) +{ + struct nlattr *nla_action_value[DEVLINK_ATTR_MAX + 1] = {}; + struct dpipe_action action; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_action_value); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_action_value[DEVLINK_ATTR_DPIPE_ACTION] || + !nla_action_value[DEVLINK_ATTR_DPIPE_VALUE]) { + return -EINVAL; + } + + pr_out_entry_start(ctx->dl); + if (dpipe_action_parse(&action, + nla_action_value[DEVLINK_ATTR_DPIPE_ACTION])) + goto err_action_parse; + pr_out_dpipe_action(&action, ctx); + pr_out_dpipe_entry_value(ctx, nla_action_value, &action.info); + pr_out_entry_end(ctx->dl); + + return 0; + +err_action_parse: + pr_out_entry_end(ctx->dl); + return -EINVAL; +} + +static int +dpipe_tables_action_values_show(struct dpipe_ctx *ctx, + struct nlattr *nla_action_values) +{ + struct nlattr *nla_action_value; + + mnl_attr_for_each_nested(nla_action_value, nla_action_values) { + if (dpipe_entry_action_value_show(ctx, nla_action_value)) + return -EINVAL; + } + return 0; +} + +static int +dpipe_tables_match_values_show(struct dpipe_ctx *ctx, + struct nlattr *nla_match_values) +{ + struct nlattr *nla_match_value; + + mnl_attr_for_each_nested(nla_match_value, nla_match_values) { + if (dpipe_entry_match_value_show(ctx, nla_match_value)) + return -EINVAL; + } + return 0; +} + +static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl) +{ + struct nlattr *nla_entry[DEVLINK_ATTR_MAX + 1] = {}; + uint32_t entry_index; + uint64_t counter; + int err; + + err = mnl_attr_parse_nested(nl, attr_cb, nla_entry); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] || + !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] || + !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]) { + return -EINVAL; + } + + check_indent_newline(ctx->dl); + entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]); + print_uint(PRINT_ANY, "index", "index %u", entry_index); + + if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) { + counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]); + print_uint(PRINT_ANY, "counter", " counter %u", counter); + } + + pr_out_array_start(ctx->dl, "match_value"); + if (dpipe_tables_match_values_show(ctx, + nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES])) + goto err_match_values_show; + pr_out_array_end(ctx->dl); + + pr_out_array_start(ctx->dl, "action_value"); + if (dpipe_tables_action_values_show(ctx, + nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES])) + goto err_action_values_show; + pr_out_array_end(ctx->dl); + return 0; + +err_action_values_show: +err_match_values_show: + pr_out_array_end(ctx->dl); + return -EINVAL; +} + +static int dpipe_table_entries_show(struct dpipe_ctx *ctx, struct nlattr **tb) +{ + struct nlattr *nla_entries = tb[DEVLINK_ATTR_DPIPE_ENTRIES]; + struct nlattr *nla_entry; + + mnl_attr_for_each_nested(nla_entry, nla_entries) { + pr_out_handle_start_arr(ctx->dl, tb); + if (dpipe_entry_show(ctx, nla_entry)) + goto err_entry_show; + pr_out_handle_end(ctx->dl); + } + return 0; + +err_entry_show: + pr_out_handle_end(ctx->dl); + return -EINVAL; +} + +static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data) +{ + struct dpipe_ctx *ctx = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_DPIPE_ENTRIES]) + return MNL_CB_ERROR; + + if (dpipe_table_entries_show(ctx, tb)) + return MNL_CB_ERROR; + return MNL_CB_OK; +} + +static int cmd_dpipe_table_dump(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct dpipe_ctx ctx = {}; + uint16_t flags = NLM_F_REQUEST; + int err; + + err = dpipe_ctx_init(&ctx, dl); + if (err) + return err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0); + if (err) + goto out; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags); + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx); + if (err) { + pr_err("error get headers %s\n", strerror(ctx.err)); + goto out; + } + + flags = NLM_F_REQUEST | NLM_F_ACK; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_ENTRIES_GET, flags); + dl_opts_put(nlh, dl); + + pr_out_section_start(dl, "table_entry"); + _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, &ctx); + pr_out_section_end(dl); +out: + dpipe_ctx_fini(&ctx); + return err; +} + +static int cmd_dpipe_table(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dpipe_help(); + return 0; + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_dpipe_table_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_dpipe_table_set(dl); + } else if (dl_argv_match(dl, "dump")) { + dl_arg_inc(dl); + return cmd_dpipe_table_dump(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_dpipe(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dpipe_help(); + return 0; + } else if (dl_argv_match(dl, "header")) { + dl_arg_inc(dl); + return cmd_dpipe_header(dl); + } else if (dl_argv_match(dl, "table")) { + dl_arg_inc(dl); + return cmd_dpipe_table(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int +resource_parse(struct resource_ctx *ctx, struct resource *resource, + struct nlattr **nla_resource) +{ + if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] || + !nla_resource[DEVLINK_ATTR_RESOURCE_ID] || + !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) { + return -EINVAL; + } + + resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME])); + resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]); + resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]); + resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]); + resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]); + resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]); + resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]); + + if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]) + resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]); + else + resource->size_new = resource->size; + + if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) { + resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]); + resource->occ_valid = true; + } + + if (resource->size_new != resource->size) + ctx->pending_change = true; + + return 0; +} + +static int +resource_get(struct resource_ctx *ctx, struct resource *resource, + struct resource *parent_resource, struct nlattr *nl) +{ + struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *nla_child_resource; + struct nlattr *nla_resources; + bool top = false; + int err; + + if (!resource) { + nla_resources = nl; + top = true; + goto out; + } + + err = mnl_attr_parse_nested(nl, attr_cb, nla_resource); + if (err != MNL_CB_OK) + return -EINVAL; + + err = resource_parse(ctx, resource, nla_resource); + if (err) + return err; + + resource->parent = parent_resource; + if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST]) + return 0; + + resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]); + nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST]; +out: + mnl_attr_for_each_nested(nla_child_resource, nla_resources) { + struct resource *child_resource; + struct list_head *list; + + child_resource = resource_alloc(); + if (!child_resource) + return -ENOMEM; + + if (top) + list = &ctx->resources->resource_list; + else + list = &resource->resource_list; + + list_add_tail(&child_resource->list, list); + err = resource_get(ctx, child_resource, resource, + nla_child_resource); + if (err) + return err; + } + + return 0; +} + +static const char *resource_unit_str_get(enum devlink_resource_unit unit) +{ + switch (unit) { + case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry"; + default: return ""; + } +} + +static void resource_show(struct resource *resource, + struct resource_ctx *ctx) +{ + struct resource *child_resource; + struct dpipe_table *table; + struct dl *dl = ctx->dl; + bool array = false; + + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", resource->name); + if (dl->verbose) + resource_path_print(dl, ctx->resources, resource->id); + pr_out_u64(dl, "size", resource->size); + if (resource->size != resource->size_new) + pr_out_u64(dl, "size_new", resource->size_new); + if (resource->occ_valid) + print_uint(PRINT_ANY, "occ", " occ %u", resource->size_occ); + print_string(PRINT_ANY, "unit", " unit %s", + resource_unit_str_get(resource->unit)); + + if (resource->size_min != resource->size_max) { + print_uint(PRINT_ANY, "size_min", " size_min %u", + resource->size_min); + pr_out_u64(dl, "size_max", resource->size_max); + print_uint(PRINT_ANY, "size_gran", " size_gran %u", + resource->size_gran); + } + + list_for_each_entry(table, &ctx->tables->table_list, list) + if (table->resource_id == resource->id && + table->resource_valid) + array = true; + + if (array) + pr_out_array_start(dl, "dpipe_tables"); + else + print_string(PRINT_ANY, "dpipe_tables", " dpipe_tables none", + "none"); + + list_for_each_entry(table, &ctx->tables->table_list, list) { + if (table->resource_id != resource->id || + !table->resource_valid) + continue; + pr_out_entry_start(dl); + check_indent_newline(dl); + print_string(PRINT_ANY, "table_name", "table_name %s", + table->name); + pr_out_entry_end(dl); + } + if (array) + pr_out_array_end(dl); + + if (list_empty(&resource->resource_list)) + return; + + if (ctx->pending_change) { + check_indent_newline(dl); + print_string(PRINT_ANY, "size_valid", "size_valid %s", + resource->size_valid ? "true" : "false"); + } + pr_out_array_start(dl, "resources"); + list_for_each_entry(child_resource, &resource->resource_list, list) { + pr_out_entry_start(dl); + resource_show(child_resource, ctx); + pr_out_entry_end(dl); + } + pr_out_array_end(dl); +} + +static void +resources_show(struct resource_ctx *ctx, struct nlattr **tb) +{ + struct resources *resources = ctx->resources; + struct resource *resource; + + list_for_each_entry(resource, &resources->resource_list, list) { + pr_out_handle_start_arr(ctx->dl, tb); + resource_show(resource, ctx); + pr_out_handle_end(ctx->dl); + } +} + +static int resources_get(struct resource_ctx *ctx, struct nlattr **tb) +{ + return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]); +} + +static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data) +{ + struct resource_ctx *ctx = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_RESOURCE_LIST]) + return MNL_CB_ERROR; + + err = resources_get(ctx, tb); + if (err) { + ctx->err = err; + return MNL_CB_ERROR; + } + + if (ctx->print_resources) + resources_show(ctx, tb); + + return MNL_CB_OK; +} + +static int cmd_resource_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct dpipe_ctx dpipe_ctx = {}; + struct resource_ctx resource_ctx = {}; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE, 0); + if (err) + return err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, + NLM_F_REQUEST); + dl_opts_put(nlh, dl); + + err = dpipe_ctx_init(&dpipe_ctx, dl); + if (err) + return err; + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, + &dpipe_ctx); + if (err) { + pr_err("error get tables %s\n", strerror(dpipe_ctx.err)); + goto out; + } + + err = resource_ctx_init(&resource_ctx, dl); + if (err) + goto out; + + resource_ctx.print_resources = true; + resource_ctx.tables = dpipe_ctx.tables; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, + NLM_F_REQUEST | NLM_F_ACK); + dl_opts_put(nlh, dl); + pr_out_section_start(dl, "resources"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, + &resource_ctx); + pr_out_section_end(dl); + resource_ctx_fini(&resource_ctx); +out: + dpipe_ctx_fini(&dpipe_ctx); + return err; +} + +static void cmd_resource_help(void) +{ + pr_err("Usage: devlink resource show DEV\n" + " devlink resource set DEV path PATH size SIZE\n"); +} + +static struct resource * +resource_find_by_name(struct list_head *list, char *name) +{ + struct resource *resource; + + list_for_each_entry(resource, list, list) { + if (!strcmp(resource->name, name)) + return resource; + } + return NULL; +} + +static int +resource_path_parse(struct resource_ctx *ctx, const char *resource_path, + uint32_t *p_resource_id, bool *p_resource_valid) +{ + struct resource *resource; + uint32_t resource_id = 0; + char *resource_path_dup; + struct list_head *list; + const char del[] = "/"; + char *resource_name; + + resource_path_dup = strdup(resource_path); + list = &ctx->resources->resource_list; + resource_name = strtok(resource_path_dup, del); + while (resource_name != NULL) { + resource = resource_find_by_name(list, resource_name); + if (!resource) + goto err_resource_lookup; + + list = &resource->resource_list; + resource_name = strtok(NULL, del); + resource_id = resource->id; + } + free(resource_path_dup); + *p_resource_valid = true; + *p_resource_id = resource_id; + return 0; + +err_resource_lookup: + free(resource_path_dup); + return -EINVAL; +} + +static int cmd_resource_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct resource_ctx ctx = {}; + int err; + + err = resource_ctx_init(&ctx, dl); + if (err) + return err; + + ctx.print_resources = false; + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH | + DL_OPT_RESOURCE_SIZE, 0); + if (err) + goto out; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, + NLM_F_REQUEST); + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx); + if (err) { + pr_err("error getting resources %s\n", strerror(ctx.err)); + goto out; + } + + err = resource_path_parse(&ctx, dl->opts.resource_path, + &dl->opts.resource_id, + &dl->opts.resource_id_valid); + if (err) { + pr_err("error parsing resource path %s\n", strerror(-err)); + goto out; + } + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET, + NLM_F_REQUEST | NLM_F_ACK); + + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +out: + resource_ctx_fini(&ctx); + return err; +} + +static int cmd_resource(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_resource_help(); + return 0; + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_resource_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_resource_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_region_handle_start(struct dl *dl, struct nlattr **tb) +{ + const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + const char *region_name = mnl_attr_get_str(tb[DEVLINK_ATTR_REGION_NAME]); + char buf[256]; + + sprintf(buf, "%s/%s/%s", bus_name, dev_name, region_name); + if (dl->json_output) + open_json_object(buf); + else + pr_out("%s:", buf); +} + +static void pr_out_region_handle_end(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + pr_out("\n"); +} + +static void pr_out_region_snapshots_start(struct dl *dl, bool array) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + open_json_array(PRINT_JSON, "snapshot"); + else + pr_out("snapshot %s", array ? "[" : ""); +} + +static void pr_out_region_snapshots_end(struct dl *dl, bool array) +{ + if (dl->json_output) + close_json_array(PRINT_JSON, NULL); + else if (array) + pr_out("]"); +} + +static void pr_out_region_snapshots_id(struct dl *dl, struct nlattr **tb, int index) +{ + uint32_t snapshot_id; + + if (!tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) + return; + + snapshot_id = mnl_attr_get_u32(tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); + + if (dl->json_output) + print_uint(PRINT_JSON, NULL, NULL, snapshot_id); + else + pr_out("%s%u", index ? " " : "", snapshot_id); +} + +static void pr_out_snapshots(struct dl *dl, struct nlattr **tb) +{ + struct nlattr *tb_snapshot[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *nla_sanpshot; + int err, index = 0; + + pr_out_region_snapshots_start(dl, true); + mnl_attr_for_each_nested(nla_sanpshot, tb[DEVLINK_ATTR_REGION_SNAPSHOTS]) { + err = mnl_attr_parse_nested(nla_sanpshot, attr_cb, tb_snapshot); + if (err != MNL_CB_OK) + return; + pr_out_region_snapshots_id(dl, tb_snapshot, index++); + } + pr_out_region_snapshots_end(dl, true); +} + +static void pr_out_snapshot(struct dl *dl, struct nlattr **tb) +{ + pr_out_region_snapshots_start(dl, false); + pr_out_region_snapshots_id(dl, tb, 0); + pr_out_region_snapshots_end(dl, false); +} + +static void pr_out_region(struct dl *dl, struct nlattr **tb) +{ + pr_out_region_handle_start(dl, tb); + + if (tb[DEVLINK_ATTR_REGION_SIZE]) + pr_out_u64(dl, "size", + mnl_attr_get_u64(tb[DEVLINK_ATTR_REGION_SIZE])); + + if (tb[DEVLINK_ATTR_REGION_SNAPSHOTS]) + pr_out_snapshots(dl, tb); + + if (tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) + pr_out_snapshot(dl, tb); + + pr_out_region_handle_end(dl); +} + +static int cmd_region_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_REGION_NAME] || !tb[DEVLINK_ATTR_REGION_SIZE]) + return MNL_CB_ERROR; + + pr_out_region(dl, tb); + + return MNL_CB_OK; +} + +static int cmd_region_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "regions"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_show_cb, dl); + pr_out_section_end(dl); + return err; +} + +static int cmd_region_snapshot_del(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_DEL, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION | + DL_OPT_REGION_SNAPSHOT_ID, 0); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_region_read_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *nla_entry, *nla_chunk_data, *nla_chunk_addr; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb_field[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_REGION_CHUNKS]) + return MNL_CB_ERROR; + + mnl_attr_for_each_nested(nla_entry, tb[DEVLINK_ATTR_REGION_CHUNKS]) { + err = mnl_attr_parse_nested(nla_entry, attr_cb, tb_field); + if (err != MNL_CB_OK) + return MNL_CB_ERROR; + + nla_chunk_data = tb_field[DEVLINK_ATTR_REGION_CHUNK_DATA]; + if (!nla_chunk_data) + continue; + + nla_chunk_addr = tb_field[DEVLINK_ATTR_REGION_CHUNK_ADDR]; + if (!nla_chunk_addr) + continue; + + pr_out_region_chunk(dl, mnl_attr_get_payload(nla_chunk_data), + mnl_attr_get_payload_len(nla_chunk_data), + mnl_attr_get_u64(nla_chunk_addr)); + } + return MNL_CB_OK; +} + +static int cmd_region_dump(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION | + DL_OPT_REGION_SNAPSHOT_ID, 0); + if (err) + return err; + + pr_out_section_start(dl, "dump"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl); + pr_out_section_end(dl); + if (!dl->json_output) + pr_out("\n"); + return err; +} + +static int cmd_region_read(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ, + NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION | + DL_OPT_REGION_ADDRESS | DL_OPT_REGION_LENGTH | + DL_OPT_REGION_SNAPSHOT_ID, 0); + if (err) + return err; + + pr_out_section_start(dl, "read"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl); + pr_out_section_end(dl); + if (!dl->json_output) + pr_out("\n"); + return err; +} + +static int cmd_region_snapshot_new_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_REGION_NAME] || + !tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) + return MNL_CB_ERROR; + + pr_out_region(dl, tb); + + return MNL_CB_OK; +} + +static int cmd_region_snapshot_new(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION, + DL_OPT_REGION_SNAPSHOT_ID); + if (err) + return err; + + pr_out_section_start(dl, "regions"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_snapshot_new_cb, dl); + pr_out_section_end(dl); + return err; +} + +static void cmd_region_help(void) +{ + pr_err("Usage: devlink region show [ DEV/REGION ]\n"); + pr_err(" devlink region del DEV/REGION snapshot SNAPSHOT_ID\n"); + pr_err(" devlink region new DEV/REGION snapshot SNAPSHOT_ID\n"); + pr_err(" devlink region dump DEV/REGION [ snapshot SNAPSHOT_ID ]\n"); + pr_err(" devlink region read DEV/REGION [ snapshot SNAPSHOT_ID ] address ADDRESS length LENGTH\n"); +} + +static int cmd_region(struct dl *dl) +{ + if (dl_no_arg(dl)) { + return cmd_region_show(dl); + } else if (dl_argv_match(dl, "help")) { + cmd_region_help(); + return 0; + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_region_show(dl); + } else if (dl_argv_match(dl, "del")) { + dl_arg_inc(dl); + return cmd_region_snapshot_del(dl); + } else if (dl_argv_match(dl, "dump")) { + dl_arg_inc(dl); + return cmd_region_dump(dl); + } else if (dl_argv_match(dl, "read")) { + dl_arg_inc(dl); + return cmd_region_read(dl); + } else if (dl_argv_match(dl, "new")) { + dl_arg_inc(dl); + return cmd_region_snapshot_new(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_health_set_params(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_SET, + NLM_F_REQUEST | NLM_F_ACK); + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP | DL_OPT_HEALTH_REPORTER_NAME, + DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD | + DL_OPT_HEALTH_REPORTER_AUTO_RECOVER | + DL_OPT_HEALTH_REPORTER_AUTO_DUMP); + if (err) + return err; + + dl_opts_put(nlh, dl); + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_health_dump_clear(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HANDLEP | + DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + dl_opts_put(nlh, dl); + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data) +{ + uint8_t *data; + uint32_t len; + + check_indent_newline(dl); + switch (type) { + case MNL_TYPE_FLAG: + print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U8: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U16: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data)); + break; + case MNL_TYPE_U32: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data)); + break; + case MNL_TYPE_U64: + print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data)); + break; + case MNL_TYPE_NUL_STRING: + print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data)); + break; + case MNL_TYPE_BINARY: + len = mnl_attr_get_payload_len(nl_data); + data = mnl_attr_get_payload(nl_data); + pr_out_binary_value(dl, data, len); + break; + default: + return -EINVAL; + } + return MNL_CB_OK; +} + +static void pr_out_fmsg_name(struct dl *dl, char **name) +{ + if (!*name) + return; + + pr_out_name(dl, *name); + free(*name); + *name = NULL; +} + +struct nest_entry { + int attr_type; + struct list_head list; +}; + +struct fmsg_cb_data { + char *name; + struct dl *dl; + uint8_t value_type; + struct list_head entry_list; +}; + +static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data, + uint8_t *attr_value, bool insert) +{ + struct nest_entry *entry; + + if (insert) { + entry = malloc(sizeof(struct nest_entry)); + if (!entry) + return -ENOMEM; + + entry->attr_type = *attr_value; + list_add(&entry->list, &fmsg_data->entry_list); + } else { + if (list_empty(&fmsg_data->entry_list)) + return MNL_CB_ERROR; + entry = list_first_entry(&fmsg_data->entry_list, + struct nest_entry, list); + *attr_value = entry->attr_type; + list_del(&entry->list); + free(entry); + } + return MNL_CB_OK; +} + +static void pr_out_fmsg_group_start(struct dl *dl, char **name) +{ + __pr_out_newline(); + pr_out_fmsg_name(dl, name); + __pr_out_newline(); + __pr_out_indent_inc(); +} + +static void pr_out_fmsg_group_end(struct dl *dl) +{ + __pr_out_newline(); + __pr_out_indent_dec(); +} + +static void pr_out_fmsg_start_object(struct dl *dl, char **name) +{ + if (dl->json_output) { + pr_out_fmsg_name(dl, name); + open_json_object(NULL); + } else { + pr_out_fmsg_group_start(dl, name); + } +} + +static void pr_out_fmsg_end_object(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + pr_out_fmsg_group_end(dl); +} + +static void pr_out_fmsg_start_array(struct dl *dl, char **name) +{ + if (dl->json_output) { + pr_out_fmsg_name(dl, name); + open_json_array(PRINT_JSON, NULL); + } else { + pr_out_fmsg_group_start(dl, name); + } +} + +static void pr_out_fmsg_end_array(struct dl *dl) +{ + if (dl->json_output) + close_json_array(PRINT_JSON, NULL); + else + pr_out_fmsg_group_end(dl); +} + +static int cmd_fmsg_nest(struct fmsg_cb_data *fmsg_data, uint8_t nest_value, + bool start) +{ + struct dl *dl = fmsg_data->dl; + uint8_t value = nest_value; + int err; + + err = cmd_fmsg_nest_queue(fmsg_data, &value, start); + if (err != MNL_CB_OK) + return err; + + switch (value) { + case DEVLINK_ATTR_FMSG_OBJ_NEST_START: + if (start) + pr_out_fmsg_start_object(dl, &fmsg_data->name); + else + pr_out_fmsg_end_object(dl); + break; + case DEVLINK_ATTR_FMSG_PAIR_NEST_START: + break; + case DEVLINK_ATTR_FMSG_ARR_NEST_START: + if (start) + pr_out_fmsg_start_array(dl, &fmsg_data->name); + else + pr_out_fmsg_end_array(dl); + break; + default: + return -EINVAL; + } + return MNL_CB_OK; +} + +static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct fmsg_cb_data *fmsg_data = data; + struct dl *dl = fmsg_data->dl; + struct nlattr *nla_object; + int attr_type; + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_FMSG]) + return MNL_CB_ERROR; + + mnl_attr_for_each_nested(nla_object, tb[DEVLINK_ATTR_FMSG]) { + attr_type = mnl_attr_get_type(nla_object); + switch (attr_type) { + case DEVLINK_ATTR_FMSG_OBJ_NEST_START: + case DEVLINK_ATTR_FMSG_PAIR_NEST_START: + case DEVLINK_ATTR_FMSG_ARR_NEST_START: + err = cmd_fmsg_nest(fmsg_data, attr_type, true); + if (err != MNL_CB_OK) + return err; + break; + case DEVLINK_ATTR_FMSG_NEST_END: + err = cmd_fmsg_nest(fmsg_data, attr_type, false); + if (err != MNL_CB_OK) + return err; + break; + case DEVLINK_ATTR_FMSG_OBJ_NAME: + free(fmsg_data->name); + fmsg_data->name = strdup(mnl_attr_get_str(nla_object)); + if (!fmsg_data->name) + return -ENOMEM; + break; + case DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE: + fmsg_data->value_type = mnl_attr_get_u8(nla_object); + break; + case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA: + pr_out_fmsg_name(dl, &fmsg_data->name); + err = fmsg_value_show(dl, fmsg_data->value_type, + nla_object); + if (err != MNL_CB_OK) + return err; + break; + default: + return -EINVAL; + } + } + return MNL_CB_OK; +} + +static void cmd_fmsg_init(struct dl *dl, struct fmsg_cb_data *data) +{ + /* FMSG is dynamic: opening of an object or array causes a + * newline. JSON starts with an { or [, but plain text should + * not start with a new line. Ensure this by setting + * g_new_line_count to 1: avoiding newline before the first + * print. + */ + g_new_line_count = 1; + data->name = NULL; + data->dl = dl; + INIT_LIST_HEAD(&data->entry_list); +} + +static int cmd_health_object_common(struct dl *dl, uint8_t cmd, uint16_t flags) +{ + struct fmsg_cb_data data; + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, cmd, flags | NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HANDLEP | + DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + cmd_fmsg_init(dl, &data); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data); + free(data.name); + return err; +} + +static int cmd_health_dump_show(struct dl *dl) +{ + return cmd_health_object_common(dl, + DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, + NLM_F_DUMP); +} + +static int cmd_health_diagnose(struct dl *dl) +{ + return cmd_health_object_common(dl, + DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, + 0); +} + +static int cmd_health_recover(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_RECOVER, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HANDLEP | + DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + dl_opts_put(nlh, dl); + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +enum devlink_health_reporter_state { + DEVLINK_HEALTH_REPORTER_STATE_HEALTHY, + DEVLINK_HEALTH_REPORTER_STATE_ERROR, +}; + +static const char *health_state_name(uint8_t state) +{ + switch (state) { + case DEVLINK_HEALTH_REPORTER_STATE_HEALTHY: + return HEALTH_REPORTER_STATE_HEALTHY_STR; + case DEVLINK_HEALTH_REPORTER_STATE_ERROR: + return HEALTH_REPORTER_STATE_ERROR_STR; + default: + return ""; + } +} + +static void pr_out_dump_reporter_format_logtime(struct dl *dl, const struct nlattr *attr) +{ + char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + uint64_t time_ms = mnl_attr_get_u64(attr); + struct sysinfo s_info; + struct tm *info; + time_t now, sec; + int err; + + time(&now); + info = localtime(&now); + err = sysinfo(&s_info); + if (err) + goto out; + /* Subtract uptime in sec from now yields the time of system + * uptime. To this, add time_ms which is the amount of + * milliseconds elapsed between uptime and the dump taken. + */ + sec = now - s_info.uptime + time_ms / 1000; + info = localtime(&sec); +out: + strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info); + strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info); + check_indent_newline(dl); + print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date); + print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time); +} + +static void pr_out_dump_report_timestamp(struct dl *dl, const struct nlattr *attr) +{ + char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + time_t tv_sec; + struct tm *tm; + uint64_t ts; + + ts = mnl_attr_get_u64(attr); + tv_sec = ts / 1000000000; + tm = localtime(&tv_sec); + + strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", tm); + strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", tm); + + check_indent_newline(dl); + print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date); + print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time); +} + +static void pr_out_health(struct dl *dl, struct nlattr **tb_health, + bool print_device, bool print_port) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + enum devlink_health_reporter_state state; + int err; + + err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER], + attr_cb, tb); + if (err != MNL_CB_OK) + return; + + if (!tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]) + return; + + if (!print_device && !print_port) + return; + if (print_port) { + if (!print_device && !tb_health[DEVLINK_ATTR_PORT_INDEX]) + return; + else if (tb_health[DEVLINK_ATTR_PORT_INDEX]) + pr_out_port_handle_start_arr(dl, tb_health, false); + } + if (print_device) { + if (!print_port && tb_health[DEVLINK_ATTR_PORT_INDEX]) + return; + else if (!tb_health[DEVLINK_ATTR_PORT_INDEX]) + pr_out_handle_start_arr(dl, tb_health); + } + + check_indent_newline(dl); + print_string(PRINT_ANY, "reporter", "reporter %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME])); + if (!dl->json_output) { + __pr_out_newline(); + __pr_out_indent_inc(); + } + state = mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]); + check_indent_newline(dl); + print_string(PRINT_ANY, "state", "state %s", health_state_name(state)); + pr_out_u64(dl, "error", + mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT])); + pr_out_u64(dl, "recover", + mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT])); + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS]) + pr_out_dump_report_timestamp(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS]); + else if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]) + pr_out_dump_reporter_format_logtime(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]); + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]) + pr_out_u64(dl, "grace_period", + mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])); + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]) + print_bool(PRINT_ANY, "auto_recover", " auto_recover %s", + mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])); + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]) + print_bool(PRINT_ANY, "auto_dump", " auto_dump %s", + mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])); + + __pr_out_indent_dec(); + pr_out_handle_end(dl); +} + +struct health_ctx { + struct dl *dl; + bool show_device; + bool show_port; +}; + +static int cmd_health_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct health_ctx *ctx = data; + struct dl *dl = ctx->dl; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER]) + return MNL_CB_ERROR; + + pr_out_health(dl, tb, ctx->show_device, ctx->show_port); + + return MNL_CB_OK; +} + +static int __cmd_health_show(struct dl *dl, bool show_device, bool show_port) +{ + struct nlmsghdr *nlh; + struct health_ctx ctx = { dl, show_device, show_port }; + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_GET, + flags); + + if (dl_argc(dl) > 0) { + ctx.show_port = true; + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HANDLEP | + DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + } + pr_out_section_start(dl, "health"); + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_health_show_cb, &ctx); + pr_out_section_end(dl); + return err; +} + +static void cmd_health_help(void) +{ + pr_err("Usage: devlink health show [ { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME ]\n"); + pr_err(" devlink health recover { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME\n"); + pr_err(" devlink health diagnose { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME\n"); + pr_err(" devlink health dump show { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME\n"); + pr_err(" devlink health dump clear { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME\n"); + pr_err(" devlink health set { DEV | DEV/PORT_INDEX } reporter REPORTER_NAME\n"); + pr_err(" [ grace_period MSEC ]\n"); + pr_err(" [ auto_recover { true | false } ]\n"); + pr_err(" [ auto_dump { true | false } ]\n"); +} + +static int cmd_health(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_health_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return __cmd_health_show(dl, true, true); + } else if (dl_argv_match(dl, "recover")) { + dl_arg_inc(dl); + return cmd_health_recover(dl); + } else if (dl_argv_match(dl, "diagnose")) { + dl_arg_inc(dl); + return cmd_health_diagnose(dl); + } else if (dl_argv_match(dl, "dump")) { + dl_arg_inc(dl); + if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_health_dump_show(dl); + } else if (dl_argv_match(dl, "clear")) { + dl_arg_inc(dl); + return cmd_health_dump_clear(dl); + } + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_health_set_params(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static const char *trap_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_TRAP_TYPE_DROP: + return "drop"; + case DEVLINK_TRAP_TYPE_EXCEPTION: + return "exception"; + case DEVLINK_TRAP_TYPE_CONTROL: + return "control"; + default: + return ""; + } +} + +static const char *trap_action_name(uint8_t action) +{ + switch (action) { + case DEVLINK_TRAP_ACTION_DROP: + return "drop"; + case DEVLINK_TRAP_ACTION_TRAP: + return "trap"; + case DEVLINK_TRAP_ACTION_MIRROR: + return "mirror"; + default: + return ""; + } +} + +static const char *trap_metadata_name(const struct nlattr *attr) +{ + switch (attr->nla_type) { + case DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT: + return "input_port"; + case DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE: + return "flow_action_cookie"; + default: + return ""; + } +} +static void pr_out_trap_metadata(struct dl *dl, struct nlattr *attr) +{ + struct nlattr *attr_metadata; + + pr_out_array_start(dl, "metadata"); + mnl_attr_for_each_nested(attr_metadata, attr) { + check_indent_newline(dl); + print_string(PRINT_ANY, NULL, "%s", + trap_metadata_name(attr_metadata)); + } + pr_out_array_end(dl); +} + +static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array) +{ + uint8_t action = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_ACTION]); + uint8_t type = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_TYPE]); + + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME])); + print_string(PRINT_ANY, "type", " type %s", trap_type_name(type)); + print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + print_string(PRINT_ANY, "action", " action %s", trap_action_name(action)); + print_string(PRINT_ANY, "group", " group %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME])); + if (dl->verbose) + pr_out_trap_metadata(dl, tb[DEVLINK_ATTR_TRAP_METADATA]); + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_NAME] || !tb[DEVLINK_ATTR_TRAP_TYPE] || + !tb[DEVLINK_ATTR_TRAP_ACTION] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_TRAP_METADATA] || !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + + pr_out_trap(dl, tb, true); + + return MNL_CB_OK; +} + +static void cmd_trap_help(void) +{ + pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop | mirror } ]\n"); + pr_err(" devlink trap show [ DEV trap TRAP ]\n"); + pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop | mirror } ]\n"); + pr_err(" [ policer POLICER ] [ nopolicer ]\n"); + pr_err(" devlink trap group show [ DEV group GROUP ]\n"); + pr_err(" devlink trap policer set DEV policer POLICER [ rate RATE ] [ burst BURST ]\n"); + pr_err(" devlink trap policer show DEV policer POLICER\n"); +} + +static int cmd_trap_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_NAME, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_TRAP_NAME, + DL_OPT_TRAP_ACTION); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array) +{ + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME])); + print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + if (tb[DEVLINK_ATTR_TRAP_POLICER_ID]) + print_uint(PRINT_ANY, "policer", " policer %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID])); + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_group_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + + pr_out_trap_group(dl, tb, true); + + return MNL_CB_OK; +} + +static int cmd_trap_group_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME, + 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap_group"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_group_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_group_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME, + DL_OPT_TRAP_ACTION | DL_OPT_TRAP_POLICER_ID); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_trap_group(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_group_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_group_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array) +{ + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_uint(PRINT_ANY, "policer", "policer %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID])); + print_u64(PRINT_ANY, "rate", " rate %llu", + mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_RATE])); + print_u64(PRINT_ANY, "burst", " burst %llu", + mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_BURST])); + if (tb[DEVLINK_ATTR_STATS]) + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_policer_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_POLICER_ID] || + !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] || + !tb[DEVLINK_ATTR_TRAP_POLICER_BURST]) + return MNL_CB_ERROR; + + pr_out_trap_policer(dl, tb, true); + + return MNL_CB_OK; +} + +static int cmd_trap_policer_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID, + 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap_policer"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_policer_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_policer_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID, + DL_OPT_TRAP_POLICER_RATE | + DL_OPT_TRAP_POLICER_BURST); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_trap_policer(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_policer_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_policer_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_trap(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_set(dl); + } else if (dl_argv_match(dl, "group")) { + dl_arg_inc(dl); + return cmd_trap_group(dl); + } else if (dl_argv_match(dl, "policer")) { + dl_arg_inc(dl); + return cmd_trap_policer(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void help(void) +{ + pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" + " devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n" + "where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n" + " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n"); +} + +static int dl_cmd(struct dl *dl, int argc, char **argv) +{ + dl->argc = argc; + dl->argv = argv; + + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + help(); + return 0; + } else if (dl_argv_match(dl, "dev")) { + dl_arg_inc(dl); + return cmd_dev(dl); + } else if (dl_argv_match(dl, "port")) { + dl_arg_inc(dl); + return cmd_port(dl); + } else if (dl_argv_match(dl, "sb")) { + dl_arg_inc(dl); + return cmd_sb(dl); + } else if (dl_argv_match(dl, "monitor")) { + dl_arg_inc(dl); + return cmd_mon(dl); + } else if (dl_argv_match(dl, "dpipe")) { + dl_arg_inc(dl); + return cmd_dpipe(dl); + } else if (dl_argv_match(dl, "resource")) { + dl_arg_inc(dl); + return cmd_resource(dl); + } else if (dl_argv_match(dl, "region")) { + dl_arg_inc(dl); + return cmd_region(dl); + } else if (dl_argv_match(dl, "health")) { + dl_arg_inc(dl); + return cmd_health(dl); + } else if (dl_argv_match(dl, "trap")) { + dl_arg_inc(dl); + return cmd_trap(dl); + } + pr_err("Object \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int dl_init(struct dl *dl) +{ + int err; + + dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION); + if (!dl->nlg) { + pr_err("Failed to connect to devlink Netlink\n"); + return -errno; + } + + err = ifname_map_init(dl); + if (err) { + pr_err("Failed to create index map\n"); + goto err_ifname_map_create; + } + new_json_obj_plain(dl->json_output); + return 0; + +err_ifname_map_create: + mnlg_socket_close(dl->nlg); + return err; +} + +static void dl_fini(struct dl *dl) +{ + delete_json_obj_plain(); + ifname_map_fini(dl); + mnlg_socket_close(dl->nlg); +} + +static struct dl *dl_alloc(void) +{ + struct dl *dl; + + dl = calloc(1, sizeof(*dl)); + if (!dl) + return NULL; + return dl; +} + +static void dl_free(struct dl *dl) +{ + free(dl); +} + +static int dl_batch(struct dl *dl, const char *name, bool force) +{ + char *line = NULL; + size_t len = 0; + int ret = EXIT_SUCCESS; + + if (name && strcmp(name, "-") != 0) { + if (freopen(name, "r", stdin) == NULL) { + fprintf(stderr, + "Cannot open file \"%s\" for reading: %s\n", + name, strerror(errno)); + return EXIT_FAILURE; + } + } + + cmdlineno = 0; + while (getcmdline(&line, &len, stdin) != -1) { + char *largv[100]; + int largc; + + largc = makeargs(line, largv, 100); + if (!largc) + continue; /* blank line */ + + if (dl_cmd(dl, largc, largv)) { + fprintf(stderr, "Command failed %s:%d\n", + name, cmdlineno); + ret = EXIT_FAILURE; + if (!force) + break; + } + } + + if (line) + free(line); + + return ret; +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "Version", no_argument, NULL, 'V' }, + { "force", no_argument, NULL, 'f' }, + { "batch", required_argument, NULL, 'b' }, + { "no-nice-names", no_argument, NULL, 'n' }, + { "json", no_argument, NULL, 'j' }, + { "pretty", no_argument, NULL, 'p' }, + { "verbose", no_argument, NULL, 'v' }, + { "statistics", no_argument, NULL, 's' }, + { "Netns", required_argument, NULL, 'N' }, + { NULL, 0, NULL, 0 } + }; + const char *batch_file = NULL; + bool force = false; + struct dl *dl; + int opt; + int err; + int ret; + + dl = dl_alloc(); + if (!dl) { + pr_err("Failed to allocate memory for devlink\n"); + return EXIT_FAILURE; + } + + while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:", + long_options, NULL)) >= 0) { + + switch (opt) { + case 'V': + printf("devlink utility, iproute2-%s\n", version); + ret = EXIT_SUCCESS; + goto dl_free; + case 'f': + force = true; + break; + case 'b': + batch_file = optarg; + break; + case 'n': + dl->no_nice_names = true; + break; + case 'j': + dl->json_output = true; + break; + case 'p': + pretty = true; + break; + case 'v': + dl->verbose = true; + break; + case 's': + dl->stats = true; + break; + case 'N': + if (netns_switch(optarg)) { + ret = EXIT_FAILURE; + goto dl_free; + } + break; + default: + pr_err("Unknown option.\n"); + help(); + ret = EXIT_FAILURE; + goto dl_free; + } + } + + argc -= optind; + argv += optind; + + err = dl_init(dl); + if (err) { + ret = EXIT_FAILURE; + goto dl_free; + } + + if (batch_file) + err = dl_batch(dl, batch_file, force); + else + err = dl_cmd(dl, argc, argv); + + if (err) { + ret = EXIT_FAILURE; + goto dl_fini; + } + + ret = EXIT_SUCCESS; + +dl_fini: + dl_fini(dl); +dl_free: + dl_free(dl); + + return ret; +} diff --git a/devlink/mnlg.c b/devlink/mnlg.c new file mode 100644 index 0000000..c7d25e8 --- /dev/null +++ b/devlink/mnlg.c @@ -0,0 +1,327 @@ +/* + * mnlg.c Generic Netlink helpers for libmnl + * + * 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. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libnetlink.h" +#include "utils.h" +#include "mnlg.h" + +struct mnlg_socket { + struct mnl_socket *nl; + char *buf; + uint32_t id; + uint8_t version; + unsigned int seq; + unsigned int portid; +}; + +static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags, uint32_t id, + uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + + nlh = mnl_nlmsg_put_header(nlg->buf); + nlh->nlmsg_type = id; + nlh->nlmsg_flags = flags; + nlg->seq = time(NULL); + nlh->nlmsg_seq = nlg->seq; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = cmd; + genl->version = version; + + return nlh; +} + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags) +{ + return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version); +} + +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) +{ + return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); +} + +static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + + /* Netlink subsystems returns the errno value with different signess */ + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + + if (nl_dump_ext_ack(nlh, NULL)) + return MNL_CB_ERROR; + + return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; +} + +static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data) +{ + int len = *(int *)NLMSG_DATA(nlh); + + if (len < 0) { + errno = -len; + nl_dump_ext_ack_done(nlh, len); + return MNL_CB_ERROR; + } + return MNL_CB_STOP; +} + +static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = mnlg_cb_noop, + [NLMSG_ERROR] = mnlg_cb_error, + [NLMSG_DONE] = mnlg_cb_stop, + [NLMSG_OVERRUN] = mnlg_cb_noop, +}; + +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) +{ + int err; + + do { + err = mnl_socket_recvfrom(nlg->nl, nlg->buf, + MNL_SOCKET_BUFFER_SIZE); + if (err <= 0) + break; + err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid, + data_cb, data, mnlg_cb_array, + ARRAY_SIZE(mnlg_cb_array)); + } while (err > 0); + + return err; +} + +struct group_info { + bool found; + uint32_t id; + const char *name; +}; + +static int parse_mc_grps_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case CTRL_ATTR_MCAST_GRP_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTRL_ATTR_MCAST_GRP_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void parse_genl_mc_grps(struct nlattr *nested, + struct group_info *group_info) +{ + struct nlattr *pos; + const char *name; + + mnl_attr_for_each_nested(pos, nested) { + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; + + mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || + !tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]); + if (strcmp(name, group_info->name) != 0) + continue; + + group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + group_info->found = true; + } +} + +static int get_group_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_MCAST_GROUPS && + mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_group_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct group_info *group_info = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return MNL_CB_ERROR; + parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info); + return MNL_CB_OK; +} + +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name) +{ + struct nlmsghdr *nlh; + struct group_info group_info; + int err; + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + return err; + + group_info.found = false; + group_info.name = group_name; + err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info); + if (err < 0) + return err; + + if (!group_info.found) { + errno = ENOENT; + return -1; + } + + err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP, + &group_info.id, sizeof(group_info.id)); + if (err < 0) + return err; + + return 0; +} + +static int get_family_id_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + if (type == CTRL_ATTR_FAMILY_ID && + mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + tb[type] = attr; + return MNL_CB_OK; +} + +static int get_family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t *p_id = data; + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_OK; +} + +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) +{ + struct mnlg_socket *nlg; + struct nlmsghdr *nlh; + int one = 1; + int err; + + nlg = malloc(sizeof(*nlg)); + if (!nlg) + return NULL; + + nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!nlg->buf) + goto err_buf_alloc; + + nlg->nl = mnl_socket_open(NETLINK_GENERIC); + if (!nlg->nl) + goto err_mnl_socket_open; + + /* Older kernels may no support capped/extended ACK reporting */ + mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one)); + mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one)); + + err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); + if (err < 0) + goto err_mnl_socket_bind; + + nlg->portid = mnl_socket_get_portid(nlg->nl); + + nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); + + err = mnlg_socket_send(nlg, nlh); + if (err < 0) + goto err_mnlg_socket_send; + + err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id); + if (err < 0) + goto err_mnlg_socket_recv_run; + + nlg->version = version; + return nlg; + +err_mnlg_socket_recv_run: +err_mnlg_socket_send: +err_mnl_socket_bind: + mnl_socket_close(nlg->nl); +err_mnl_socket_open: + free(nlg->buf); +err_buf_alloc: + free(nlg); + return NULL; +} + +void mnlg_socket_close(struct mnlg_socket *nlg) +{ + mnl_socket_close(nlg->nl); + free(nlg->buf); + free(nlg); +} + +int mnlg_socket_get_fd(struct mnlg_socket *nlg) +{ + return mnl_socket_get_fd(nlg->nl); +} diff --git a/devlink/mnlg.h b/devlink/mnlg.h new file mode 100644 index 0000000..61bc5a3 --- /dev/null +++ b/devlink/mnlg.h @@ -0,0 +1,28 @@ +/* + * mnlg.h Generic Netlink helpers for libmnl + * + * 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. + * + * Authors: Jiri Pirko + */ + +#ifndef _MNLG_H_ +#define _MNLG_H_ + +#include + +struct mnlg_socket; + +struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd, + uint16_t flags); +int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh); +int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data); +int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name); +struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version); +void mnlg_socket_close(struct mnlg_socket *nlg); +int mnlg_socket_get_fd(struct mnlg_socket *nlg); + +#endif /* _MNLG_H_ */ diff --git a/doc/actions/actions-general b/doc/actions/actions-general new file mode 100644 index 0000000..407a514 --- /dev/null +++ b/doc/actions/actions-general @@ -0,0 +1,256 @@ + +This documented is slightly dated but should give you idea of how things +work. + +What is it? +----------- + +An extension to the filtering/classification architecture of Linux Traffic +Control. +Up to 2.6.8 the only action that could be "attached" to a filter was policing. +i.e you could say something like: + +----- +tc filter add dev lo parent ffff: protocol ip prio 10 u32 match ip src \ +127.0.0.1/32 flowid 1:1 police mtu 4000 rate 1500kbit burst 90k +----- + +which implies "if a packet is seen on the ingress of the lo device with +a source IP address of 127.0.0.1/32 we give it a classification id of 1:1 and +we execute a policing action which rate limits its bandwidth utilization +to 1.5Mbps". + +The new extensions allow for more than just policing actions to be added. +They are also fully backward compatible. If you have a kernel that doesn't +understand them, then the effect is null i.e if you have a newer tc +but older kernel, the actions are not installed. Likewise if you +have a newer kernel but older tc, obviously the tc will use current +syntax which will work fine. Of course to get the required effect you need +both newer tc and kernel. If you are reading this you have the +right tc ;-> + +A side effect is that we can now get stateless firewalling to work with tc. +Essentially this is now an alternative to iptables. +I won't go into details of my dislike for iptables at times, but +scalability is one of the main issues; however, if you need stateful +classification - use netfilter (for now). + +This stuff works on both ingress and egress qdiscs. + +Features +-------- + +1) new additional syntax and actions enabled. Note old syntax is still valid. + +Essentially this is still the same syntax as tc with a new construct +"action". The syntax is of the form: +tc filter add parent 1:0 protocol ip prio 10 +flowid 1:1 action * + +You can have as many actions as you want (within sensible reasoning). + +In the past the only real action was the policer; i.e you could do something +along the lines of: +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match ip src 127.0.0.1/32 flowid 1:1 \ +police mtu 4000 rate 1500kbit burst 90k + +Although you can still use the same syntax, now you can say: + +tc filter add dev lo parent 1:0 protocol ip prio 10 u32 \ +match ip src 127.0.0.1/32 flowid 1:1 \ +action police mtu 4000 rate 1500kbit burst 90k + +" generic Actions" (gact) at the moment are: +{ drop, pass, reclassify, continue} +(If you have others, no listed here give me a reason and we will add them) ++drop says to drop the packet ++pass and ok (are equivalent) says to accept it ++reclassify requests for reclassification of the packet ++continue requests for next lookup to match + +2)In order to take advantage of some of the targets written by the +iptables people, a classifier can have a packet being massaged by an +iptable target. I have only tested with mangler targets up to now. +(infact anything that is not in the mangling table is disabled right now) + +In terms of hooks: +*ingress is mapped to pre-routing hook +*egress is mapped to post-routing hook +I don't see much value in the other hooks, if you see it and email me good +reasons, the addition is trivial. + +Example syntax for iptables targets usage becomes: +tc filter add ..... u32 action ipt -j + +example: +tc filter add dev lo parent ffff: protocol ip prio 8 u32 \ +match ip dst 127.0.0.8/32 flowid 1:12 \ +action ipt -j mark --set-mark 2 + +NOTE: flowid 1:12 is parsed flowid 0x1:0x12. Make sure if you want flowid +decimal 12, then use flowid 1:c. + +3) A feature i call pipe +The motivation is derived from Unix pipe mechanism but applied to packets. +Essentially take a matching packet and pass it through +action1 | action2 | action3 etc. +You could do something similar to this with the tc policer and the "continue" +operator but this rather restricts it to just the policer and requires +multiple rules (and lookups, hence quiet inefficient); + +as an example -- and please note that this is just an example _not_ The +Word Youve Been Waiting For (yes i have had problems giving examples +which ended becoming dogma in documents and people modifying them a little +to look clever); + +i selected the metering rates to be small so that i can show better how +things work. + +The script below does the following: +- an incoming packet from 10.0.0.21 is first given a firewall mark of 1. + +- It is then metered to make sure it does not exceed its allocated rate of +1Kbps. If it doesn't exceed rate, this is where we terminate action execution. + +- If it does exceed its rate, its "color" changes to a mark of 2 and it is +then passed through a second meter. + +-The second meter is shared across all flows on that device [i am surpised +that this seems to be not a well know feature of the policer; Bert was telling +me that someone was writing a qdisc just to do sharing across multiple devices; +it must be the summer heat again; weve had someone doing that every year around +summer -- the key to sharing is to use a operator "index" in your policer +rules (example "index 20"). All your rules have to use the same index to +share.] + +-If the second meter is exceeded the color of the flow changes further to 3. + +-We then pass the packet to another meter which is shared across all devices +in the system. If this meter is exceeded we drop the packet. + +Note the mark can be used further up the system to do things like policy +or more interesting things on the egress. + +------------------ cut here ------------------------------- +# +# Add an ingress qdisc on eth0 +tc qdisc add dev eth0 ingress +# +#if you see an incoming packet from 10.0.0.21 +tc filter add dev eth0 parent ffff: protocol ip prio 1 \ +u32 match ip src 10.0.0.21/32 flowid 1:15 \ +# +# first give it a mark of 1 +action ipt -j mark --set-mark 1 index 2 \ +# +# then pass it through a policer which allows 1kbps; if the flow +# doesn't exceed that rate, this is where we stop, if it exceeds we +# pipe the packet to the next action +action police rate 1kbit burst 9k pipe \ +# +# which marks the packet fwmark as 2 and pipes +action ipt -j mark --set-mark 2 \ +# +# next attempt to borrow b/width from a meter +# used across all flows incoming on eth0("index 30") +# and if that is exceeded we pipe to the next action +action police index 30 mtu 5000 rate 1kbit burst 10k pipe \ +# mark it as fwmark 3 if exceeded +action ipt -j mark --set-mark 3 \ +# and then attempt to borrow from a meter used by all devices in the +# system. Should this be exceeded, drop the packet on the floor. +action police index 20 mtu 5000 rate 1kbit burst 90k drop +--------------------------------- + +Now lets see the actions installed with +"tc filter show parent ffff: dev eth0" + +-------- output ----------- +jroot# tc filter show parent ffff: dev eth0 +filter protocol ip pref 1 u32 +filter protocol ip pref 1 u32 fh 800: ht divisor 1 +filter protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:15 + + action order 1: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x1 index 2 + + action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb + + action order 3: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x2 index 1 + + action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b + + action order 5: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x3 index 3 + + action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b + + match 0a000015/ffffffff at 12 +------------------------------- + +Note the ordering of the actions is based on the order in which we entered +them. In the future i will add explicit priorities. + +Now lets run a ping -f from 10.0.0.21 to this host; stop the ping after +you see a few lines of dots + +---- +[root@jzny hadi]# ping -f 10.0.0.22 +PING 10.0.0.22 (10.0.0.22): 56 data bytes +.................................................................................................................................................................................................................................................................................................................................................................................................................................................... +--- 10.0.0.22 ping statistics --- +2248 packets transmitted, 1811 packets received, 19% packet loss +round-trip min/avg/max = 0.7/9.3/20.1 ms +----------------------------- + +Now lets take a look at the stats with "tc -s filter show parent ffff: dev eth0" + +-------------- +jroot# tc -s filter show parent ffff: dev eth0 +filter protocol ip pref 1 u32 +filter protocol ip pref 1 u32 fh 800: ht divisor 1 +filter protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 +5 + + action order 1: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x1 index 2 + Sent 188832 bytes 2248 pkts (dropped 0, overlimits 0) + + action order 2: police 1 action pipe rate 1Kbit burst 9Kb mtu 2Kb + Sent 188832 bytes 2248 pkts (dropped 0, overlimits 2122) + + action order 3: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x2 index 1 + Sent 178248 bytes 2122 pkts (dropped 0, overlimits 0) + + action order 4: police 30 action pipe rate 1Kbit burst 10Kb mtu 5000b + Sent 178248 bytes 2122 pkts (dropped 0, overlimits 1945) + + action order 5: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x3 index 3 + Sent 163380 bytes 1945 pkts (dropped 0, overlimits 0) + + action order 6: police 20 action drop rate 1Kbit burst 90Kb mtu 5000b + Sent 163380 bytes 1945 pkts (dropped 0, overlimits 437) + + match 0a000015/ffffffff at 12 +------------------------------- + +Neat, eh? + + +Want to write an action module? +------------------------------ +Its easy. Either look at the code or send me email. I will document at +some point; will also accept documentation. + +TODO +---- + +Lotsa goodies/features coming. Requests also being accepted. +At the moment the focus has been on getting the architecture in place. +Expect new things in the spurious time i have to work on this +(particularly around end of year when i have typically get time off +from work). diff --git a/doc/actions/gact-usage b/doc/actions/gact-usage new file mode 100644 index 0000000..5fc3e62 --- /dev/null +++ b/doc/actions/gact-usage @@ -0,0 +1,78 @@ + +gact [RAND] [INDEX] + +Where: + ACTION := reclassify | drop | continue | pass | ok + RAND := random + RANDTYPE := netrand | determ + VAL : = value not exceeding 10000 + INDEX := index value used + +ACTION semantics +- pass and ok are equivalent to accept +- continue allows to restart classification lookup +- drop drops packets +- reclassify implies continue classification where we left off + +randomization +-------------- + +At the moment there are only two algorithms. One is deterministic +and the other uses internal kernel netrand. + +Examples: + +Rules can be installed on both ingress and egress - this shows ingress +only + +tc qdisc add dev eth0 ingress + +# example 1 +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop + +ping -c 20 10.0.0.9 + +-- +filter u32 +filter u32 fh 800: ht divisor 1 +filter u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 32 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type none pass val 0 + index 1 ref 1 bind 1 installed 59 sec used 35 sec + Sent 1680 bytes 20 pkts (dropped 20, overlimits 0 ) + +---- + +# example 2 +#allow 1 out 10 randomly using the netrand generator +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random netrand ok 10 + +ping -c 20 10.0.0.9 + +---- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type netrand pass val 10 + index 5 ref 1 bind 1 installed 49 sec used 25 sec + Sent 1680 bytes 20 pkts (dropped 16, overlimits 0 ) + +-------- +#alternative: deterministically accept every second packet +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random determ ok 2 + +ping -c 20 10.0.0.9 + +tc -s filter show parent ffff: dev eth0 +----- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type determ pass val 2 + index 4 ref 1 bind 1 installed 118 sec used 82 sec + Sent 1680 bytes 20 pkts (dropped 10, overlimits 0 ) +----- diff --git a/doc/actions/ifb-README b/doc/actions/ifb-README new file mode 100644 index 0000000..5fe9171 --- /dev/null +++ b/doc/actions/ifb-README @@ -0,0 +1,125 @@ + +IFB is intended to replace IMQ. +Advantage over current IMQ; cleaner in particular in in SMP; +with a _lot_ less code. + +Known IMQ/IFB USES +------------------ + +As far as i know the reasons listed below is why people use IMQ. +It would be nice to know of anything else that i missed. + +1) qdiscs/policies that are per device as opposed to system wide. +IFB allows for sharing. + +2) Allows for queueing incoming traffic for shaping instead of +dropping. I am not aware of any study that shows policing is +worse than shaping in achieving the end goal of rate control. +I would be interested if anyone is experimenting. + +3) Very interesting use: if you are serving p2p you may want to give +preference to your own locally originated traffic (when responses come back) +vs someone using your system to do bittorent. So QoSing based on state +comes in as the solution. What people did to achieve this was stick +the IMQ somewhere prelocal hook. +I think this is a pretty neat feature to have in Linux in general. +(i.e not just for IMQ). +But i won't go back to putting netfilter hooks in the device to satisfy +this. I also don't think its worth it hacking ifb some more to be +aware of say L3 info and play ip rule tricks to achieve this. +--> Instead the plan is to have a conntrack related action. This action will +selectively either query/create conntrack state on incoming packets. +Packets could then be redirected to ifb based on what happens -> eg +on incoming packets; if we find they are of known state we could send to +a different queue than one which didn't have existing state. This +all however is dependent on whatever rules the admin enters. + +At the moment this 3rd function does not exist yet. I have decided that +instead of sitting on the patch for another year, to release it and then +if there is pressure i will add this feature. + +An example, to provide functionality that most people use IMQ for below: + +-------- +export TC="/sbin/tc" + +$TC qdisc add dev ifb0 root handle 1: prio +$TC qdisc add dev ifb0 parent 1:1 handle 10: sfq +$TC qdisc add dev ifb0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000 +$TC qdisc add dev ifb0 parent 1:3 handle 30: sfq +$TC filter add dev ifb0 protocol ip pref 1 parent 1: handle 1 fw classid 1:1 +$TC filter add dev ifb0 protocol ip pref 2 parent 1: handle 2 fw classid 1:2 + +ifconfig ifb0 up + +$TC qdisc add dev eth0 ingress + +# redirect all IP packets arriving in eth0 to ifb0 +# use mark 1 --> puts them onto class 1:1 +$TC filter add dev eth0 parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:1 \ +action ipt -j MARK --set-mark 1 \ +action mirred egress redirect dev ifb0 + +-------- + + +Run A Little test: + +from another machine ping so that you have packets going into the box: +----- +[root@jzny action-tests]# ping 10.22 +PING 10.22 (10.0.0.22): 56 data bytes +64 bytes from 10.0.0.22: icmp_seq=0 ttl=64 time=2.8 ms +64 bytes from 10.0.0.22: icmp_seq=1 ttl=64 time=0.6 ms +64 bytes from 10.0.0.22: icmp_seq=2 ttl=64 time=0.6 ms + +--- 10.22 ping statistics --- +3 packets transmitted, 3 packets received, 0% packet loss +round-trip min/avg/max = 0.6/1.3/2.8 ms +[root@jzny action-tests]# +----- +Now look at some stats: + +--- +[root@jmandrake]:~# $TC -s filter show parent ffff: dev eth0 +filter protocol ip pref 10 u32 +filter protocol ip pref 10 u32 fh 800: ht divisor 1 +filter protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 + match 00000000/00000000 at 0 + action order 1: tablename: mangle hook: NF_IP_PRE_ROUTING + target MARK set 0x1 + index 1 ref 1 bind 1 installed 4195sec used 27sec + Sent 252 bytes 3 pkts (dropped 0, overlimits 0) + + action order 2: mirred (Egress Redirect to device ifb0) stolen + index 1 ref 1 bind 1 installed 165 sec used 27 sec + Sent 252 bytes 3 pkts (dropped 0, overlimits 0) + +[root@jmandrake]:~# $TC -s qdisc +qdisc sfq 30: dev ifb0 limit 128p quantum 1514b + Sent 0 bytes 0 pkts (dropped 0, overlimits 0) +qdisc tbf 20: dev ifb0 rate 20Kbit burst 1575b lat 2147.5s + Sent 210 bytes 3 pkts (dropped 0, overlimits 0) +qdisc sfq 10: dev ifb0 limit 128p quantum 1514b + Sent 294 bytes 3 pkts (dropped 0, overlimits 0) +qdisc prio 1: dev ifb0 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 + Sent 504 bytes 6 pkts (dropped 0, overlimits 0) +qdisc ingress ffff: dev eth0 ---------------- + Sent 308 bytes 5 pkts (dropped 0, overlimits 0) + +[root@jmandrake]:~# ifconfig ifb0 +ifb0 Link encap:Ethernet HWaddr 00:00:00:00:00:00 + inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link + UP BROADCAST RUNNING NOARP MTU:1500 Metric:1 + RX packets:6 errors:0 dropped:3 overruns:0 frame:0 + TX packets:3 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:32 + RX bytes:504 (504.0 b) TX bytes:252 (252.0 b) +----- + +You send it any packet not originating from the actions it will drop them. +[In this case the three dropped packets were ipv6 ndisc]. + +cheers, +jamal diff --git a/doc/actions/mirred-usage b/doc/actions/mirred-usage new file mode 100644 index 0000000..482ff66 --- /dev/null +++ b/doc/actions/mirred-usage @@ -0,0 +1,164 @@ + +Very funky action. I do plan to add to a few more things to it +This is the basic stuff. Idea borrowed from the way ethernet switches +mirror and redirect packets. The main difference with say a vannila +ethernet switch is that you can use u32 classifier to select a +flow to be mirrored. High end switches typically can select based +on more than just a port (eg a 5 tuple classifier). They may also be +capable of redirecting. + +Usage: + +mirred [index INDEX] +where: +DIRECTION := +ACTION := +INDEX is the specific policy instance id +DEVICENAME is the devicename + +Direction: +- Ingress is not supported at the moment. It will be in the +future as well as mirror/redirecting to a socket. + +Action: +- Mirror takes a copy of the packet and sends it to specified +dev ("port" in ethernet switch/bridging terminology) +- redirect +steals the packet and redirects to specified destination dev. + +What NOT to do if you don't want your machine to crash: +------------------------------------------------------ + +Do not create loops! +Loops are not hard to create in the egress qdiscs. + +Here are simple rules to follow if you don't want to get +hurt: +A) Do not have the same packet go to same netdevice twice +in a single graph of policies. Your machine will just hang! +This is design intent _not a bug_ to teach you some lessons. + +In the future if there are easy ways to do this in the kernel +without affecting other packets not interested in this feature +I will add them. At the moment that is not clear. + +Some examples of bad things NOT to do: +1) redirecting eth0 to eth0 +2) eth0->eth1-> eth0 +3) eth0->lo-> eth1-> eth0 + +B) Do not redirect from one IFB device to another. +Remember that IFB is a very specialized case of packet redirecting +device. Instead of redirecting it puts packets at the exact spot +on the stack it found them from. +Redirecting from ifbX->ifbY will actually not crash your machine but your +packets will all be dropped (this is much simpler to detect +and resolve and is only affecting users of ifb as opposed to the +whole stack). + +In the case of A) the problem has to do with a recursive contention +for the devices queue lock and in the second case for the transmit lock. + +Some examples: +------------- + +1) Mirror all packets arriving on eth0 to be sent out on eth1. +You may have a sniffer or some accounting box hooked up on eth1. + +--- +tc qdisc add dev eth0 ingress +tc filter add dev eth0 parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress mirror dev eth1 +--- + +If you replace "mirror" with "redirect" then not a copy but rather +the original packet is sent to eth1. + +2) Host A is hooked up to us on eth0 + +# redirect all packets arriving on ingress of lo to eth0 +--- +tc qdisc add dev lo ingress +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress redirect dev eth0 +--- + +On host A start a tcpdump on interface connecting to us. + +on our host ping -c 2 127.0.0.1 + +Ping would fail since all packets are heading out eth0 +tcpudmp on host A would show them + +if you substitute the redirect with mirror above as in: +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress mirror dev eth0 + +Then you should see the packets on both host A and the local +stack (i.e ping would work). + +3) Even more funky example: + +# +#allow 1 out 10 packets on ingress of lo to randomly make it to the +# host A (Randomness uses the netrand generator) +# +--- +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 \ +action drop random determ ok 10\ +action mirred egress mirror dev eth0 +--- + +4) +# for packets from 10.0.0.9 going out on eth0 (could be local +# IP or something # we are forwarding) - +# if exceeding a 100Kbps rate, then redirect to eth1 +# + +--- +tc qdisc add dev eth0 handle 1:0 root prio +tc filter add dev eth0 parent 1:0 protocol ip prio 6 u32 \ +match ip src 10.0.0.9/32 flowid 1:16 \ +action police rate 100kbit burst 90k ok \ +action mirred egress mirror dev eth1 +--- + +A more interesting example is when you mirror flows to a dummy device +so you could tcpdump them (dummy by defaults drops all packets it sees). +This is a very useful debug feature. + +Lets say you are policing packets from alias 192.168.200.200/32 +you don't want those to exceed 100kbps going out. + +--- +tc qdisc add dev eth0 handle 1:0 root prio +tc filter add dev eth0 parent 1: protocol ip prio 10 u32 \ +match ip src 192.168.200.200/32 flowid 1:2 \ +action police rate 100kbit burst 90k drop +--- + +If you run tcpdump on eth0 you will see all packets going out +with src 192.168.200.200/32 dropped or not (since tcpdump shows +all packets being egressed). +Extend the rule a little to see only the packets making it out. + +--- +tc qdisc add dev eth0 handle 1:0 root prio +tc filter add dev eth0 parent 1: protocol ip prio 10 u32 \ +match ip src 192.168.200.200/32 flowid 1:2 \ +action police rate 10kbit burst 90k drop \ +action mirred egress mirror dev dummy0 +--- + +Now fire tcpdump on dummy0 to see only those packets .. +tcpdump -n -i dummy0 -x -e -t + +Essentially a good debugging/logging interface (sort of like +BSDs speacialized log device does without needing one). + +If you replace mirror with redirect, those packets will be +blackholed and will never make it out. + +cheers, +jamal diff --git a/etc/iproute2/bpf_pinning b/etc/iproute2/bpf_pinning new file mode 100644 index 0000000..2b39c70 --- /dev/null +++ b/etc/iproute2/bpf_pinning @@ -0,0 +1,6 @@ +# +# subpath mappings from mount point for pinning +# +#3 tracing +#4 foo/bar +#5 tc/cls1 diff --git a/etc/iproute2/ematch_map b/etc/iproute2/ematch_map new file mode 100644 index 0000000..4d6bb2f --- /dev/null +++ b/etc/iproute2/ematch_map @@ -0,0 +1,8 @@ +# lookup table for ematch kinds +1 cmp +2 nbyte +3 u32 +4 meta +7 canid +8 ipset +9 ipt diff --git a/etc/iproute2/group b/etc/iproute2/group new file mode 100644 index 0000000..6f000b2 --- /dev/null +++ b/etc/iproute2/group @@ -0,0 +1,2 @@ +# device group names +0 default diff --git a/etc/iproute2/nl_protos b/etc/iproute2/nl_protos new file mode 100644 index 0000000..7c17cf0 --- /dev/null +++ b/etc/iproute2/nl_protos @@ -0,0 +1,23 @@ +# Netlink protocol names mapping + +0 rtnl +1 unused +2 usersock +3 fw +4 tcpdiag +5 nflog +6 xfrm +7 selinux +8 iscsi +9 audit +10 fiblookup +11 connector +12 nft +13 ip6fw +14 dec-rt +15 uevent +16 genl +18 scsi-trans +19 ecryptfs +20 rdma +21 crypto diff --git a/etc/iproute2/rt_dsfield b/etc/iproute2/rt_dsfield new file mode 100644 index 0000000..1426d60 --- /dev/null +++ b/etc/iproute2/rt_dsfield @@ -0,0 +1,26 @@ +# Differentiated field values +# These include the DSCP and unused bits +0x0 default +# Newer RFC2597 values +0x28 AF11 +0x30 AF12 +0x38 AF13 +0x48 AF21 +0x50 AF22 +0x58 AF23 +0x68 AF31 +0x70 AF32 +0x78 AF33 +0x88 AF41 +0x90 AF42 +0x98 AF43 +# Older values RFC2474 +0x20 CS1 +0x40 CS2 +0x60 CS3 +0x80 CS4 +0xA0 CS5 +0xC0 CS6 +0xE0 CS7 +# RFC 2598 +0xB8 EF diff --git a/etc/iproute2/rt_protos b/etc/iproute2/rt_protos new file mode 100644 index 0000000..7cafddc --- /dev/null +++ b/etc/iproute2/rt_protos @@ -0,0 +1,24 @@ +# +# Reserved protocols. +# +0 unspec +1 redirect +2 kernel +3 boot +4 static +8 gated +9 ra +10 mrt +11 zebra +12 bird +13 dnrouted +14 xorp +15 ntk +16 dhcp +18 keepalived +42 babel +186 bgp +187 isis +188 ospf +189 rip +192 eigrp diff --git a/etc/iproute2/rt_protos.d/README b/etc/iproute2/rt_protos.d/README new file mode 100644 index 0000000..f9c599c --- /dev/null +++ b/etc/iproute2/rt_protos.d/README @@ -0,0 +1,2 @@ +Each file in this directory is an rt_protos configuration file. iproute2 +commands scan this directory processing all files that end in '.conf'. diff --git a/etc/iproute2/rt_realms b/etc/iproute2/rt_realms new file mode 100644 index 0000000..eedd76d --- /dev/null +++ b/etc/iproute2/rt_realms @@ -0,0 +1,13 @@ +# +# reserved values +# +0 cosmos +# +# local +# +#1 inr.ac +#2 inr.ruhep +#3 freenet +#4 radio-msu +#5 russia +#6 internet diff --git a/etc/iproute2/rt_scopes b/etc/iproute2/rt_scopes new file mode 100644 index 0000000..8514bc1 --- /dev/null +++ b/etc/iproute2/rt_scopes @@ -0,0 +1,11 @@ +# +# reserved values +# +0 global +255 nowhere +254 host +253 link +# +# pseudo-reserved +# +200 site diff --git a/etc/iproute2/rt_tables b/etc/iproute2/rt_tables new file mode 100644 index 0000000..541abfd --- /dev/null +++ b/etc/iproute2/rt_tables @@ -0,0 +1,11 @@ +# +# reserved values +# +255 local +254 main +253 default +0 unspec +# +# local +# +#1 inr.ruhep diff --git a/etc/iproute2/rt_tables.d/README b/etc/iproute2/rt_tables.d/README new file mode 100644 index 0000000..0920cb1 --- /dev/null +++ b/etc/iproute2/rt_tables.d/README @@ -0,0 +1,2 @@ +Each file in this directory is an rt_tables configuration file. iproute2 +commands scan this directory processing all files that end in '.conf'. diff --git a/examples/bpf/README b/examples/bpf/README new file mode 100644 index 0000000..1bbdda3 --- /dev/null +++ b/examples/bpf/README @@ -0,0 +1,8 @@ +eBPF toy code examples (running in kernel) to familiarize yourself +with syntax and features: + + - bpf_shared.c -> Ingress/egress map sharing example + - bpf_tailcall.c -> Using tail call chains + - bpf_cyclic.c -> Simple cycle as tail calls + - bpf_graft.c -> Demo on altering runtime behaviour + - bpf_map_in_map.c -> Using map in map example diff --git a/examples/bpf/bpf_cyclic.c b/examples/bpf/bpf_cyclic.c new file mode 100644 index 0000000..11d1c06 --- /dev/null +++ b/examples/bpf/bpf_cyclic.c @@ -0,0 +1,35 @@ +#include "../../include/bpf_api.h" + +/* Cyclic dependency example to test the kernel's runtime upper + * bound on loops. Also demonstrates on how to use direct-actions, + * loaded as: tc filter add [...] bpf da obj [...] + */ +#define JMP_MAP_ID 0xabccba + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = JMP_MAP_ID, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +__section_tail(JMP_MAP_ID, 0) +int cls_loop(struct __sk_buff *skb) +{ + printt("cb: %u\n", skb->cb[0]++); + tail_call(skb, &jmp_tc, 0); + + skb->tc_classid = TC_H_MAKE(1, 42); + return TC_ACT_OK; +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + tail_call(skb, &jmp_tc, 0); + return TC_ACT_SHOT; +} + +BPF_LICENSE("GPL"); diff --git a/examples/bpf/bpf_graft.c b/examples/bpf/bpf_graft.c new file mode 100644 index 0000000..07113d4 --- /dev/null +++ b/examples/bpf/bpf_graft.c @@ -0,0 +1,66 @@ +#include "../../include/bpf_api.h" + +/* This example demonstrates how classifier run-time behaviour + * can be altered with tail calls. We start out with an empty + * jmp_tc array, then add section aaa to the array slot 0, and + * later on atomically replace it with section bbb. Note that + * as shown in other examples, the tc loader can prepopulate + * tail called sections, here we start out with an empty one + * on purpose to show it can also be done this way. + * + * tc filter add dev foo parent ffff: bpf obj graft.o + * tc exec bpf dbg + * [...] + * Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough + * -0 [001] ..s. 138993.202265: : fallthrough + * Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough + * [...] + * + * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa + * tc exec bpf dbg + * [...] + * Socket Thread-19818 [002] ..s. 139012.053587: : aaa + * -0 [002] ..s. 139012.172359: : aaa + * Socket Thread-19818 [001] ..s. 139012.173556: : aaa + * [...] + * + * tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb + * tc exec bpf dbg + * [...] + * Socket Thread-19818 [002] ..s. 139022.102967: : bbb + * -0 [002] ..s. 139022.155640: : bbb + * Socket Thread-19818 [001] ..s. 139022.156730: : bbb + * [...] + */ + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_GLOBAL_NS, + .max_elem = 1, +}; + +__section("aaa") +int cls_aaa(struct __sk_buff *skb) +{ + printt("aaa\n"); + return TC_H_MAKE(1, 42); +} + +__section("bbb") +int cls_bbb(struct __sk_buff *skb) +{ + printt("bbb\n"); + return TC_H_MAKE(1, 43); +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + tail_call(skb, &jmp_tc, 0); + printt("fallthrough\n"); + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff --git a/examples/bpf/bpf_map_in_map.c b/examples/bpf/bpf_map_in_map.c new file mode 100644 index 0000000..ff0e623 --- /dev/null +++ b/examples/bpf/bpf_map_in_map.c @@ -0,0 +1,56 @@ +#include "../../include/bpf_api.h" + +#define MAP_INNER_ID 42 + +struct bpf_elf_map __section_maps map_inner = { + .type = BPF_MAP_TYPE_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .id = MAP_INNER_ID, + .inner_idx = 0, + .pinning = PIN_GLOBAL_NS, + .max_elem = 1, +}; + +struct bpf_elf_map __section_maps map_outer = { + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .inner_id = MAP_INNER_ID, + .pinning = PIN_GLOBAL_NS, + .max_elem = 1, +}; + +__section("egress") +int emain(struct __sk_buff *skb) +{ + struct bpf_elf_map *map_inner; + int key = 0, *val; + + map_inner = map_lookup_elem(&map_outer, &key); + if (map_inner) { + val = map_lookup_elem(map_inner, &key); + if (val) + lock_xadd(val, 1); + } + + return BPF_H_DEFAULT; +} + +__section("ingress") +int imain(struct __sk_buff *skb) +{ + struct bpf_elf_map *map_inner; + int key = 0, *val; + + map_inner = map_lookup_elem(&map_outer, &key); + if (map_inner) { + val = map_lookup_elem(map_inner, &key); + if (val) + printt("map val: %d\n", *val); + } + + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff --git a/examples/bpf/bpf_shared.c b/examples/bpf/bpf_shared.c new file mode 100644 index 0000000..21fe6f1 --- /dev/null +++ b/examples/bpf/bpf_shared.c @@ -0,0 +1,53 @@ +#include "../../include/bpf_api.h" + +/* Minimal, stand-alone toy map pinning example: + * + * clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c + * tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress + * tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress + * + * Both classifier will share the very same map instance in this example, + * so map content can be accessed from ingress *and* egress side! + * + * This example has a pinning of PIN_OBJECT_NS, so it's private and + * thus shared among various program sections within the object. + * + * A setting of PIN_GLOBAL_NS would place it into a global namespace, + * so that it can be shared among different object files. A setting + * of PIN_NONE (= 0) means no sharing, so each tc invocation a new map + * instance is being created. + */ + +struct bpf_elf_map __section_maps map_sh = { + .type = BPF_MAP_TYPE_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, /* or PIN_GLOBAL_NS, or PIN_NONE */ + .max_elem = 1, +}; + +__section("egress") +int emain(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + lock_xadd(val, 1); + + return BPF_H_DEFAULT; +} + +__section("ingress") +int imain(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("map val: %d\n", *val); + + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff --git a/examples/bpf/bpf_tailcall.c b/examples/bpf/bpf_tailcall.c new file mode 100644 index 0000000..161eb60 --- /dev/null +++ b/examples/bpf/bpf_tailcall.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "../../include/bpf_api.h" + +#define ENTRY_INIT 3 +#define ENTRY_0 0 +#define ENTRY_1 1 +#define MAX_JMP_SIZE 2 + +#define FOO 42 +#define BAR 43 + +/* This example doesn't really do anything useful, but it's purpose is to + * demonstrate eBPF tail calls on a very simple example. + * + * cls_entry() is our classifier entry point, from there we jump based on + * skb->hash into cls_case1() or cls_case2(). They are both part of the + * program array jmp_tc. Indicated via __section_tail(), the tc loader + * populates the program arrays with the loaded file descriptors already. + * + * To demonstrate nested jumps, cls_case2() jumps within the same jmp_tc + * array to cls_case1(). And whenever we arrive at cls_case1(), we jump + * into cls_exit(), part of the jump array jmp_ex. + * + * Also, to show it's possible, all programs share map_sh and dump the value + * that the entry point incremented. The sections that are loaded into a + * program array can be atomically replaced during run-time, e.g. to change + * classifier behaviour. + */ + +struct bpf_elf_map __section_maps jmp_tc = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = FOO, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = MAX_JMP_SIZE, +}; + +struct bpf_elf_map __section_maps jmp_ex = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .id = BAR, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +struct bpf_elf_map __section_maps map_sh = { + .type = BPF_MAP_TYPE_ARRAY, + .size_key = sizeof(uint32_t), + .size_value = sizeof(uint32_t), + .pinning = PIN_OBJECT_NS, + .max_elem = 1, +}; + +__section_tail(FOO, ENTRY_0) +int cls_case1(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("case1: map-val: %d from:%u\n", *val, skb->cb[0]); + + skb->cb[0] = ENTRY_0; + tail_call(skb, &jmp_ex, ENTRY_0); + + return BPF_H_DEFAULT; +} + +__section_tail(FOO, ENTRY_1) +int cls_case2(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("case2: map-val: %d from:%u\n", *val, skb->cb[0]); + + skb->cb[0] = ENTRY_1; + tail_call(skb, &jmp_tc, ENTRY_0); + + return BPF_H_DEFAULT; +} + +__section_tail(BAR, ENTRY_0) +int cls_exit(struct __sk_buff *skb) +{ + int key = 0, *val; + + val = map_lookup_elem(&map_sh, &key); + if (val) + printt("exit: map-val: %d from:%u\n", *val, skb->cb[0]); + + /* Termination point. */ + return BPF_H_DEFAULT; +} + +__section_cls_entry +int cls_entry(struct __sk_buff *skb) +{ + int key = 0, *val; + + /* For transferring state, we can use skb->cb[0] ... skb->cb[4]. */ + val = map_lookup_elem(&map_sh, &key); + if (val) { + lock_xadd(val, 1); + + skb->cb[0] = ENTRY_INIT; + tail_call(skb, &jmp_tc, skb->hash & (MAX_JMP_SIZE - 1)); + } + + printt("fallthrough\n"); + return BPF_H_DEFAULT; +} + +BPF_LICENSE("GPL"); diff --git a/genl/.gitignore b/genl/.gitignore new file mode 100644 index 0000000..383ef04 --- /dev/null +++ b/genl/.gitignore @@ -0,0 +1 @@ +genl diff --git a/genl/Makefile b/genl/Makefile new file mode 100644 index 0000000..2b7a45b --- /dev/null +++ b/genl/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +GENLOBJ=genl.o + +include ../config.mk +SHARED_LIBS ?= y + +CFLAGS += -fno-strict-aliasing + +GENLMODULES := +GENLMODULES += ctrl.o + +GENLOBJ += $(GENLMODULES) + +GENLLIB := + +ifeq ($(SHARED_LIBS),y) +LDFLAGS += -Wl,-export-dynamic +LDLIBS += -lm -ldl +endif + +all: genl + +genl: $(GENLOBJ) $(LIBNETLINK) $(LIBUTIL) $(GENLLIB) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 genl $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(GENLOBJ) $(GENLLIB) genl + +ifneq ($(SHARED_LIBS),y) + +genl: static-syms.o +static-syms.o: static-syms.h +static-syms.h: $(wildcard *.c) + files="$^" ; \ + for s in `grep -B 3 '\ $@ + +endif diff --git a/genl/ctrl.c b/genl/ctrl.c new file mode 100644 index 0000000..0fb464b --- /dev/null +++ b/genl/ctrl.c @@ -0,0 +1,340 @@ +/* + * ctrl.c generic netlink controller + * + * This program is free software; you can distribute 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. + * + * Authors: J Hadi Salim (hadi@cyberus.ca) + * Johannes Berg (johannes@sipsolutions.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "genl_utils.h" + +#define GENL_MAX_FAM_OPS 256 +#define GENL_MAX_FAM_GRPS 256 + +static int usage(void) +{ + fprintf(stderr,"Usage: ctrl \n" \ + "CMD := get | list | monitor\n" \ + "PARMS := name | id \n" \ + "Examples:\n" \ + "\tctrl ls\n" \ + "\tctrl monitor\n" \ + "\tctrl get name foobar\n" \ + "\tctrl get id 0xF\n"); + return -1; +} + +static void print_ctrl_cmd_flags(FILE *fp, __u32 fl) +{ + fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl); + if (!fl) { + fprintf(fp, "\n"); + return; + } + fprintf(fp, "\t\t "); + + if (fl & GENL_ADMIN_PERM) + fprintf(fp, " requires admin permission;"); + if (fl & GENL_CMD_CAP_DO) + fprintf(fp, " can doit;"); + if (fl & GENL_CMD_CAP_DUMP) + fprintf(fp, " can dumpit;"); + if (fl & GENL_CMD_CAP_HASPOL) + fprintf(fp, " has policy"); + + fprintf(fp, "\n"); +} + +static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver) +{ + struct rtattr *tb[CTRL_ATTR_OP_MAX + 1]; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg); + if (tb[CTRL_ATTR_OP_ID]) { + __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]); + fprintf(fp, " ID-0x%x ",*id); + } + /* we are only gonna do this for newer version of the controller */ + if (tb[CTRL_ATTR_OP_FLAGS] && ctrl_ver >= 0x2) { + __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]); + print_ctrl_cmd_flags(fp, *fl); + } + return 0; + +} + +static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver) +{ + struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg); + if (tb[2]) { + __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]); + fprintf(fp, " ID-0x%x ",*id); + } + if (tb[1]) { + char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]); + fprintf(fp, " name: %s ", name); + } + return 0; + +} + +/* + * The controller sends one nlmsg per family +*/ +static int print_ctrl(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + struct rtattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *ghdr = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *attrs; + FILE *fp = (FILE *) arg; + __u32 ctrl_v = 0x1; + + if (n->nlmsg_type != GENL_ID_CTRL) { + fprintf(stderr, "Not a controller message, nlmsg_len=%d " + "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type); + return 0; + } + + if (ghdr->cmd != CTRL_CMD_GETFAMILY && + ghdr->cmd != CTRL_CMD_DELFAMILY && + ghdr->cmd != CTRL_CMD_NEWFAMILY && + ghdr->cmd != CTRL_CMD_NEWMCAST_GRP && + ghdr->cmd != CTRL_CMD_DELMCAST_GRP) { + fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd); + return 0; + } + + len -= NLMSG_LENGTH(GENL_HDRLEN); + + if (len < 0) { + fprintf(stderr, "wrong controller message len %d\n", len); + return -1; + } + + attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); + parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len); + + if (tb[CTRL_ATTR_FAMILY_NAME]) { + char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]); + fprintf(fp, "\nName: %s\n",name); + } + if (tb[CTRL_ATTR_FAMILY_ID]) { + __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + fprintf(fp, "\tID: 0x%x ",*id); + } + if (tb[CTRL_ATTR_VERSION]) { + __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]); + fprintf(fp, " Version: 0x%x ",*v); + ctrl_v = *v; + } + if (tb[CTRL_ATTR_HDRSIZE]) { + __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]); + fprintf(fp, " header size: %d ",*h); + } + if (tb[CTRL_ATTR_MAXATTR]) { + __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]); + fprintf(fp, " max attribs: %d ",*ma); + } + /* end of family definitions .. */ + fprintf(fp,"\n"); + if (tb[CTRL_ATTR_OPS]) { + struct rtattr *tb2[GENL_MAX_FAM_OPS]; + int i=0; + parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]); + fprintf(fp, "\tcommands supported: \n"); + for (i = 0; i < GENL_MAX_FAM_OPS; i++) { + if (tb2[i]) { + fprintf(fp, "\t\t#%d: ", i); + if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) { + fprintf(fp, "Error printing command\n"); + } + /* for next command */ + fprintf(fp,"\n"); + } + } + + /* end of family::cmds definitions .. */ + fprintf(fp,"\n"); + } + + if (tb[CTRL_ATTR_MCAST_GROUPS]) { + struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1]; + int i; + + parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS, + tb[CTRL_ATTR_MCAST_GROUPS]); + fprintf(fp, "\tmulticast groups:\n"); + + for (i = 0; i < GENL_MAX_FAM_GRPS; i++) { + if (tb2[i]) { + fprintf(fp, "\t\t#%d: ", i); + if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v)) + fprintf(fp, "Error printing group\n"); + /* for next group */ + fprintf(fp,"\n"); + } + } + + /* end of family::groups definitions .. */ + fprintf(fp,"\n"); + } + + fflush(fp); + return 0; +} + +static int print_ctrl2(struct nlmsghdr *n, void *arg) +{ + return print_ctrl(NULL, n, arg); +} + +static int ctrl_list(int cmd, int argc, char **argv) +{ + struct rtnl_handle rth; + int ret = -1; + char d[GENL_NAMSIZ]; + struct { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[4096]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = GENL_ID_CTRL, + .g.cmd = CTRL_CMD_GETFAMILY, + }; + struct nlmsghdr *nlh = &req.n; + struct nlmsghdr *answer = NULL; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + exit(1); + } + + if (cmd == CTRL_CMD_GETFAMILY) { + if (argc != 2) { + fprintf(stderr, "Wrong number of params\n"); + return -1; + } + + if (matches(*argv, "name") == 0) { + NEXT_ARG(); + strlcpy(d, *argv, sizeof(d)); + addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, + d, strlen(d) + 1); + } else if (matches(*argv, "id") == 0) { + __u16 id; + NEXT_ARG(); + if (get_u16(&id, *argv, 0)) { + fprintf(stderr, "Illegal \"id\"\n"); + goto ctrl_done; + } + + addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2); + + } else { + fprintf(stderr, "Wrong params\n"); + goto ctrl_done; + } + + if (rtnl_talk(&rth, nlh, &answer) < 0) { + fprintf(stderr, "Error talking to the kernel\n"); + goto ctrl_done; + } + + if (print_ctrl2(answer, (void *) stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + goto ctrl_done; + } + + } + + if (cmd == CTRL_CMD_UNSPEC) { + nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh->nlmsg_seq = rth.dump = ++rth.seq; + + if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) { + perror("Failed to send dump request\n"); + goto ctrl_done; + } + + rtnl_dump_filter(&rth, print_ctrl2, stdout); + + } + + ret = 0; +ctrl_done: + free(answer); + rtnl_close(&rth); + return ret; +} + +static int ctrl_listen(int argc, char **argv) +{ + struct rtnl_handle rth; + + if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) { + fprintf(stderr, "Canot open generic netlink socket\n"); + return -1; + } + + if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0) + return -1; + + return 0; +} + +static int parse_ctrl(struct genl_util *a, int argc, char **argv) +{ + argv++; + if (--argc <= 0) { + fprintf(stderr, "wrong controller params\n"); + return -1; + } + + if (matches(*argv, "monitor") == 0) + return ctrl_listen(argc-1, argv+1); + if (matches(*argv, "get") == 0) + return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1); + if (matches(*argv, "list") == 0 || + matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0) + return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1); + if (matches(*argv, "help") == 0) + return usage(); + + fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n", + *argv); + + return -1; +} + +struct genl_util ctrl_genl_util = { + .name = "ctrl", + .parse_genlopt = parse_ctrl, + .print_genlopt = print_ctrl2, +}; diff --git a/genl/genl.c b/genl/genl.c new file mode 100644 index 0000000..6557e6b --- /dev/null +++ b/genl/genl.c @@ -0,0 +1,149 @@ +/* + * genl.c "genl" utility frontend. + * + * 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. + * + * Authors: Jamal Hadi Salim + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* until we put our own header */ +#include "version.h" +#include "utils.h" +#include "genl_utils.h" + +int show_stats; +int show_details; +int show_raw; + +static void *BODY; +static struct genl_util *genl_list; + + +static int print_nofopt(struct nlmsghdr *n, void *arg) +{ + fprintf((FILE *) arg, "unknown genl type ..\n"); + return 0; +} + +static int parse_nofopt(struct genl_util *f, int argc, char **argv) +{ + if (argc) { + fprintf(stderr, + "Unknown genl \"%s\", hence option \"%s\" is unparsable\n", + f->name, *argv); + return -1; + } + + return 0; +} + +static struct genl_util *get_genl_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct genl_util *f; + + for (f = genl_list; f; f = f->next) + if (strcmp(f->name, str) == 0) + return f; + + snprintf(buf, sizeof(buf), "%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_genl_util", str); + + f = dlsym(dlh, buf); + if (f == NULL) + goto noexist; +reg: + f->next = genl_list; + genl_list = f; + return f; + +noexist: + f = calloc(1, sizeof(*f)); + if (f) { + strncpy(f->name, str, 15); + f->parse_genlopt = parse_nofopt; + f->print_genlopt = print_nofopt; + goto reg; + } + return f; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: genl [ OPTIONS ] OBJECT [help] }\n" + "where OBJECT := { ctrl etc }\n" + " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -V[ersion] | -h[elp] }\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + while (argc > 1) { + if (argv[1][0] != '-') + break; + if (matches(argv[1], "-stats") == 0 || + matches(argv[1], "-statistics") == 0) { + ++show_stats; + } else if (matches(argv[1], "-details") == 0) { + ++show_details; + } else if (matches(argv[1], "-raw") == 0) { + ++show_raw; + } else if (matches(argv[1], "-Version") == 0) { + printf("genl utility, iproute2-%s\n", version); + exit(0); + } else if (matches(argv[1], "-help") == 0) { + usage(); + } else { + fprintf(stderr, + "Option \"%s\" is unknown, try \"genl -help\".\n", + argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (argc > 1) { + struct genl_util *a; + int ret; + + a = get_genl_kind(argv[1]); + if (!a) { + fprintf(stderr, "bad genl %s\n", argv[1]); + exit(-1); + } + + ret = a->parse_genlopt(a, argc-1, argv+1); + return ret; + } + + usage(); +} diff --git a/genl/genl_utils.h b/genl/genl_utils.h new file mode 100644 index 0000000..87b4f34 --- /dev/null +++ b/genl/genl_utils.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TC_UTIL_H_ +#define _TC_UTIL_H_ 1 + +#include +#include "utils.h" + +struct genl_util { + struct genl_util *next; + char name[16]; + int (*parse_genlopt)(struct genl_util *fu, int argc, char **argv); + int (*print_genlopt)(struct nlmsghdr *n, void *arg); +}; + +#endif diff --git a/genl/static-syms.c b/genl/static-syms.c new file mode 100644 index 0000000..47c4092 --- /dev/null +++ b/genl/static-syms.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file creates a dummy version of dynamic loading + * for environments where dynamic linking + * is not used or available. + */ + +#include +#include "dlfcn.h" + +void *_dlsym(const char *sym) +{ +#include "static-syms.h" + return NULL; +} diff --git a/include/bpf_api.h b/include/bpf_api.h new file mode 100644 index 0000000..89d3488 --- /dev/null +++ b/include/bpf_api.h @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BPF_API__ +#define __BPF_API__ + +/* Note: + * + * This file can be included into eBPF kernel programs. It contains + * a couple of useful helper functions, map/section ABI (bpf_elf.h), + * misc macros and some eBPF specific LLVM built-ins. + */ + +#include + +#include +#include +#include + +#include + +#include "bpf_elf.h" + +/** Misc macros. */ + +#ifndef __stringify +# define __stringify(X) #X +#endif + +#ifndef __maybe_unused +# define __maybe_unused __attribute__((__unused__)) +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +#endif + +#ifndef likely +# define likely(X) __builtin_expect(!!(X), 1) +#endif + +#ifndef unlikely +# define unlikely(X) __builtin_expect(!!(X), 0) +#endif + +#ifndef htons +# define htons(X) __constant_htons((X)) +#endif + +#ifndef ntohs +# define ntohs(X) __constant_ntohs((X)) +#endif + +#ifndef htonl +# define htonl(X) __constant_htonl((X)) +#endif + +#ifndef ntohl +# define ntohl(X) __constant_ntohl((X)) +#endif + +#ifndef __inline__ +# define __inline__ __attribute__((always_inline)) +#endif + +/** Section helper macros. */ + +#ifndef __section +# define __section(NAME) \ + __attribute__((section(NAME), used)) +#endif + +#ifndef __section_tail +# define __section_tail(ID, KEY) \ + __section(__stringify(ID) "/" __stringify(KEY)) +#endif + +#ifndef __section_xdp_entry +# define __section_xdp_entry \ + __section(ELF_SECTION_PROG) +#endif + +#ifndef __section_cls_entry +# define __section_cls_entry \ + __section(ELF_SECTION_CLASSIFIER) +#endif + +#ifndef __section_act_entry +# define __section_act_entry \ + __section(ELF_SECTION_ACTION) +#endif + +#ifndef __section_lwt_entry +# define __section_lwt_entry \ + __section(ELF_SECTION_PROG) +#endif + +#ifndef __section_license +# define __section_license \ + __section(ELF_SECTION_LICENSE) +#endif + +#ifndef __section_maps +# define __section_maps \ + __section(ELF_SECTION_MAPS) +#endif + +/** Declaration helper macros. */ + +#ifndef BPF_LICENSE +# define BPF_LICENSE(NAME) \ + char ____license[] __section_license = NAME +#endif + +/** Classifier helper */ + +#ifndef BPF_H_DEFAULT +# define BPF_H_DEFAULT -1 +#endif + +/** BPF helper functions for tc. Individual flags are in linux/bpf.h */ + +#ifndef __BPF_FUNC +# define __BPF_FUNC(NAME, ...) \ + (* NAME)(__VA_ARGS__) __maybe_unused +#endif + +#ifndef BPF_FUNC +# define BPF_FUNC(NAME, ...) \ + __BPF_FUNC(NAME, __VA_ARGS__) = (void *) BPF_FUNC_##NAME +#endif + +/* Map access/manipulation */ +static void *BPF_FUNC(map_lookup_elem, void *map, const void *key); +static int BPF_FUNC(map_update_elem, void *map, const void *key, + const void *value, uint32_t flags); +static int BPF_FUNC(map_delete_elem, void *map, const void *key); + +/* Time access */ +static uint64_t BPF_FUNC(ktime_get_ns); + +/* Debugging */ + +/* FIXME: __attribute__ ((format(printf, 1, 3))) not possible unless + * llvm bug https://llvm.org/bugs/show_bug.cgi?id=26243 gets resolved. + * It would require ____fmt to be made const, which generates a reloc + * entry (non-map). + */ +static void BPF_FUNC(trace_printk, const char *fmt, int fmt_size, ...); + +#ifndef printt +# define printt(fmt, ...) \ + ({ \ + char ____fmt[] = fmt; \ + trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \ + }) +#endif + +/* Random numbers */ +static uint32_t BPF_FUNC(get_prandom_u32); + +/* Tail calls */ +static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map, + uint32_t index); + +/* System helpers */ +static uint32_t BPF_FUNC(get_smp_processor_id); +static uint32_t BPF_FUNC(get_numa_node_id); + +/* Packet misc meta data */ +static uint32_t BPF_FUNC(get_cgroup_classid, struct __sk_buff *skb); +static int BPF_FUNC(skb_under_cgroup, void *map, uint32_t index); + +static uint32_t BPF_FUNC(get_route_realm, struct __sk_buff *skb); +static uint32_t BPF_FUNC(get_hash_recalc, struct __sk_buff *skb); +static uint32_t BPF_FUNC(set_hash_invalid, struct __sk_buff *skb); + +/* Packet redirection */ +static int BPF_FUNC(redirect, int ifindex, uint32_t flags); +static int BPF_FUNC(clone_redirect, struct __sk_buff *skb, int ifindex, + uint32_t flags); + +/* Packet manipulation */ +static int BPF_FUNC(skb_load_bytes, struct __sk_buff *skb, uint32_t off, + void *to, uint32_t len); +static int BPF_FUNC(skb_store_bytes, struct __sk_buff *skb, uint32_t off, + const void *from, uint32_t len, uint32_t flags); + +static int BPF_FUNC(l3_csum_replace, struct __sk_buff *skb, uint32_t off, + uint32_t from, uint32_t to, uint32_t flags); +static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, uint32_t off, + uint32_t from, uint32_t to, uint32_t flags); +static int BPF_FUNC(csum_diff, const void *from, uint32_t from_size, + const void *to, uint32_t to_size, uint32_t seed); +static int BPF_FUNC(csum_update, struct __sk_buff *skb, uint32_t wsum); + +static int BPF_FUNC(skb_change_type, struct __sk_buff *skb, uint32_t type); +static int BPF_FUNC(skb_change_proto, struct __sk_buff *skb, uint32_t proto, + uint32_t flags); +static int BPF_FUNC(skb_change_tail, struct __sk_buff *skb, uint32_t nlen, + uint32_t flags); + +static int BPF_FUNC(skb_pull_data, struct __sk_buff *skb, uint32_t len); + +/* Event notification */ +static int __BPF_FUNC(skb_event_output, struct __sk_buff *skb, void *map, + uint64_t index, const void *data, uint32_t size) = + (void *) BPF_FUNC_perf_event_output; + +/* Packet vlan encap/decap */ +static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto, + uint16_t vlan_tci); +static int BPF_FUNC(skb_vlan_pop, struct __sk_buff *skb); + +/* Packet tunnel encap/decap */ +static int BPF_FUNC(skb_get_tunnel_key, struct __sk_buff *skb, + struct bpf_tunnel_key *to, uint32_t size, uint32_t flags); +static int BPF_FUNC(skb_set_tunnel_key, struct __sk_buff *skb, + const struct bpf_tunnel_key *from, uint32_t size, + uint32_t flags); + +static int BPF_FUNC(skb_get_tunnel_opt, struct __sk_buff *skb, + void *to, uint32_t size); +static int BPF_FUNC(skb_set_tunnel_opt, struct __sk_buff *skb, + const void *from, uint32_t size); + +/** LLVM built-ins, mem*() routines work for constant size */ + +#ifndef lock_xadd +# define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val)) +#endif + +#ifndef memset +# define memset(s, c, n) __builtin_memset((s), (c), (n)) +#endif + +#ifndef memcpy +# define memcpy(d, s, n) __builtin_memcpy((d), (s), (n)) +#endif + +#ifndef memmove +# define memmove(d, s, n) __builtin_memmove((d), (s), (n)) +#endif + +/* FIXME: __builtin_memcmp() is not yet fully useable unless llvm bug + * https://llvm.org/bugs/show_bug.cgi?id=26218 gets resolved. Also + * this one would generate a reloc entry (non-map), otherwise. + */ +#if 0 +#ifndef memcmp +# define memcmp(a, b, n) __builtin_memcmp((a), (b), (n)) +#endif +#endif + +unsigned long long load_byte(void *skb, unsigned long long off) + asm ("llvm.bpf.load.byte"); + +unsigned long long load_half(void *skb, unsigned long long off) + asm ("llvm.bpf.load.half"); + +unsigned long long load_word(void *skb, unsigned long long off) + asm ("llvm.bpf.load.word"); + +#endif /* __BPF_API__ */ diff --git a/include/bpf_elf.h b/include/bpf_elf.h new file mode 100644 index 0000000..84e8ae0 --- /dev/null +++ b/include/bpf_elf.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BPF_ELF__ +#define __BPF_ELF__ + +#include + +/* Note: + * + * Below ELF section names and bpf_elf_map structure definition + * are not (!) kernel ABI. It's rather a "contract" between the + * application and the BPF loader in tc. For compatibility, the + * section names should stay as-is. Introduction of aliases, if + * needed, are a possibility, though. + */ + +/* ELF section names, etc */ +#define ELF_SECTION_LICENSE "license" +#define ELF_SECTION_MAPS "maps" +#define ELF_SECTION_PROG "prog" +#define ELF_SECTION_CLASSIFIER "classifier" +#define ELF_SECTION_ACTION "action" + +#define ELF_MAX_MAPS 64 +#define ELF_MAX_LICENSE_LEN 128 + +/* Object pinning settings */ +#define PIN_NONE 0 +#define PIN_OBJECT_NS 1 +#define PIN_GLOBAL_NS 2 + +/* ELF map definition */ +struct bpf_elf_map { + __u32 type; + __u32 size_key; + __u32 size_value; + __u32 max_elem; + __u32 flags; + __u32 id; + __u32 pinning; + __u32 inner_id; + __u32 inner_idx; +}; + +#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ + struct ____btf_map_##name { \ + type_key key; \ + type_val value; \ + }; \ + struct ____btf_map_##name \ + __attribute__ ((section(".maps." #name), used)) \ + ____btf_map_##name = { } + +#endif /* __BPF_ELF__ */ diff --git a/include/bpf_scm.h b/include/bpf_scm.h new file mode 100644 index 0000000..669f053 --- /dev/null +++ b/include/bpf_scm.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BPF_SCM__ +#define __BPF_SCM__ + +#include +#include +#include + +#include "utils.h" +#include "bpf_elf.h" + +#define BPF_SCM_AUX_VER 1 +#define BPF_SCM_MAX_FDS ELF_MAX_MAPS +#define BPF_SCM_MSG_SIZE 1024 + +struct bpf_elf_st { + dev_t st_dev; + ino_t st_ino; +}; + +struct bpf_map_aux { + unsigned short uds_ver; + unsigned short num_ent; + char obj_name[64]; + struct bpf_elf_st obj_st; + struct bpf_elf_map ent[BPF_SCM_MAX_FDS]; +}; + +struct bpf_map_set_msg { + struct msghdr hdr; + struct iovec iov; + char msg_buf[BPF_SCM_MSG_SIZE]; + struct bpf_map_aux aux; +}; + +static inline int *bpf_map_set_init(struct bpf_map_set_msg *msg, + struct sockaddr_un *addr, + unsigned int addr_len) +{ + const unsigned int cmsg_ctl_len = sizeof(int) * BPF_SCM_MAX_FDS; + struct cmsghdr *cmsg; + + msg->iov.iov_base = &msg->aux; + msg->iov.iov_len = sizeof(msg->aux); + + msg->hdr.msg_iov = &msg->iov; + msg->hdr.msg_iovlen = 1; + + msg->hdr.msg_name = (struct sockaddr *)addr; + msg->hdr.msg_namelen = addr_len; + + BUILD_BUG_ON(sizeof(msg->msg_buf) < cmsg_ctl_len); + msg->hdr.msg_control = &msg->msg_buf; + msg->hdr.msg_controllen = cmsg_ctl_len; + + cmsg = CMSG_FIRSTHDR(&msg->hdr); + cmsg->cmsg_len = msg->hdr.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + return (int *)CMSG_DATA(cmsg); +} + +static inline void bpf_map_set_init_single(struct bpf_map_set_msg *msg, + int num) +{ + struct cmsghdr *cmsg; + + msg->hdr.msg_controllen = CMSG_LEN(sizeof(int) * num); + msg->iov.iov_len = offsetof(struct bpf_map_aux, ent) + + sizeof(struct bpf_elf_map) * num; + + cmsg = CMSG_FIRSTHDR(&msg->hdr); + cmsg->cmsg_len = msg->hdr.msg_controllen; +} + +#endif /* __BPF_SCM__ */ diff --git a/include/bpf_util.h b/include/bpf_util.h new file mode 100644 index 0000000..63db07c --- /dev/null +++ b/include/bpf_util.h @@ -0,0 +1,303 @@ +/* + * bpf_util.h BPF common code + * + * This program is free software; you can distribute 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. + * + * Authors: Daniel Borkmann + * Jiri Pirko + */ + +#ifndef __BPF_UTIL__ +#define __BPF_UTIL__ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "bpf_scm.h" + +#define BPF_ENV_UDS "TC_BPF_UDS" +#define BPF_ENV_MNT "TC_BPF_MNT" + +#ifndef BPF_MAX_LOG +# define BPF_MAX_LOG 4096 +#endif + +#define BPF_DIR_GLOBALS "globals" + +#ifndef BPF_FS_MAGIC +# define BPF_FS_MAGIC 0xcafe4a11 +#endif + +#define BPF_DIR_MNT "/sys/fs/bpf" + +#ifndef TRACEFS_MAGIC +# define TRACEFS_MAGIC 0x74726163 +#endif + +#define TRACE_DIR_MNT "/sys/kernel/tracing" + +#ifndef AF_ALG +# define AF_ALG 38 +#endif + +#ifndef EM_BPF +# define EM_BPF 247 +#endif + +struct bpf_cfg_ops { + void (*cbpf_cb)(void *nl, const struct sock_filter *ops, int ops_len); + void (*ebpf_cb)(void *nl, int fd, const char *annotation); +}; + +enum bpf_mode { + CBPF_BYTECODE, + CBPF_FILE, + EBPF_OBJECT, + EBPF_PINNED, + BPF_MODE_MAX, +}; + +struct bpf_cfg_in { + const char *object; + const char *section; + const char *uds; + enum bpf_prog_type type; + enum bpf_mode mode; + __u32 ifindex; + bool verbose; + int argc; + char **argv; + struct sock_filter opcodes[BPF_MAXINSNS]; + union { + int n_opcodes; + int prog_fd; + }; +}; + +/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU32_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_ALU32_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Short form of mov, dst_reg = src_reg */ + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV32_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* Short form of mov, dst_reg = imm32 */ + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV32_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +#ifndef BPF_PSEUDO_MAP_FD +# define BPF_PSEUDO_MAP_FD 1 +#endif + +/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ +#define BPF_LD_MAP_FD(DST, MAP_FD) \ + BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) + + +/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ + +#define BPF_LD_ABS(SIZE, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Memory load, dst_reg = *(uint *) (src_reg + off16) */ + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Memory store, *(uint *) (dst_reg + off16) = src_reg */ + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Memory store, *(uint *) (dst_reg + off16) = imm32 */ + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Raw code statement block */ + +#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = CODE, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = IMM }) + +/* Program exit */ + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + +int bpf_parse_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops); +int bpf_load_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops, + void *nl); +int bpf_parse_and_load_common(struct bpf_cfg_in *cfg, + const struct bpf_cfg_ops *ops, void *nl); + +const char *bpf_prog_to_default_section(enum bpf_prog_type type); + +int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv); +int bpf_trace_pipe(void); + +void bpf_print_ops(struct rtattr *bpf_ops, __u16 len); + +int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, + size_t size_insns, const char *license, char *log, + size_t size_log); + +int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type); +int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type); + +int bpf_dump_prog_info(FILE *f, uint32_t id); + +#ifdef HAVE_ELF +int bpf_send_map_fds(const char *path, const char *obj); +int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux, + unsigned int entries); +#else +static inline int bpf_send_map_fds(const char *path, const char *obj) +{ + return 0; +} + +static inline int bpf_recv_map_fds(const char *path, int *fds, + struct bpf_map_aux *aux, + unsigned int entries) +{ + return -1; +} +#endif /* HAVE_ELF */ +#endif /* __BPF_UTIL__ */ diff --git a/include/cg_map.h b/include/cg_map.h new file mode 100644 index 0000000..d30517f --- /dev/null +++ b/include/cg_map.h @@ -0,0 +1,6 @@ +#ifndef __CG_MAP_H__ +#define __CG_MAP_H__ + +const char *cg_id_to_path(__u64 id); + +#endif /* __CG_MAP_H__ */ diff --git a/include/color.h b/include/color.h new file mode 100644 index 0000000..17ec56f --- /dev/null +++ b/include/color.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __COLOR_H__ +#define __COLOR_H__ 1 + +#include + +enum color_attr { + COLOR_IFNAME, + COLOR_MAC, + COLOR_INET, + COLOR_INET6, + COLOR_OPERSTATE_UP, + COLOR_OPERSTATE_DOWN, + COLOR_NONE +}; + +enum color_opt { + COLOR_OPT_NEVER = 0, + COLOR_OPT_AUTO = 1, + COLOR_OPT_ALWAYS = 2 +}; + +bool check_enable_color(int color, int json); +bool matches_color(const char *arg, int *val); +int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...); +enum color_attr ifa_family_color(__u8 ifa_family); +enum color_attr oper_state_color(__u8 state); + +#endif diff --git a/include/dlfcn.h b/include/dlfcn.h new file mode 100644 index 0000000..1d8890a --- /dev/null +++ b/include/dlfcn.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Stub dlfcn implementation for systems that lack shared library support + * but obviously can still reference compiled-in symbols. + */ + +#ifndef NO_SHARED_LIBS +#include_next +#else + +#define RTLD_LAZY 0 +#define RTLD_GLOBAL 1 +#define _FAKE_DLFCN_HDL (void *)0xbeefcafe + +static inline void *dlopen(const char *file, int flag) +{ + if (file == NULL) + return _FAKE_DLFCN_HDL; + else + return NULL; +} + +void *_dlsym(const char *sym); +static inline void *dlsym(void *handle, const char *sym) +{ + if (handle != _FAKE_DLFCN_HDL) + return NULL; + return _dlsym(sym); +} + +static inline char *dlerror(void) +{ + return NULL; +} + +static inline int dlclose(void *handle) +{ + return (handle == _FAKE_DLFCN_HDL) ? 0 : 1; +} + +#endif diff --git a/include/ip6tables.h b/include/ip6tables.h new file mode 100644 index 0000000..bfb2868 --- /dev/null +++ b/include/ip6tables.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _IP6TABLES_USER_H +#define _IP6TABLES_USER_H + +#include +#include +#include +#include + +/* Your shared library should call one of these. */ +extern int do_command6(int argc, char *argv[], char **table, + struct xtc_handle **handle, bool restore); + +extern int for_each_chain6(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), int verbose, int builtinstoo, struct xtc_handle *handle); +extern int flush_entries6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle); +extern int delete_chain6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle); +void print_rule6(const struct ip6t_entry *e, struct xtc_handle *h, const char *chain, int counters); + +extern struct xtables_globals ip6tables_globals; + +#endif /*_IP6TABLES_USER_H*/ diff --git a/include/iptables.h b/include/iptables.h new file mode 100644 index 0000000..78bc378 --- /dev/null +++ b/include/iptables.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _IPTABLES_USER_H +#define _IPTABLES_USER_H + +#include +#include +#include +#include + +/* Your shared library should call one of these. */ +extern int do_command4(int argc, char *argv[], char **table, + struct xtc_handle **handle, bool restore); +extern int delete_chain4(const xt_chainlabel chain, int verbose, + struct xtc_handle *handle); +extern int flush_entries4(const xt_chainlabel chain, int verbose, + struct xtc_handle *handle); +extern int for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), + int verbose, int builtinstoo, struct xtc_handle *handle); +extern void print_rule4(const struct ipt_entry *e, + struct xtc_handle *handle, const char *chain, int counters); + +extern struct xtables_globals iptables_globals; + +extern struct xtables_globals xtables_globals; + +#endif /*_IPTABLES_USER_H*/ diff --git a/include/iptables/internal.h b/include/iptables/internal.h new file mode 100644 index 0000000..1fd1372 --- /dev/null +++ b/include/iptables/internal.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef IPTABLES_INTERNAL_H +#define IPTABLES_INTERNAL_H 1 + +#define IPTABLES_VERSION "1.6.0" + +/** + * Program's own name and version. + */ +extern const char *program_name, *program_version; + +extern int line; + +#endif /* IPTABLES_INTERNAL_H */ diff --git a/include/json_print.h b/include/json_print.h new file mode 100644 index 0000000..50e71de --- /dev/null +++ b/include/json_print.h @@ -0,0 +1,88 @@ +/* + * json_print.h "print regular or json output, based on json_writer". + * + * 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. + * + * Authors: Julien Fortin, + */ + +#ifndef _JSON_PRINT_H_ +#define _JSON_PRINT_H_ + +#include "json_writer.h" +#include "color.h" + +json_writer_t *get_json_writer(void); + +/* + * use: + * - PRINT_ANY for context based output + * - PRINT_FP for non json specific output + * - PRINT_JSON for json specific output + */ +enum output_type { + PRINT_FP = 1, + PRINT_JSON = 2, + PRINT_ANY = 4, +}; + +void new_json_obj(int json); +void delete_json_obj(void); +void new_json_obj_plain(int json); +void delete_json_obj_plain(void); + +bool is_json_context(void); + +void open_json_object(const char *str); +void close_json_object(void); +void open_json_array(enum output_type type, const char *delim); +void close_json_array(enum output_type type, const char *delim); + +void print_nl(void); + +#define _PRINT_FUNC(type_name, type) \ + int print_color_##type_name(enum output_type t, \ + enum color_attr color, \ + const char *key, \ + const char *fmt, \ + type value); \ + \ + static inline int print_##type_name(enum output_type t, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + return print_color_##type_name(t, COLOR_NONE, key, fmt, \ + value); \ + } + +/* These functions return 0 if printing to a JSON context, number of + * characters printed otherwise (as calculated by printf(3)). + */ +_PRINT_FUNC(int, int) +_PRINT_FUNC(s64, int64_t) +_PRINT_FUNC(bool, bool) +_PRINT_FUNC(null, const char*) +_PRINT_FUNC(string, const char*) +_PRINT_FUNC(uint, unsigned int) +_PRINT_FUNC(u64, uint64_t) +_PRINT_FUNC(hhu, unsigned char) +_PRINT_FUNC(hu, unsigned short) +_PRINT_FUNC(hex, unsigned int) +_PRINT_FUNC(0xhex, unsigned long long) +_PRINT_FUNC(luint, unsigned long) +_PRINT_FUNC(lluint, unsigned long long) +_PRINT_FUNC(float, double) +#undef _PRINT_FUNC + +#define _PRINT_NAME_VALUE_FUNC(type_name, type, format_char) \ + void print_##type_name##_name_value(const char *name, type value) \ + +_PRINT_NAME_VALUE_FUNC(uint, unsigned int, u); +_PRINT_NAME_VALUE_FUNC(string, const char*, s); +#undef _PRINT_NAME_VALUE_FUNC + +#endif /* _JSON_PRINT_H_ */ diff --git a/include/json_writer.h b/include/json_writer.h new file mode 100644 index 0000000..b52dc2d --- /dev/null +++ b/include/json_writer.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/* + * Simple streaming JSON writer + * + * This takes care of the annoying bits of JSON syntax like the commas + * after elements + * + * Authors: Stephen Hemminger + */ + +#ifndef _JSON_WRITER_H_ +#define _JSON_WRITER_H_ + +#include +#include + +/* Opaque class structure */ +typedef struct json_writer json_writer_t; + +/* Create a new JSON stream */ +json_writer_t *jsonw_new(FILE *f); +/* End output to JSON stream */ +void jsonw_destroy(json_writer_t **self_p); + +/* Cause output to have pretty whitespace */ +void jsonw_pretty(json_writer_t *self, bool on); + +/* Add property name */ +void jsonw_name(json_writer_t *self, const char *name); + +/* Add value */ +__attribute__((format(printf, 2, 3))) +void jsonw_printf(json_writer_t *self, const char *fmt, ...); +void jsonw_string(json_writer_t *self, const char *value); +void jsonw_bool(json_writer_t *self, bool value); +void jsonw_float(json_writer_t *self, double number); +void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num); +void jsonw_uint(json_writer_t *self, unsigned int number); +void jsonw_u64(json_writer_t *self, uint64_t number); +void jsonw_xint(json_writer_t *self, uint64_t number); +void jsonw_hhu(json_writer_t *self, unsigned char num); +void jsonw_hu(json_writer_t *self, unsigned short number); +void jsonw_int(json_writer_t *self, int number); +void jsonw_s64(json_writer_t *self, int64_t number); +void jsonw_null(json_writer_t *self); +void jsonw_luint(json_writer_t *self, unsigned long num); +void jsonw_lluint(json_writer_t *self, unsigned long long num); + +/* Useful Combinations of name and value */ +void jsonw_string_field(json_writer_t *self, const char *prop, const char *val); +void jsonw_bool_field(json_writer_t *self, const char *prop, bool value); +void jsonw_float_field(json_writer_t *self, const char *prop, double num); +void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num); +void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num); +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num); +void jsonw_int_field(json_writer_t *self, const char *prop, int num); +void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num); +void jsonw_null_field(json_writer_t *self, const char *prop); +void jsonw_luint_field(json_writer_t *self, const char *prop, + unsigned long num); +void jsonw_lluint_field(json_writer_t *self, const char *prop, + unsigned long long num); + +/* Collections */ +void jsonw_start_object(json_writer_t *self); +void jsonw_end_object(json_writer_t *self); + +void jsonw_start_array(json_writer_t *self); +void jsonw_end_array(json_writer_t *self); + +/* Override default exception handling */ +typedef void (jsonw_err_handler_fn)(const char *); + +#endif /* _JSON_WRITER_H_ */ diff --git a/include/libgenl.h b/include/libgenl.h new file mode 100644 index 0000000..656493a --- /dev/null +++ b/include/libgenl.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBGENL_H__ +#define __LIBGENL_H__ + +#include "libnetlink.h" + +#define GENL_REQUEST(_req, _bufsiz, _family, _hdrsiz, _ver, _cmd, _flags) \ +struct { \ + struct nlmsghdr n; \ + struct genlmsghdr g; \ + char buf[NLMSG_ALIGN(_hdrsiz) + (_bufsiz)]; \ +} _req = { \ + .n = { \ + .nlmsg_type = (_family), \ + .nlmsg_flags = (_flags), \ + .nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN + (_hdrsiz)), \ + }, \ + .g = { \ + .cmd = (_cmd), \ + .version = (_ver), \ + }, \ +} + +int genl_resolve_family(struct rtnl_handle *grth, const char *family); +int genl_init_handle(struct rtnl_handle *grth, const char *family, + int *genl_family); + +#endif /* __LIBGENL_H__ */ diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h new file mode 100644 index 0000000..3d2a2a3 --- /dev/null +++ b/include/libiptc/ipt_kernel_headers.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* This is the userspace/kernel interface for Generic IP Chains, + required for libc6. */ +#ifndef _FWCHAINS_KERNEL_HEADERS_H +#define _FWCHAINS_KERNEL_HEADERS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#endif diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h new file mode 100644 index 0000000..cd588de --- /dev/null +++ b/include/libiptc/libip6tc.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIBIP6TC_H +#define _LIBIP6TC_H +/* Library which manipulates firewall rules. Version 0.2. */ + +#include +#include +#ifdef __cplusplus +# include +#else +# include /* INT_MAX in ip6_tables.h */ +#endif +#include +#include + +#define ip6tc_handle xtc_handle +#define ip6t_chainlabel xt_chainlabel + +#define IP6TC_LABEL_ACCEPT "ACCEPT" +#define IP6TC_LABEL_DROP "DROP" +#define IP6TC_LABEL_QUEUE "QUEUE" +#define IP6TC_LABEL_RETURN "RETURN" + +/* Does this chain exist? */ +int ip6tc_is_chain(const char *chain, struct xtc_handle *const handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +struct xtc_handle *ip6tc_init(const char *tablename); + +/* Cleanup after ip6tc_init(). */ +void ip6tc_free(struct xtc_handle *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *ip6tc_first_chain(struct xtc_handle *handle); +const char *ip6tc_next_chain(struct xtc_handle *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ip6t_entry *ip6tc_first_rule(const char *chain, + struct xtc_handle *handle); + +/* Returns NULL when rules run out. */ +const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev, + struct xtc_handle *handle); + +/* Returns a pointer to the target name of this position. */ +const char *ip6tc_get_target(const struct ip6t_entry *e, + struct xtc_handle *handle); + +/* Is this a built-in chain? */ +int ip6tc_builtin(const char *chain, struct xtc_handle *const handle); + +/* Get the policy of a given built-in chain */ +const char *ip6tc_get_policy(const char *chain, + struct xt_counters *counters, + struct xtc_handle *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `fw' in chain `chain' into position `rulenum'. */ +int ip6tc_insert_entry(const xt_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Atomically replace rule `rulenum' in `chain' with `fw'. */ +int ip6tc_replace_entry(const xt_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Append entry `fw' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int ip6tc_append_entry(const xt_chainlabel chain, + const struct ip6t_entry *e, + struct xtc_handle *handle); + +/* Check whether a matching rule exists */ +int ip6tc_check_entry(const xt_chainlabel chain, + const struct ip6t_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); + +/* Delete the first rule in `chain' which matches `fw'. */ +int ip6tc_delete_entry(const xt_chainlabel chain, + const struct ip6t_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int ip6tc_delete_num_entry(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Check the packet `fw' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *ip6tc_check_packet(const xt_chainlabel chain, + struct ip6t_entry *, + struct xtc_handle *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int ip6tc_flush_entries(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Zeroes the counters in a chain. */ +int ip6tc_zero_entries(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Creates a new chain. */ +int ip6tc_create_chain(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Deletes a chain. */ +int ip6tc_delete_chain(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Renames a chain. */ +int ip6tc_rename_chain(const xt_chainlabel oldname, + const xt_chainlabel newname, + struct xtc_handle *handle); + +/* Sets the policy on a built-in chain. */ +int ip6tc_set_policy(const xt_chainlabel chain, + const xt_chainlabel policy, + struct xt_counters *counters, + struct xtc_handle *handle); + +/* Get the number of references to this chain */ +int ip6tc_get_references(unsigned int *ref, const xt_chainlabel chain, + struct xtc_handle *handle); + +/* read packet and byte counters for a specific rule */ +struct xt_counters *ip6tc_read_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* zero packet and byte counters for a specific rule */ +int ip6tc_zero_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* set packet and byte counters for a specific rule */ +int ip6tc_set_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xt_counters *counters, + struct xtc_handle *handle); + +/* Makes the actual changes. */ +int ip6tc_commit(struct xtc_handle *handle); + +/* Get raw socket. */ +int ip6tc_get_raw_socket(void); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *ip6tc_strerror(int err); + +extern void dump_entries6(struct xtc_handle *const); + +extern const struct xtc_ops ip6tc_ops; + +#endif /* _LIBIP6TC_H */ diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h new file mode 100644 index 0000000..1bfe4e1 --- /dev/null +++ b/include/libiptc/libiptc.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIBIPTC_H +#define _LIBIPTC_H +/* Library which manipulates filtering rules. */ + +#include +#include +#ifdef __cplusplus +# include +#else +# include /* INT_MAX in ip_tables.h */ +#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define iptc_handle xtc_handle +#define ipt_chainlabel xt_chainlabel + +#define IPTC_LABEL_ACCEPT "ACCEPT" +#define IPTC_LABEL_DROP "DROP" +#define IPTC_LABEL_QUEUE "QUEUE" +#define IPTC_LABEL_RETURN "RETURN" + +/* Does this chain exist? */ +int iptc_is_chain(const char *chain, struct xtc_handle *const handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +struct xtc_handle *iptc_init(const char *tablename); + +/* Cleanup after iptc_init(). */ +void iptc_free(struct xtc_handle *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *iptc_first_chain(struct xtc_handle *handle); +const char *iptc_next_chain(struct xtc_handle *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ipt_entry *iptc_first_rule(const char *chain, + struct xtc_handle *handle); + +/* Returns NULL when rules run out. */ +const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, + struct xtc_handle *handle); + +/* Returns a pointer to the target name of this entry. */ +const char *iptc_get_target(const struct ipt_entry *e, + struct xtc_handle *handle); + +/* Is this a built-in chain? */ +int iptc_builtin(const char *chain, struct xtc_handle *const handle); + +/* Get the policy of a given built-in chain */ +const char *iptc_get_policy(const char *chain, + struct xt_counters *counter, + struct xtc_handle *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `e' in chain `chain' into position `rulenum'. */ +int iptc_insert_entry(const xt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Atomically replace rule `rulenum' in `chain' with `e'. */ +int iptc_replace_entry(const xt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Append entry `e' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int iptc_append_entry(const xt_chainlabel chain, + const struct ipt_entry *e, + struct xtc_handle *handle); + +/* Check whether a mathching rule exists */ +int iptc_check_entry(const xt_chainlabel chain, + const struct ipt_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); + +/* Delete the first rule in `chain' which matches `e', subject to + matchmask (array of length == origfw) */ +int iptc_delete_entry(const xt_chainlabel chain, + const struct ipt_entry *origfw, + unsigned char *matchmask, + struct xtc_handle *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int iptc_delete_num_entry(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* Check the packet `e' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *iptc_check_packet(const xt_chainlabel chain, + struct ipt_entry *entry, + struct xtc_handle *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int iptc_flush_entries(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Zeroes the counters in a chain. */ +int iptc_zero_entries(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Creates a new chain. */ +int iptc_create_chain(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Deletes a chain. */ +int iptc_delete_chain(const xt_chainlabel chain, + struct xtc_handle *handle); + +/* Renames a chain. */ +int iptc_rename_chain(const xt_chainlabel oldname, + const xt_chainlabel newname, + struct xtc_handle *handle); + +/* Sets the policy on a built-in chain. */ +int iptc_set_policy(const xt_chainlabel chain, + const xt_chainlabel policy, + struct xt_counters *counters, + struct xtc_handle *handle); + +/* Get the number of references to this chain */ +int iptc_get_references(unsigned int *ref, + const xt_chainlabel chain, + struct xtc_handle *handle); + +/* read packet and byte counters for a specific rule */ +struct xt_counters *iptc_read_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* zero packet and byte counters for a specific rule */ +int iptc_zero_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xtc_handle *handle); + +/* set packet and byte counters for a specific rule */ +int iptc_set_counter(const xt_chainlabel chain, + unsigned int rulenum, + struct xt_counters *counters, + struct xtc_handle *handle); + +/* Makes the actual changes. */ +int iptc_commit(struct xtc_handle *handle); + +/* Get raw socket. */ +int iptc_get_raw_socket(void); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *iptc_strerror(int err); + +extern void dump_entries(struct xtc_handle *const); + +extern const struct xtc_ops iptc_ops; + +#ifdef __cplusplus +} +#endif + + +#endif /* _LIBIPTC_H */ diff --git a/include/libiptc/libxtc.h b/include/libiptc/libxtc.h new file mode 100644 index 0000000..1e9596a --- /dev/null +++ b/include/libiptc/libxtc.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIBXTC_H +#define _LIBXTC_H +/* Library which manipulates filtering rules. */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XT_MIN_ALIGN +/* xt_entry has pointers and u_int64_t's in it, so if you align to + it, you'll also align to any crazy matches and targets someone + might write */ +#define XT_MIN_ALIGN (__alignof__(struct xt_entry)) +#endif + +#ifndef XT_ALIGN +#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1)) +#endif + +#define XTC_LABEL_ACCEPT "ACCEPT" +#define XTC_LABEL_DROP "DROP" +#define XTC_LABEL_QUEUE "QUEUE" +#define XTC_LABEL_RETURN "RETURN" + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBXTC_H */ diff --git a/include/libiptc/xtcshared.h b/include/libiptc/xtcshared.h new file mode 100644 index 0000000..278a58f --- /dev/null +++ b/include/libiptc/xtcshared.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIBXTC_SHARED_H +#define _LIBXTC_SHARED_H 1 + +typedef char xt_chainlabel[32]; +struct xtc_handle; +struct xt_counters; + +struct xtc_ops { + int (*commit)(struct xtc_handle *); + void (*free)(struct xtc_handle *); + int (*builtin)(const char *, struct xtc_handle *const); + int (*is_chain)(const char *, struct xtc_handle *const); + int (*flush_entries)(const xt_chainlabel, struct xtc_handle *); + int (*create_chain)(const xt_chainlabel, struct xtc_handle *); + int (*set_policy)(const xt_chainlabel, const xt_chainlabel, + struct xt_counters *, struct xtc_handle *); + const char *(*strerror)(int); +}; + +#endif /* _LIBXTC_SHARED_H */ diff --git a/include/libnetlink.h b/include/libnetlink.h new file mode 100644 index 0000000..e27516f --- /dev/null +++ b/include/libnetlink.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rtnl_handle { + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; + int proto; + FILE *dump_fp; +#define RTNL_HANDLE_F_LISTEN_ALL_NSID 0x01 +#define RTNL_HANDLE_F_SUPPRESS_NLERR 0x02 +#define RTNL_HANDLE_F_STRICT_CHK 0x04 + int flags; +}; + +struct nlmsg_list { + struct nlmsg_list *next; + struct nlmsghdr h; +}; + +struct nlmsg_chain { + struct nlmsg_list *head; + struct nlmsg_list *tail; +}; + +extern int rcvbuf; + +int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) + __attribute__((warn_unused_result)); + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, + int protocol) + __attribute__((warn_unused_result)); +int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group) + __attribute__((warn_unused_result)); +void rtnl_close(struct rtnl_handle *rth); +void rtnl_set_strict_dump(struct rtnl_handle *rth); + +typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen); + +int rtnl_addrdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); +int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family) + __attribute__((warn_unused_result)); +int rtnl_routedump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); +int rtnl_ruledump_req(struct rtnl_handle *rth, int family) + __attribute__((warn_unused_result)); +int rtnl_neighdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); +int rtnl_neightbldump_req(struct rtnl_handle *rth, int family) + __attribute__((warn_unused_result)); +int rtnl_mdbdump_req(struct rtnl_handle *rth, int family) + __attribute__((warn_unused_result)); +int rtnl_netconfdump_req(struct rtnl_handle *rth, int family) + __attribute__((warn_unused_result)); + +int rtnl_linkdump_req(struct rtnl_handle *rth, int fam) + __attribute__((warn_unused_result)); +int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) + __attribute__((warn_unused_result)); + +int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int fam, + req_filter_fn_t fn) + __attribute__((warn_unused_result)); +int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); +int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); +int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) + __attribute__((warn_unused_result)); +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, + int len) + __attribute__((warn_unused_result)); +int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) + __attribute__((warn_unused_result)); + +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); + +struct rtnl_ctrl_data { + int nsid; +}; + +typedef int (*rtnl_filter_t)(struct nlmsghdr *n, void *); + +typedef int (*rtnl_listen_filter_t)(struct rtnl_ctrl_data *, + struct nlmsghdr *n, void *); + +typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off, + const struct nlmsghdr *inner_nlh); + +struct rtnl_dump_filter_arg { + rtnl_filter_t filter; + void *arg1; + __u16 nc_flags; +}; + +int rtnl_dump_filter_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg, __u16 nc_flags); +#define rtnl_dump_filter(rth, filter, arg) \ + rtnl_dump_filter_nc(rth, filter, arg, 0) +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, + struct nlmsghdr **answer) + __attribute__((warn_unused_result)); +int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen, + struct nlmsghdr **answer) + __attribute__((warn_unused_result)); +int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n, + struct nlmsghdr **answer) + __attribute__((warn_unused_result)); +int rtnl_send(struct rtnl_handle *rth, const void *buf, int) + __attribute__((warn_unused_result)); +int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int) + __attribute__((warn_unused_result)); +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn); +int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error); + +int addattr(struct nlmsghdr *n, int maxlen, int type); +int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data); +int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data); +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data); +int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *data); + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, + const void *data, int alen); +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type); +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest); +struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, + const void *data, int len); +int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest); +int rta_addattr8(struct rtattr *rta, int maxlen, int type, __u8 data); +int rta_addattr16(struct rtattr *rta, int maxlen, int type, __u16 data); +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +int rta_addattr64(struct rtattr *rta, int maxlen, int type, __u64 data); +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen); + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags); +struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len); +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type); +int rta_nest_end(struct rtattr *rta, struct rtattr *nest); + +#define RTA_TAIL(rta) \ + ((struct rtattr *) (((void *) (rta)) + \ + RTA_ALIGN((rta)->rta_len))) + +#define parse_rtattr_nested(tb, max, rta) \ + (parse_rtattr_flags((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta), \ + NLA_F_NESTED)) + +#define parse_rtattr_one_nested(type, rta) \ + (parse_rtattr_one(type, RTA_DATA(rta), RTA_PAYLOAD(rta))) + +#define parse_rtattr_nested_compat(tb, max, rta, data, len) \ + ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ + __parse_rtattr_nested_compat(tb, max, rta, len); }) + +static inline __u8 rta_getattr_u8(const struct rtattr *rta) +{ + return *(__u8 *)RTA_DATA(rta); +} +static inline __u16 rta_getattr_u16(const struct rtattr *rta) +{ + return *(__u16 *)RTA_DATA(rta); +} +static inline __be16 rta_getattr_be16(const struct rtattr *rta) +{ + return ntohs(rta_getattr_u16(rta)); +} +static inline __u32 rta_getattr_u32(const struct rtattr *rta) +{ + return *(__u32 *)RTA_DATA(rta); +} +static inline __be32 rta_getattr_be32(const struct rtattr *rta) +{ + return ntohl(rta_getattr_u32(rta)); +} +static inline __u64 rta_getattr_u64(const struct rtattr *rta) +{ + __u64 tmp; + + memcpy(&tmp, RTA_DATA(rta), sizeof(__u64)); + return tmp; +} +static inline __s32 rta_getattr_s32(const struct rtattr *rta) +{ + return *(__s32 *)RTA_DATA(rta); +} +static inline __s64 rta_getattr_s64(const struct rtattr *rta) +{ + __s64 tmp; + + memcpy(&tmp, RTA_DATA(rta), sizeof(tmp)); + return tmp; +} +static inline const char *rta_getattr_str(const struct rtattr *rta) +{ + return (const char *)RTA_DATA(rta); +} + +int rtnl_listen_all_nsid(struct rtnl_handle *); +int rtnl_listen(struct rtnl_handle *, rtnl_listen_filter_t handler, + void *jarg); +int rtnl_from_file(FILE *, rtnl_listen_filter_t handler, + void *jarg); + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +#ifndef IFA_RTA +#define IFA_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#endif +#ifndef IFA_PAYLOAD +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)) +#endif + +#ifndef IFLA_RTA +#define IFLA_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#endif +#ifndef IFLA_PAYLOAD +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)) +#endif + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif +#ifndef NDA_PAYLOAD +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ndmsg)) +#endif + +#ifndef NDTA_RTA +#define NDTA_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg)))) +#endif +#ifndef NDTA_PAYLOAD +#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ndtmsg)) +#endif + +#ifndef NETNS_RTA +#define NETNS_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) +#endif +#ifndef NETNS_PAYLOAD +#define NETNS_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct rtgenmsg)) +#endif + +#ifndef IFLA_STATS_RTA +#define IFLA_STATS_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg)))) +#endif + +/* User defined nlmsg_type which is used mostly for logging netlink + * messages from dump file */ +#define NLMSG_TSTAMP 15 + +#endif /* __LIBNETLINK_H__ */ diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..5d86b13 --- /dev/null +++ b/include/list.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIST_H__ +#define __LIST_H__ 1 +/* List and hash list stuff from kernel */ + +#include + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct list_head { + struct list_head *next, *prev; +}; + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +static inline void hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos ; pos = pos->next) + + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +#define hlist_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ + }) + +#define hlist_for_each_entry(pos, head, member) \ + for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ + pos; \ + pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) + +#endif /* __LIST_H__ */ diff --git a/include/ll_map.h b/include/ll_map.h new file mode 100644 index 0000000..4de1041 --- /dev/null +++ b/include/ll_map.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LL_MAP_H__ +#define __LL_MAP_H__ 1 + +int ll_remember_index(struct nlmsghdr *n, void *arg); + +void ll_init_map(struct rtnl_handle *rth); +unsigned ll_name_to_index(const char *name); +const char *ll_index_to_name(unsigned idx); +int ll_index_to_type(unsigned idx); +int ll_index_to_flags(unsigned idx); +void ll_drop_by_index(unsigned index); +unsigned namehash(const char *str); + +const char *ll_idx_n2a(unsigned int idx); + +#endif /* __LL_MAP_H__ */ diff --git a/include/names.h b/include/names.h new file mode 100644 index 0000000..2fcaacc --- /dev/null +++ b/include/names.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DB_NAMES_H_ +#define DB_NAMES_H_ 1 + +#define IDNAME_MAX 256 + +struct db_entry { + struct db_entry *next; + unsigned int id; + char *name; +}; + +struct db_names { + unsigned int size; + struct db_entry *cached; + struct db_entry **hash; + int max; +}; + +struct db_names *db_names_alloc(void); +int db_names_load(struct db_names *db, const char *path); +void db_names_free(struct db_names *db); + +char *id_to_name(struct db_names *db, int id, char *name); + +#endif diff --git a/include/namespace.h b/include/namespace.h new file mode 100644 index 0000000..e47f9b5 --- /dev/null +++ b/include/namespace.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __NAMESPACE_H__ +#define __NAMESPACE_H__ 1 + +#include +#include +#include +#include +#include + +#ifndef NETNS_RUN_DIR +#define NETNS_RUN_DIR "/var/run/netns" +#endif + +#ifndef NETNS_ETC_DIR +#define NETNS_ETC_DIR "/etc/netns" +#endif + +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 0x00000002 /* Just detach from the tree */ +#endif /* MNT_DETACH */ + +/* sys/mount.h may be out too old to have these */ +#ifndef MS_REC +#define MS_REC 16384 +#endif + +#ifndef MS_SLAVE +#define MS_SLAVE (1 << 19) +#endif + +#ifndef MS_SHARED +#define MS_SHARED (1 << 20) +#endif + +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif /* HAVE_SETNS */ + +int netns_switch(char *netns); +int netns_get_fd(const char *netns); +int netns_foreach(int (*func)(char *nsname, void *arg), void *arg); + +struct netns_func { + int (*func)(char *nsname, void *arg); + void *arg; +}; + +#endif /* __NAMESPACE_H__ */ diff --git a/include/netinet/tcp.h b/include/netinet/tcp.h new file mode 100644 index 0000000..3f890a1 --- /dev/null +++ b/include/netinet/tcp.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tcp.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET_TCP_H +#define _NETINET_TCP_H 1 + +#include + +/* + * User-settable options (used with setsockopt). + */ +#define TCP_NODELAY 1 /* Don't delay send to coalesce packets */ +#define TCP_MAXSEG 2 /* Set maximum segment size */ +#define TCP_CORK 3 /* Control sending of partial frames */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Bock/reenable quick ACKs. */ +#define TCP_CONGESTION 13 /* Congestion control algorithm. */ + +#ifdef __USE_MISC +# include + +# ifdef __FAVOR_BSD +typedef u_int32_t tcp_seq; +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +struct tcphdr + { + u_int16_t th_sport; /* source port */ + u_int16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +# if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t th_x2:4; /* (unused) */ + u_int8_t th_off:4; /* data offset */ +# endif +# if __BYTE_ORDER == __BIG_ENDIAN + u_int8_t th_off:4; /* data offset */ + u_int8_t th_x2:4; /* (unused) */ +# endif + u_int8_t th_flags; +# define TH_FIN 0x01 +# define TH_SYN 0x02 +# define TH_RST 0x04 +# define TH_PUSH 0x08 +# define TH_ACK 0x10 +# define TH_URG 0x20 + u_int16_t th_win; /* window */ + u_int16_t th_sum; /* checksum */ + u_int16_t th_urp; /* urgent pointer */ +}; + +# else /* !__FAVOR_BSD */ +struct tcphdr + { + u_int16_t source; + u_int16_t dest; + u_int32_t seq; + u_int32_t ack_seq; +# if __BYTE_ORDER == __LITTLE_ENDIAN + u_int16_t res1:4; + u_int16_t doff:4; + u_int16_t fin:1; + u_int16_t syn:1; + u_int16_t rst:1; + u_int16_t psh:1; + u_int16_t ack:1; + u_int16_t urg:1; + u_int16_t res2:2; +# elif __BYTE_ORDER == __BIG_ENDIAN + u_int16_t doff:4; + u_int16_t res1:4; + u_int16_t res2:2; + u_int16_t urg:1; + u_int16_t ack:1; + u_int16_t psh:1; + u_int16_t rst:1; + u_int16_t syn:1; + u_int16_t fin:1; +# else +# error "Adjust your defines" +# endif + u_int16_t window; + u_int16_t check; + u_int16_t urg_ptr; +}; +# endif /* __FAVOR_BSD */ + +enum +{ + TCP_ESTABLISHED = 1, + TCP_SYN_SENT, + TCP_SYN_RECV, + TCP_FIN_WAIT1, + TCP_FIN_WAIT2, + TCP_TIME_WAIT, + TCP_CLOSE, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, + TCP_LISTEN, + TCP_CLOSING /* now a valid state */ +}; + +# define TCPOPT_EOL 0 +# define TCPOPT_NOP 1 +# define TCPOPT_MAXSEG 2 +# define TCPOLEN_MAXSEG 4 +# define TCPOPT_WINDOW 3 +# define TCPOLEN_WINDOW 3 +# define TCPOPT_SACK_PERMITTED 4 /* Experimental */ +# define TCPOLEN_SACK_PERMITTED 2 +# define TCPOPT_SACK 5 /* Experimental */ +# define TCPOPT_TIMESTAMP 8 +# define TCPOLEN_TIMESTAMP 10 +# define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ + +# define TCPOPT_TSTAMP_HDR \ + (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP) + +/* + * Default maximum segment size for TCP. + * With an IP MSS of 576, this is 536, + * but 512 is probably more convenient. + * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)). + */ +# define TCP_MSS 512 + +# define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ + +# define TCP_MAX_WINSHIFT 14 /* maximum window shift */ + +# define SOL_TCP 6 /* TCP level */ + + +# define TCPI_OPT_TIMESTAMPS 1 +# define TCPI_OPT_SACK 2 +# define TCPI_OPT_WSCALE 4 +# define TCPI_OPT_ECN 8 +# define TCPI_OPT_ECN_SEEN 16 + +/* Values for tcpi_state. */ +enum tcp_ca_state +{ + TCP_CA_Open = 0, + TCP_CA_Disorder = 1, + TCP_CA_CWR = 2, + TCP_CA_Recovery = 3, + TCP_CA_Loss = 4 +}; + +struct tcp_info +{ + u_int8_t tcpi_state; + u_int8_t tcpi_ca_state; + u_int8_t tcpi_retransmits; + u_int8_t tcpi_probes; + u_int8_t tcpi_backoff; + u_int8_t tcpi_options; + u_int8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + + u_int32_t tcpi_rto; + u_int32_t tcpi_ato; + u_int32_t tcpi_snd_mss; + u_int32_t tcpi_rcv_mss; + + u_int32_t tcpi_unacked; + u_int32_t tcpi_sacked; + u_int32_t tcpi_lost; + u_int32_t tcpi_retrans; + u_int32_t tcpi_fackets; + + /* Times. */ + u_int32_t tcpi_last_data_sent; + u_int32_t tcpi_last_ack_sent; /* Not remembered, sorry. */ + u_int32_t tcpi_last_data_recv; + u_int32_t tcpi_last_ack_recv; + + /* Metrics. */ + u_int32_t tcpi_pmtu; + u_int32_t tcpi_rcv_ssthresh; + u_int32_t tcpi_rtt; + u_int32_t tcpi_rttvar; + u_int32_t tcpi_snd_ssthresh; + u_int32_t tcpi_snd_cwnd; + u_int32_t tcpi_advmss; + u_int32_t tcpi_reordering; + u_int32_t tcpi_rcv_rtt; + u_int32_t tcpi_rcv_space; + u_int32_t tcpi_total_retrans; + +}; + +#endif /* Misc. */ + +#endif /* netinet/tcp.h */ diff --git a/include/rt_names.h b/include/rt_names.h new file mode 100644 index 0000000..7afce17 --- /dev/null +++ b/include/rt_names.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include + +const char *rtnl_rtprot_n2a(int id, char *buf, int len); +const char *rtnl_rtscope_n2a(int id, char *buf, int len); +const char *rtnl_rttable_n2a(__u32 id, char *buf, int len); +const char *rtnl_rtrealm_n2a(int id, char *buf, int len); +const char *rtnl_dsfield_n2a(int id, char *buf, int len); +const char *rtnl_group_n2a(int id, char *buf, int len); + +int rtnl_rtprot_a2n(__u32 *id, const char *arg); +int rtnl_rtscope_a2n(__u32 *id, const char *arg); +int rtnl_rttable_a2n(__u32 *id, const char *arg); +int rtnl_rtrealm_a2n(__u32 *id, const char *arg); +int rtnl_dsfield_a2n(__u32 *id, const char *arg); +int rtnl_group_a2n(int *id, const char *arg); + +const char *inet_proto_n2a(int proto, char *buf, int len); +int inet_proto_a2n(const char *buf); + + +const char * ll_type_n2a(int type, char *buf, int len); +const char *ll_addr_n2a(const unsigned char *addr, int alen, + int type, char *buf, int blen); +int ll_addr_a2n(char *lladdr, int len, const char *arg); + +const char * ll_proto_n2a(unsigned short id, char *buf, int len); +int ll_proto_a2n(unsigned short *id, const char *buf); + +const char *nl_proto_n2a(int id, char *buf, int len); +int nl_proto_a2n(__u32 *id, const char *arg); + +extern int numeric; + +#endif diff --git a/include/rtm_map.h b/include/rtm_map.h new file mode 100644 index 0000000..f85e52c --- /dev/null +++ b/include/rtm_map.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RTM_MAP_H__ +#define __RTM_MAP_H__ 1 + +char *rtnl_rtntype_n2a(int id, char *buf, int len); +int rtnl_rtntype_a2n(int *id, char *arg); + +int get_rt_realms_or_raw(__u32 *realms, char *arg); + +#endif /* __RTM_MAP_H__ */ diff --git a/include/uapi/asm-generic/sockios.h b/include/uapi/asm-generic/sockios.h new file mode 100644 index 0000000..44fa3ed --- /dev/null +++ b/include/uapi/asm-generic/sockios.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_SOCKIOS_H +#define __ASM_GENERIC_SOCKIOS_H + +/* Socket-level I/O control calls. */ +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP_OLD 0x8906 /* Get stamp (timeval) */ +#define SIOCGSTAMPNS_OLD 0x8907 /* Get stamp (timespec) */ + +#endif /* __ASM_GENERIC_SOCKIOS_H */ diff --git a/include/uapi/linux/atm.h b/include/uapi/linux/atm.h new file mode 100644 index 0000000..e33ff6b --- /dev/null +++ b/include/uapi/linux/atm.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atm.h - general ATM declarations */ + +/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ + + +/* + * WARNING: User-space programs should not #include directly. + * Instead, #include + */ + +#ifndef _LINUX_ATM_H +#define _LINUX_ATM_H + +/* + * BEGIN_xx and END_xx markers are used for automatic generation of + * documentation. Do not change them. + */ + + +#include +#include +#include +#include + + +/* general ATM constants */ +#define ATM_CELL_SIZE 53 /* ATM cell size incl. header */ +#define ATM_CELL_PAYLOAD 48 /* ATM payload size */ +#define ATM_AAL0_SDU 52 /* AAL0 SDU size */ +#define ATM_MAX_AAL34_PDU 65535 /* maximum AAL3/4 PDU payload */ +#define ATM_AAL5_TRAILER 8 /* AAL5 trailer size */ +#define ATM_MAX_AAL5_PDU 65535 /* maximum AAL5 PDU payload */ +#define ATM_MAX_CDV 9999 /* maximum (default) CDV */ +#define ATM_NOT_RSV_VCI 32 /* first non-reserved VCI value */ + +#define ATM_MAX_VPI 255 /* maximum VPI at the UNI */ +#define ATM_MAX_VPI_NNI 4096 /* maximum VPI at the NNI */ +#define ATM_MAX_VCI 65535 /* maximum VCI */ + + +/* "protcol" values for the socket system call */ +#define ATM_NO_AAL 0 /* AAL not specified */ +#define ATM_AAL0 13 /* "raw" ATM cells */ +#define ATM_AAL1 1 /* AAL1 (CBR) */ +#define ATM_AAL2 2 /* AAL2 (VBR) */ +#define ATM_AAL34 3 /* AAL3/4 (data) */ +#define ATM_AAL5 5 /* AAL5 (data) */ + +/* + * socket option name coding functions + * + * Note that __SO_ENCODE and __SO_LEVEL are somewhat a hack since the + * << 22 only reserves 9 bits for the level. On some architectures + * SOL_SOCKET is 0xFFFF, so that's a bit of a problem + */ + +#define __SO_ENCODE(l,n,t) ((((l) & 0x1FF) << 22) | ((n) << 16) | \ + sizeof(t)) +#define __SO_LEVEL_MATCH(c,m) (((c) >> 22) == ((m) & 0x1FF)) +#define __SO_NUMBER(c) (((c) >> 16) & 0x3f) +#define __SO_SIZE(c) ((c) & 0x3fff) + +/* + * ATM layer + */ + +#define SO_SETCLP __SO_ENCODE(SOL_ATM,0,int) + /* set CLP bit value - TODO */ +#define SO_CIRANGE __SO_ENCODE(SOL_ATM,1,struct atm_cirange) + /* connection identifier range; socket must be + bound or connected */ +#define SO_ATMQOS __SO_ENCODE(SOL_ATM,2,struct atm_qos) + /* Quality of Service setting */ +#define SO_ATMSAP __SO_ENCODE(SOL_ATM,3,struct atm_sap) + /* Service Access Point */ +#define SO_ATMPVC __SO_ENCODE(SOL_ATM,4,struct sockaddr_atmpvc) + /* "PVC" address (also for SVCs); get only */ +#define SO_MULTIPOINT __SO_ENCODE(SOL_ATM, 5, int) + /* make this vc a p2mp */ + + +/* + * Note @@@: since the socket layers don't really distinguish the control and + * the data plane but generally seems to be data plane-centric, any layer is + * about equally wrong for the SAP. If you have a better idea about this, + * please speak up ... + */ + + +/* ATM cell header (for AAL0) */ + +/* BEGIN_CH */ +#define ATM_HDR_GFC_MASK 0xf0000000 +#define ATM_HDR_GFC_SHIFT 28 +#define ATM_HDR_VPI_MASK 0x0ff00000 +#define ATM_HDR_VPI_SHIFT 20 +#define ATM_HDR_VCI_MASK 0x000ffff0 +#define ATM_HDR_VCI_SHIFT 4 +#define ATM_HDR_PTI_MASK 0x0000000e +#define ATM_HDR_PTI_SHIFT 1 +#define ATM_HDR_CLP 0x00000001 +/* END_CH */ + + +/* PTI codings */ + +/* BEGIN_PTI */ +#define ATM_PTI_US0 0 /* user data cell, congestion not exp, SDU-type 0 */ +#define ATM_PTI_US1 1 /* user data cell, congestion not exp, SDU-type 1 */ +#define ATM_PTI_UCES0 2 /* user data cell, cong. experienced, SDU-type 0 */ +#define ATM_PTI_UCES1 3 /* user data cell, cong. experienced, SDU-type 1 */ +#define ATM_PTI_SEGF5 4 /* segment OAM F5 flow related cell */ +#define ATM_PTI_E2EF5 5 /* end-to-end OAM F5 flow related cell */ +#define ATM_PTI_RSV_RM 6 /* reserved for traffic control/resource mgmt */ +#define ATM_PTI_RSV 7 /* reserved */ +/* END_PTI */ + + +/* + * The following items should stay in linux/atm.h, which should be linked to + * netatm/atm.h + */ + +/* Traffic description */ + +#define ATM_NONE 0 /* no traffic */ +#define ATM_UBR 1 +#define ATM_CBR 2 +#define ATM_VBR 3 +#define ATM_ABR 4 +#define ATM_ANYCLASS 5 /* compatible with everything */ + +#define ATM_MAX_PCR -1 /* maximum available PCR */ + +struct atm_trafprm { + unsigned char traffic_class; /* traffic class (ATM_UBR, ...) */ + int max_pcr; /* maximum PCR in cells per second */ + int pcr; /* desired PCR in cells per second */ + int min_pcr; /* minimum PCR in cells per second */ + int max_cdv; /* maximum CDV in microseconds */ + int max_sdu; /* maximum SDU in bytes */ + /* extra params for ABR */ + unsigned int icr; /* Initial Cell Rate (24-bit) */ + unsigned int tbe; /* Transient Buffer Exposure (24-bit) */ + unsigned int frtt : 24; /* Fixed Round Trip Time (24-bit) */ + unsigned int rif : 4; /* Rate Increment Factor (4-bit) */ + unsigned int rdf : 4; /* Rate Decrease Factor (4-bit) */ + unsigned int nrm_pres :1; /* nrm present bit */ + unsigned int trm_pres :1; /* rm present bit */ + unsigned int adtf_pres :1; /* adtf present bit */ + unsigned int cdf_pres :1; /* cdf present bit*/ + unsigned int nrm :3; /* Max # of Cells for each forward RM cell (3-bit) */ + unsigned int trm :3; /* Time between forward RM cells (3-bit) */ + unsigned int adtf :10; /* ACR Decrease Time Factor (10-bit) */ + unsigned int cdf :3; /* Cutoff Decrease Factor (3-bit) */ + unsigned int spare :9; /* spare bits */ +}; + +struct atm_qos { + struct atm_trafprm txtp; /* parameters in TX direction */ + struct atm_trafprm rxtp __ATM_API_ALIGN; + /* parameters in RX direction */ + unsigned char aal __ATM_API_ALIGN; +}; + +/* PVC addressing */ + +#define ATM_ITF_ANY -1 /* "magic" PVC address values */ +#define ATM_VPI_ANY -1 +#define ATM_VCI_ANY -1 +#define ATM_VPI_UNSPEC -2 +#define ATM_VCI_UNSPEC -2 + + +struct sockaddr_atmpvc { + unsigned short sap_family; /* address family, AF_ATMPVC */ + struct { /* PVC address */ + short itf; /* ATM interface */ + short vpi; /* VPI (only 8 bits at UNI) */ + int vci; /* VCI (only 16 bits at UNI) */ + } sap_addr __ATM_API_ALIGN; /* PVC address */ +}; + +/* SVC addressing */ + +#define ATM_ESA_LEN 20 /* ATM End System Address length */ +#define ATM_E164_LEN 12 /* maximum E.164 number length */ + +#define ATM_AFI_DCC 0x39 /* DCC ATM Format */ +#define ATM_AFI_ICD 0x47 /* ICD ATM Format */ +#define ATM_AFI_E164 0x45 /* E.164 ATM Format */ +#define ATM_AFI_LOCAL 0x49 /* Local ATM Format */ + +#define ATM_AFI_DCC_GROUP 0xBD /* DCC ATM Group Format */ +#define ATM_AFI_ICD_GROUP 0xC5 /* ICD ATM Group Format */ +#define ATM_AFI_E164_GROUP 0xC3 /* E.164 ATM Group Format */ +#define ATM_AFI_LOCAL_GROUP 0xC7 /* Local ATM Group Format */ + +#define ATM_LIJ_NONE 0 /* no leaf-initiated join */ +#define ATM_LIJ 1 /* request joining */ +#define ATM_LIJ_RPJ 2 /* set to root-prompted join */ +#define ATM_LIJ_NJ 3 /* set to network join */ + + +struct sockaddr_atmsvc { + unsigned short sas_family; /* address family, AF_ATMSVC */ + struct { /* SVC address */ + unsigned char prv[ATM_ESA_LEN];/* private ATM address */ + char pub[ATM_E164_LEN+1]; /* public address (E.164) */ + /* unused addresses must be bzero'ed */ + char lij_type; /* role in LIJ call; one of ATM_LIJ* */ + __u32 lij_id; /* LIJ call identifier */ + } sas_addr __ATM_API_ALIGN; /* SVC address */ +}; + + +static __inline__ int atmsvc_addr_in_use(struct sockaddr_atmsvc addr) +{ + return *addr.sas_addr.prv || *addr.sas_addr.pub; +} + + +static __inline__ int atmpvc_addr_in_use(struct sockaddr_atmpvc addr) +{ + return addr.sap_addr.itf || addr.sap_addr.vpi || addr.sap_addr.vci; +} + + +/* + * Some stuff for linux/sockios.h + */ + +struct atmif_sioc { + int number; + int length; + void *arg; +}; + + +typedef unsigned short atm_backend_t; +#endif /* _LINUX_ATM_H */ diff --git a/include/uapi/linux/atmapi.h b/include/uapi/linux/atmapi.h new file mode 100644 index 0000000..c9bf5c2 --- /dev/null +++ b/include/uapi/linux/atmapi.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmapi.h - ATM API user space/kernel compatibility */ + +/* Written 1999,2000 by Werner Almesberger, EPFL ICA */ + + +#ifndef _LINUX_ATMAPI_H +#define _LINUX_ATMAPI_H + +#if defined(__sparc__) || defined(__ia64__) +/* such alignment is not required on 32 bit sparcs, but we can't + figure that we are on a sparc64 while compiling user-space programs. */ +#define __ATM_API_ALIGN __attribute__((aligned(8))) +#else +#define __ATM_API_ALIGN +#endif + + +/* + * Opaque type for kernel pointers. Note that _ is never accessed. We need + * the struct in order hide the array, so that we can make simple assignments + * instead of being forced to use memcpy. It also improves error reporting for + * code that still assumes that we're passing unsigned longs. + * + * Convention: NULL pointers are passed as a field of all zeroes. + */ + +typedef struct { unsigned char _[8]; } __ATM_API_ALIGN atm_kptr_t; + +#endif diff --git a/include/uapi/linux/atmarp.h b/include/uapi/linux/atmarp.h new file mode 100644 index 0000000..8e44d12 --- /dev/null +++ b/include/uapi/linux/atmarp.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef _LINUX_ATMARP_H +#define _LINUX_ATMARP_H + +#include +#include +#include + + +#define ATMARP_RETRY_DELAY 30 /* request next resolution or forget + NAK after 30 sec - should go into + atmclip.h */ +#define ATMARP_MAX_UNRES_PACKETS 5 /* queue that many packets while + waiting for the resolver */ + + +#define ATMARPD_CTRL _IO('a',ATMIOC_CLIP+1) /* become atmarpd ctrl sock */ +#define ATMARP_MKIP _IO('a',ATMIOC_CLIP+2) /* attach socket to IP */ +#define ATMARP_SETENTRY _IO('a',ATMIOC_CLIP+3) /* fill or hide ARP entry */ +#define ATMARP_ENCAP _IO('a',ATMIOC_CLIP+5) /* change encapsulation */ + + +enum atmarp_ctrl_type { + act_invalid, /* catch uninitialized structures */ + act_need, /* need address resolution */ + act_up, /* interface is coming up */ + act_down, /* interface is going down */ + act_change /* interface configuration has changed */ +}; + +struct atmarp_ctrl { + enum atmarp_ctrl_type type; /* message type */ + int itf_num;/* interface number (if present) */ + __be32 ip; /* IP address (act_need only) */ +}; + +#endif diff --git a/include/uapi/linux/atmdev.h b/include/uapi/linux/atmdev.h new file mode 100644 index 0000000..9bdb96a --- /dev/null +++ b/include/uapi/linux/atmdev.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmdev.h - ATM device driver declarations and various related items */ + +/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef LINUX_ATMDEV_H +#define LINUX_ATMDEV_H + + +#include +#include +#include + + +#define ESI_LEN 6 + +#define ATM_OC3_PCR (155520000/270*260/8/53) + /* OC3 link rate: 155520000 bps + SONET overhead: /270*260 (9 section, 1 path) + bits per cell: /8/53 + max cell rate: 353207.547 cells/sec */ +#define ATM_25_PCR ((25600000/8-8000)/54) + /* 25 Mbps ATM cell rate (59111) */ +#define ATM_OC12_PCR (622080000/1080*1040/8/53) + /* OC12 link rate: 622080000 bps + SONET overhead: /1080*1040 + bits per cell: /8/53 + max cell rate: 1412830.188 cells/sec */ +#define ATM_DS3_PCR (8000*12) + /* DS3: 12 cells in a 125 usec time slot */ + + +#define __AAL_STAT_ITEMS \ + __HANDLE_ITEM(tx); /* TX okay */ \ + __HANDLE_ITEM(tx_err); /* TX errors */ \ + __HANDLE_ITEM(rx); /* RX okay */ \ + __HANDLE_ITEM(rx_err); /* RX errors */ \ + __HANDLE_ITEM(rx_drop); /* RX out of memory */ + +struct atm_aal_stats { +#define __HANDLE_ITEM(i) int i + __AAL_STAT_ITEMS +#undef __HANDLE_ITEM +}; + + +struct atm_dev_stats { + struct atm_aal_stats aal0; + struct atm_aal_stats aal34; + struct atm_aal_stats aal5; +} __ATM_API_ALIGN; + + +#define ATM_GETLINKRATE _IOW('a',ATMIOC_ITF+1,struct atmif_sioc) + /* get link rate */ +#define ATM_GETNAMES _IOW('a',ATMIOC_ITF+3,struct atm_iobuf) + /* get interface names (numbers) */ +#define ATM_GETTYPE _IOW('a',ATMIOC_ITF+4,struct atmif_sioc) + /* get interface type name */ +#define ATM_GETESI _IOW('a',ATMIOC_ITF+5,struct atmif_sioc) + /* get interface ESI */ +#define ATM_GETADDR _IOW('a',ATMIOC_ITF+6,struct atmif_sioc) + /* get itf's local ATM addr. list */ +#define ATM_RSTADDR _IOW('a',ATMIOC_ITF+7,struct atmif_sioc) + /* reset itf's ATM address list */ +#define ATM_ADDADDR _IOW('a',ATMIOC_ITF+8,struct atmif_sioc) + /* add a local ATM address */ +#define ATM_DELADDR _IOW('a',ATMIOC_ITF+9,struct atmif_sioc) + /* remove a local ATM address */ +#define ATM_GETCIRANGE _IOW('a',ATMIOC_ITF+10,struct atmif_sioc) + /* get connection identifier range */ +#define ATM_SETCIRANGE _IOW('a',ATMIOC_ITF+11,struct atmif_sioc) + /* set connection identifier range */ +#define ATM_SETESI _IOW('a',ATMIOC_ITF+12,struct atmif_sioc) + /* set interface ESI */ +#define ATM_SETESIF _IOW('a',ATMIOC_ITF+13,struct atmif_sioc) + /* force interface ESI */ +#define ATM_ADDLECSADDR _IOW('a', ATMIOC_ITF+14, struct atmif_sioc) + /* register a LECS address */ +#define ATM_DELLECSADDR _IOW('a', ATMIOC_ITF+15, struct atmif_sioc) + /* unregister a LECS address */ +#define ATM_GETLECSADDR _IOW('a', ATMIOC_ITF+16, struct atmif_sioc) + /* retrieve LECS address(es) */ + +#define ATM_GETSTAT _IOW('a',ATMIOC_SARCOM+0,struct atmif_sioc) + /* get AAL layer statistics */ +#define ATM_GETSTATZ _IOW('a',ATMIOC_SARCOM+1,struct atmif_sioc) + /* get AAL layer statistics and zero */ +#define ATM_GETLOOP _IOW('a',ATMIOC_SARCOM+2,struct atmif_sioc) + /* get loopback mode */ +#define ATM_SETLOOP _IOW('a',ATMIOC_SARCOM+3,struct atmif_sioc) + /* set loopback mode */ +#define ATM_QUERYLOOP _IOW('a',ATMIOC_SARCOM+4,struct atmif_sioc) + /* query supported loopback modes */ +#define ATM_SETSC _IOW('a',ATMIOC_SPECIAL+1,int) + /* enable or disable single-copy */ +#define ATM_SETBACKEND _IOW('a',ATMIOC_SPECIAL+2,atm_backend_t) + /* set backend handler */ +#define ATM_NEWBACKENDIF _IOW('a',ATMIOC_SPECIAL+3,atm_backend_t) + /* use backend to make new if */ +#define ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf) + /* add party to p2mp call */ +#ifdef CONFIG_COMPAT +/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */ +#define COMPAT_ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct compat_atm_iobuf) +#endif +#define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int) + /* drop party from p2mp call */ + +/* + * These are backend handkers that can be set via the ATM_SETBACKEND call + * above. In the future we may support dynamic loading of these - for now, + * they're just being used to share the ATMIOC_BACKEND ioctls + */ +#define ATM_BACKEND_RAW 0 +#define ATM_BACKEND_PPP 1 /* PPPoATM - RFC2364 */ +#define ATM_BACKEND_BR2684 2 /* Bridged RFC1483/2684 */ + +/* for ATM_GETTYPE */ +#define ATM_ITFTYP_LEN 8 /* maximum length of interface type name */ + +/* + * Loopback modes for ATM_{PHY,SAR}_{GET,SET}LOOP + */ + +/* Point of loopback CPU-->SAR-->PHY-->line--> ... */ +#define __ATM_LM_NONE 0 /* no loop back ^ ^ ^ ^ */ +#define __ATM_LM_AAL 1 /* loop back PDUs --' | | | */ +#define __ATM_LM_ATM 2 /* loop back ATM cells ---' | | */ +/* RESERVED 4 loop back on PHY side ---' */ +#define __ATM_LM_PHY 8 /* loop back bits (digital) ----' | */ +#define __ATM_LM_ANALOG 16 /* loop back the analog signal --------' */ + +/* Direction of loopback */ +#define __ATM_LM_MKLOC(n) ((n)) /* Local (i.e. loop TX to RX) */ +#define __ATM_LM_MKRMT(n) ((n) << 8) /* Remote (i.e. loop RX to TX) */ + +#define __ATM_LM_XTLOC(n) ((n) & 0xff) +#define __ATM_LM_XTRMT(n) (((n) >> 8) & 0xff) + +#define ATM_LM_NONE 0 /* no loopback */ + +#define ATM_LM_LOC_AAL __ATM_LM_MKLOC(__ATM_LM_AAL) +#define ATM_LM_LOC_ATM __ATM_LM_MKLOC(__ATM_LM_ATM) +#define ATM_LM_LOC_PHY __ATM_LM_MKLOC(__ATM_LM_PHY) +#define ATM_LM_LOC_ANALOG __ATM_LM_MKLOC(__ATM_LM_ANALOG) + +#define ATM_LM_RMT_AAL __ATM_LM_MKRMT(__ATM_LM_AAL) +#define ATM_LM_RMT_ATM __ATM_LM_MKRMT(__ATM_LM_ATM) +#define ATM_LM_RMT_PHY __ATM_LM_MKRMT(__ATM_LM_PHY) +#define ATM_LM_RMT_ANALOG __ATM_LM_MKRMT(__ATM_LM_ANALOG) + +/* + * Note: ATM_LM_LOC_* and ATM_LM_RMT_* can be combined, provided that + * __ATM_LM_XTLOC(x) <= __ATM_LM_XTRMT(x) + */ + + +struct atm_iobuf { + int length; + void *buffer; +}; + +/* for ATM_GETCIRANGE / ATM_SETCIRANGE */ + +#define ATM_CI_MAX -1 /* use maximum range of VPI/VCI */ + +struct atm_cirange { + signed char vpi_bits; /* 1..8, ATM_CI_MAX (-1) for maximum */ + signed char vci_bits; /* 1..16, ATM_CI_MAX (-1) for maximum */ +}; + +/* for ATM_SETSC; actually taken from the ATM_VF number space */ + +#define ATM_SC_RX 1024 /* enable RX single-copy */ +#define ATM_SC_TX 2048 /* enable TX single-copy */ + +#define ATM_BACKLOG_DEFAULT 32 /* if we get more, we're likely to time out + anyway */ + +/* MF: change_qos (Modify) flags */ + +#define ATM_MF_IMMED 1 /* Block until change is effective */ +#define ATM_MF_INC_RSV 2 /* Change reservation on increase */ +#define ATM_MF_INC_SHP 4 /* Change shaping on increase */ +#define ATM_MF_DEC_RSV 8 /* Change reservation on decrease */ +#define ATM_MF_DEC_SHP 16 /* Change shaping on decrease */ +#define ATM_MF_BWD 32 /* Set the backward direction parameters */ + +#define ATM_MF_SET (ATM_MF_INC_RSV | ATM_MF_INC_SHP | ATM_MF_DEC_RSV | \ + ATM_MF_DEC_SHP | ATM_MF_BWD) + +/* + * ATM_VS_* are used to express VC state in a human-friendly way. + */ + +#define ATM_VS_IDLE 0 /* VC is not used */ +#define ATM_VS_CONNECTED 1 /* VC is connected */ +#define ATM_VS_CLOSING 2 /* VC is closing */ +#define ATM_VS_LISTEN 3 /* VC is listening for incoming setups */ +#define ATM_VS_INUSE 4 /* VC is in use (registered with atmsigd) */ +#define ATM_VS_BOUND 5 /* VC is bound */ + +#define ATM_VS2TXT_MAP \ + "IDLE", "CONNECTED", "CLOSING", "LISTEN", "INUSE", "BOUND" + +#define ATM_VF2TXT_MAP \ + "ADDR", "READY", "PARTIAL", "REGIS", \ + "RELEASED", "HASQOS", "LISTEN", "META", \ + "256", "512", "1024", "2048", \ + "SESSION", "HASSAP", "BOUND", "CLOSE" + + + +#endif /* LINUX_ATMDEV_H */ diff --git a/include/uapi/linux/atmioc.h b/include/uapi/linux/atmioc.h new file mode 100644 index 0000000..a9030bc --- /dev/null +++ b/include/uapi/linux/atmioc.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmioc.h - ranges for ATM-related ioctl numbers */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +/* + * See https://icawww1.epfl.ch/linux-atm/magic.html for the complete list of + * "magic" ioctl numbers. + */ + + +#ifndef _LINUX_ATMIOC_H +#define _LINUX_ATMIOC_H + +#include + /* everybody including atmioc.h will also need _IO{,R,W,WR} */ + +#define ATMIOC_PHYCOM 0x00 /* PHY device common ioctls, globally unique */ +#define ATMIOC_PHYCOM_END 0x0f +#define ATMIOC_PHYTYP 0x10 /* PHY dev type ioctls, unique per PHY type */ +#define ATMIOC_PHYTYP_END 0x2f +#define ATMIOC_PHYPRV 0x30 /* PHY dev private ioctls, unique per driver */ +#define ATMIOC_PHYPRV_END 0x4f +#define ATMIOC_SARCOM 0x50 /* SAR device common ioctls, globally unique */ +#define ATMIOC_SARCOM_END 0x50 +#define ATMIOC_SARPRV 0x60 /* SAR dev private ioctls, unique per driver */ +#define ATMIOC_SARPRV_END 0x7f +#define ATMIOC_ITF 0x80 /* Interface ioctls, globally unique */ +#define ATMIOC_ITF_END 0x8f +#define ATMIOC_BACKEND 0x90 /* ATM generic backend ioctls, u. per backend */ +#define ATMIOC_BACKEND_END 0xaf +/* 0xb0-0xbf: Reserved for future use */ +#define ATMIOC_AREQUIPA 0xc0 /* Application requested IP over ATM, glob. u. */ +#define ATMIOC_LANE 0xd0 /* LAN Emulation, globally unique */ +#define ATMIOC_MPOA 0xd8 /* MPOA, globally unique */ +#define ATMIOC_CLIP 0xe0 /* Classical IP over ATM control, globally u. */ +#define ATMIOC_CLIP_END 0xef +#define ATMIOC_SPECIAL 0xf0 /* Special-purpose controls, globally unique */ +#define ATMIOC_SPECIAL_END 0xff + +#endif diff --git a/include/uapi/linux/atmsap.h b/include/uapi/linux/atmsap.h new file mode 100644 index 0000000..fc05248 --- /dev/null +++ b/include/uapi/linux/atmsap.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* atmsap.h - ATM Service Access Point addressing definitions */ + +/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */ + + +#ifndef _LINUX_ATMSAP_H +#define _LINUX_ATMSAP_H + +#include + +/* + * BEGIN_xx and END_xx markers are used for automatic generation of + * documentation. Do not change them. + */ + + +/* + * Layer 2 protocol identifiers + */ + +/* BEGIN_L2 */ +#define ATM_L2_NONE 0 /* L2 not specified */ +#define ATM_L2_ISO1745 0x01 /* Basic mode ISO 1745 */ +#define ATM_L2_Q291 0x02 /* ITU-T Q.291 (Rec. I.441) */ +#define ATM_L2_X25_LL 0x06 /* ITU-T X.25, link layer */ +#define ATM_L2_X25_ML 0x07 /* ITU-T X.25, multilink */ +#define ATM_L2_LAPB 0x08 /* Extended LAPB, half-duplex (Rec. T.71) */ +#define ATM_L2_HDLC_ARM 0x09 /* HDLC ARM (ISO/IEC 4335) */ +#define ATM_L2_HDLC_NRM 0x0a /* HDLC NRM (ISO/IEC 4335) */ +#define ATM_L2_HDLC_ABM 0x0b /* HDLC ABM (ISO/IEC 4335) */ +#define ATM_L2_ISO8802 0x0c /* LAN LLC (ISO/IEC 8802/2) */ +#define ATM_L2_X75 0x0d /* ITU-T X.75, SLP */ +#define ATM_L2_Q922 0x0e /* ITU-T Q.922 */ +#define ATM_L2_USER 0x10 /* user-specified */ +#define ATM_L2_ISO7776 0x11 /* ISO 7776 DTE-DTE */ +/* END_L2 */ + + +/* + * Layer 3 protocol identifiers + */ + +/* BEGIN_L3 */ +#define ATM_L3_NONE 0 /* L3 not specified */ +#define ATM_L3_X25 0x06 /* ITU-T X.25, packet layer */ +#define ATM_L3_ISO8208 0x07 /* ISO/IEC 8208 */ +#define ATM_L3_X223 0x08 /* ITU-T X.223 | ISO/IEC 8878 */ +#define ATM_L3_ISO8473 0x09 /* ITU-T X.233 | ISO/IEC 8473 */ +#define ATM_L3_T70 0x0a /* ITU-T T.70 minimum network layer */ +#define ATM_L3_TR9577 0x0b /* ISO/IEC TR 9577 */ +#define ATM_L3_H310 0x0c /* ITU-T Recommendation H.310 */ +#define ATM_L3_H321 0x0d /* ITU-T Recommendation H.321 */ +#define ATM_L3_USER 0x10 /* user-specified */ +/* END_L3 */ + + +/* + * High layer identifiers + */ + +/* BEGIN_HL */ +#define ATM_HL_NONE 0 /* HL not specified */ +#define ATM_HL_ISO 0x01 /* ISO */ +#define ATM_HL_USER 0x02 /* user-specific */ +#define ATM_HL_HLP 0x03 /* high layer profile - UNI 3.0 only */ +#define ATM_HL_VENDOR 0x04 /* vendor-specific application identifier */ +/* END_HL */ + + +/* + * ITU-T coded mode of operation + */ + +/* BEGIN_IMD */ +#define ATM_IMD_NONE 0 /* mode not specified */ +#define ATM_IMD_NORMAL 1 /* normal mode of operation */ +#define ATM_IMD_EXTENDED 2 /* extended mode of operation */ +/* END_IMD */ + +/* + * H.310 code points + */ + +#define ATM_TT_NONE 0 /* terminal type not specified */ +#define ATM_TT_RX 1 /* receive only */ +#define ATM_TT_TX 2 /* send only */ +#define ATM_TT_RXTX 3 /* receive and send */ + +#define ATM_MC_NONE 0 /* no multiplexing */ +#define ATM_MC_TS 1 /* transport stream (TS) */ +#define ATM_MC_TS_FEC 2 /* transport stream with forward error corr. */ +#define ATM_MC_PS 3 /* program stream (PS) */ +#define ATM_MC_PS_FEC 4 /* program stream with forward error corr. */ +#define ATM_MC_H221 5 /* ITU-T Rec. H.221 */ + +/* + * SAP structures + */ + +#define ATM_MAX_HLI 8 /* maximum high-layer information length */ + + +struct atm_blli { + unsigned char l2_proto; /* layer 2 protocol */ + union { + struct { + unsigned char mode; /* mode of operation (ATM_IMD_xxx), 0 if */ + /* absent */ + unsigned char window; /* window size (k), 1-127 (0 to omit) */ + } itu; /* ITU-T encoding */ + unsigned char user; /* user-specified l2 information */ + } l2; + unsigned char l3_proto; /* layer 3 protocol */ + union { + struct { + unsigned char mode; /* mode of operation (ATM_IMD_xxx), 0 if */ + /* absent */ + unsigned char def_size; /* default packet size (log2), 4-12 (0 to */ + /* omit) */ + unsigned char window;/* packet window size, 1-127 (0 to omit) */ + } itu; /* ITU-T encoding */ + unsigned char user; /* user specified l3 information */ + struct { /* if l3_proto = ATM_L3_H310 */ + unsigned char term_type; /* terminal type */ + unsigned char fw_mpx_cap; /* forward multiplexing capability */ + /* only if term_type != ATM_TT_NONE */ + unsigned char bw_mpx_cap; /* backward multiplexing capability */ + /* only if term_type != ATM_TT_NONE */ + } h310; + struct { /* if l3_proto = ATM_L3_TR9577 */ + unsigned char ipi; /* initial protocol id */ + unsigned char snap[5];/* IEEE 802.1 SNAP identifier */ + /* (only if ipi == NLPID_IEEE802_1_SNAP) */ + } tr9577; + } l3; +} __ATM_API_ALIGN; + + +struct atm_bhli { + unsigned char hl_type; /* high layer information type */ + unsigned char hl_length; /* length (only if hl_type == ATM_HL_USER || */ + /* hl_type == ATM_HL_ISO) */ + unsigned char hl_info[ATM_MAX_HLI];/* high layer information */ +}; + + +#define ATM_MAX_BLLI 3 /* maximum number of BLLI elements */ + + +struct atm_sap { + struct atm_bhli bhli; /* local SAP, high-layer information */ + struct atm_blli blli[ATM_MAX_BLLI] __ATM_API_ALIGN; + /* local SAP, low-layer info */ +}; + + +static __inline__ int blli_in_use(struct atm_blli blli) +{ + return blli.l2_proto || blli.l3_proto; +} + +#endif diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h new file mode 100644 index 0000000..b21cc6a --- /dev/null +++ b/include/uapi/linux/bpf.h @@ -0,0 +1,4450 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef __LINUX_BPF_H__ +#define __LINUX_BPF_H__ + +#include +#include + +/* Extended instruction set based on top of classic BPF */ + +/* instruction classes */ +#define BPF_JMP32 0x06 /* jmp mode in word width */ +#define BPF_ALU64 0x07 /* alu mode in double word width */ + +/* ld/ldx fields */ +#define BPF_DW 0x18 /* double word (64-bit) */ +#define BPF_XADD 0xc0 /* exclusive add */ + +/* alu/jmp fields */ +#define BPF_MOV 0xb0 /* mov reg to reg */ +#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ + +/* change endianness of a register */ +#define BPF_END 0xd0 /* flags for endianness conversion: */ +#define BPF_TO_LE 0x00 /* convert to little-endian */ +#define BPF_TO_BE 0x08 /* convert to big-endian */ +#define BPF_FROM_LE BPF_TO_LE +#define BPF_FROM_BE BPF_TO_BE + +/* jmp encodings */ +#define BPF_JNE 0x50 /* jump != */ +#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ +#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ +#define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ +#define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ +#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ +#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ +#define BPF_CALL 0x80 /* function call */ +#define BPF_EXIT 0x90 /* function return */ + +/* Register numbers */ +enum { + BPF_REG_0 = 0, + BPF_REG_1, + BPF_REG_2, + BPF_REG_3, + BPF_REG_4, + BPF_REG_5, + BPF_REG_6, + BPF_REG_7, + BPF_REG_8, + BPF_REG_9, + BPF_REG_10, + __MAX_BPF_REG, +}; + +/* BPF has 10 general purpose 64-bit registers and stack frame. */ +#define MAX_BPF_REG __MAX_BPF_REG + +struct bpf_insn { + __u8 code; /* opcode */ + __u8 dst_reg:4; /* dest register */ + __u8 src_reg:4; /* source register */ + __s16 off; /* signed offset */ + __s32 imm; /* signed immediate constant */ +}; + +/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ +struct bpf_lpm_trie_key { + __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ + __u8 data[0]; /* Arbitrary size */ +}; + +struct bpf_cgroup_storage_key { + __u64 cgroup_inode_id; /* cgroup inode id */ + __u32 attach_type; /* program attach type */ +}; + +union bpf_iter_link_info { + struct { + __u32 map_fd; + } map; +}; + +/* BPF syscall commands, see bpf(2) man-page for details. */ +enum bpf_cmd { + BPF_MAP_CREATE, + BPF_MAP_LOOKUP_ELEM, + BPF_MAP_UPDATE_ELEM, + BPF_MAP_DELETE_ELEM, + BPF_MAP_GET_NEXT_KEY, + BPF_PROG_LOAD, + BPF_OBJ_PIN, + BPF_OBJ_GET, + BPF_PROG_ATTACH, + BPF_PROG_DETACH, + BPF_PROG_TEST_RUN, + BPF_PROG_GET_NEXT_ID, + BPF_MAP_GET_NEXT_ID, + BPF_PROG_GET_FD_BY_ID, + BPF_MAP_GET_FD_BY_ID, + BPF_OBJ_GET_INFO_BY_FD, + BPF_PROG_QUERY, + BPF_RAW_TRACEPOINT_OPEN, + BPF_BTF_LOAD, + BPF_BTF_GET_FD_BY_ID, + BPF_TASK_FD_QUERY, + BPF_MAP_LOOKUP_AND_DELETE_ELEM, + BPF_MAP_FREEZE, + BPF_BTF_GET_NEXT_ID, + BPF_MAP_LOOKUP_BATCH, + BPF_MAP_LOOKUP_AND_DELETE_BATCH, + BPF_MAP_UPDATE_BATCH, + BPF_MAP_DELETE_BATCH, + BPF_LINK_CREATE, + BPF_LINK_UPDATE, + BPF_LINK_GET_FD_BY_ID, + BPF_LINK_GET_NEXT_ID, + BPF_ENABLE_STATS, + BPF_ITER_CREATE, + BPF_LINK_DETACH, +}; + +enum bpf_map_type { + BPF_MAP_TYPE_UNSPEC, + BPF_MAP_TYPE_HASH, + BPF_MAP_TYPE_ARRAY, + BPF_MAP_TYPE_PROG_ARRAY, + BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BPF_MAP_TYPE_PERCPU_HASH, + BPF_MAP_TYPE_PERCPU_ARRAY, + BPF_MAP_TYPE_STACK_TRACE, + BPF_MAP_TYPE_CGROUP_ARRAY, + BPF_MAP_TYPE_LRU_HASH, + BPF_MAP_TYPE_LRU_PERCPU_HASH, + BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, + BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, + BPF_MAP_TYPE_XSKMAP, + BPF_MAP_TYPE_SOCKHASH, + BPF_MAP_TYPE_CGROUP_STORAGE, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, + BPF_MAP_TYPE_QUEUE, + BPF_MAP_TYPE_STACK, + BPF_MAP_TYPE_SK_STORAGE, + BPF_MAP_TYPE_DEVMAP_HASH, + BPF_MAP_TYPE_STRUCT_OPS, + BPF_MAP_TYPE_RINGBUF, +}; + +/* Note that tracing related programs such as + * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT} + * are not subject to a stable API since kernel internal data + * structures can change from release to release and may + * therefore break existing tracing BPF programs. Tracing BPF + * programs correspond to /a/ specific kernel which is to be + * analyzed, and not /a/ specific kernel /and/ all future ones. + */ +enum bpf_prog_type { + BPF_PROG_TYPE_UNSPEC, + BPF_PROG_TYPE_SOCKET_FILTER, + BPF_PROG_TYPE_KPROBE, + BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_TRACEPOINT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_PERF_EVENT, + BPF_PROG_TYPE_CGROUP_SKB, + BPF_PROG_TYPE_CGROUP_SOCK, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, + BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, + BPF_PROG_TYPE_RAW_TRACEPOINT, + BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + BPF_PROG_TYPE_LWT_SEG6LOCAL, + BPF_PROG_TYPE_LIRC_MODE2, + BPF_PROG_TYPE_SK_REUSEPORT, + BPF_PROG_TYPE_FLOW_DISSECTOR, + BPF_PROG_TYPE_CGROUP_SYSCTL, + BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, + BPF_PROG_TYPE_CGROUP_SOCKOPT, + BPF_PROG_TYPE_TRACING, + BPF_PROG_TYPE_STRUCT_OPS, + BPF_PROG_TYPE_EXT, + BPF_PROG_TYPE_LSM, + BPF_PROG_TYPE_SK_LOOKUP, +}; + +enum bpf_attach_type { + BPF_CGROUP_INET_INGRESS, + BPF_CGROUP_INET_EGRESS, + BPF_CGROUP_INET_SOCK_CREATE, + BPF_CGROUP_SOCK_OPS, + BPF_SK_SKB_STREAM_PARSER, + BPF_SK_SKB_STREAM_VERDICT, + BPF_CGROUP_DEVICE, + BPF_SK_MSG_VERDICT, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET4_POST_BIND, + BPF_CGROUP_INET6_POST_BIND, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + BPF_LIRC_MODE2, + BPF_FLOW_DISSECTOR, + BPF_CGROUP_SYSCTL, + BPF_CGROUP_UDP4_RECVMSG, + BPF_CGROUP_UDP6_RECVMSG, + BPF_CGROUP_GETSOCKOPT, + BPF_CGROUP_SETSOCKOPT, + BPF_TRACE_RAW_TP, + BPF_TRACE_FENTRY, + BPF_TRACE_FEXIT, + BPF_MODIFY_RETURN, + BPF_LSM_MAC, + BPF_TRACE_ITER, + BPF_CGROUP_INET4_GETPEERNAME, + BPF_CGROUP_INET6_GETPEERNAME, + BPF_CGROUP_INET4_GETSOCKNAME, + BPF_CGROUP_INET6_GETSOCKNAME, + BPF_XDP_DEVMAP, + BPF_CGROUP_INET_SOCK_RELEASE, + BPF_XDP_CPUMAP, + BPF_SK_LOOKUP, + BPF_XDP, + __MAX_BPF_ATTACH_TYPE +}; + +#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE + +enum bpf_link_type { + BPF_LINK_TYPE_UNSPEC = 0, + BPF_LINK_TYPE_RAW_TRACEPOINT = 1, + BPF_LINK_TYPE_TRACING = 2, + BPF_LINK_TYPE_CGROUP = 3, + BPF_LINK_TYPE_ITER = 4, + BPF_LINK_TYPE_NETNS = 5, + BPF_LINK_TYPE_XDP = 6, + + MAX_BPF_LINK_TYPE, +}; + +/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command + * + * NONE(default): No further bpf programs allowed in the subtree. + * + * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, + * the program in this cgroup yields to sub-cgroup program. + * + * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, + * that cgroup program gets run in addition to the program in this cgroup. + * + * Only one program is allowed to be attached to a cgroup with + * NONE or BPF_F_ALLOW_OVERRIDE flag. + * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will + * release old program and attach the new one. Attach flags has to match. + * + * Multiple programs are allowed to be attached to a cgroup with + * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order + * (those that were attached first, run first) + * The programs of sub-cgroup are executed first, then programs of + * this cgroup and then programs of parent cgroup. + * When children program makes decision (like picking TCP CA or sock bind) + * parent program has a chance to override it. + * + * With BPF_F_ALLOW_MULTI a new program is added to the end of the list of + * programs for a cgroup. Though it's possible to replace an old program at + * any position by also specifying BPF_F_REPLACE flag and position itself in + * replace_bpf_fd attribute. Old program at this position will be released. + * + * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. + * A cgroup with NONE doesn't allow any programs in sub-cgroups. + * Ex1: + * cgrp1 (MULTI progs A, B) -> + * cgrp2 (OVERRIDE prog C) -> + * cgrp3 (MULTI prog D) -> + * cgrp4 (OVERRIDE prog E) -> + * cgrp5 (NONE prog F) + * the event in cgrp5 triggers execution of F,D,A,B in that order. + * if prog F is detached, the execution is E,D,A,B + * if prog F and D are detached, the execution is E,A,B + * if prog F, E and D are detached, the execution is C,A,B + * + * All eligible programs are executed regardless of return code from + * earlier programs. + */ +#define BPF_F_ALLOW_OVERRIDE (1U << 0) +#define BPF_F_ALLOW_MULTI (1U << 1) +#define BPF_F_REPLACE (1U << 2) + +/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the + * verifier will perform strict alignment checking as if the kernel + * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set, + * and NET_IP_ALIGN defined to 2. + */ +#define BPF_F_STRICT_ALIGNMENT (1U << 0) + +/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the + * verifier will allow any alignment whatsoever. On platforms + * with strict alignment requirements for loads ands stores (such + * as sparc and mips) the verifier validates that all loads and + * stores provably follow this requirement. This flag turns that + * checking and enforcement off. + * + * It is mostly used for testing when we want to validate the + * context and memory access aspects of the verifier, but because + * of an unaligned access the alignment check would trigger before + * the one we are interested in. + */ +#define BPF_F_ANY_ALIGNMENT (1U << 1) + +/* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose. + * Verifier does sub-register def/use analysis and identifies instructions whose + * def only matters for low 32-bit, high 32-bit is never referenced later + * through implicit zero extension. Therefore verifier notifies JIT back-ends + * that it is safe to ignore clearing high 32-bit for these instructions. This + * saves some back-ends a lot of code-gen. However such optimization is not + * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends + * hence hasn't used verifier's analysis result. But, we really want to have a + * way to be able to verify the correctness of the described optimization on + * x86_64 on which testsuites are frequently exercised. + * + * So, this flag is introduced. Once it is set, verifier will randomize high + * 32-bit for those instructions who has been identified as safe to ignore them. + * Then, if verifier is not doing correct analysis, such randomization will + * regress tests to expose bugs. + */ +#define BPF_F_TEST_RND_HI32 (1U << 2) + +/* The verifier internal test flag. Behavior is undefined */ +#define BPF_F_TEST_STATE_FREQ (1U << 3) + +/* When BPF ldimm64's insn[0].src_reg != 0 then this can have + * two extensions: + * + * insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE + * insn[0].imm: map fd map fd + * insn[1].imm: 0 offset into value + * insn[0].off: 0 0 + * insn[1].off: 0 0 + * ldimm64 rewrite: address of map address of map[0]+offset + * verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE + */ +#define BPF_PSEUDO_MAP_FD 1 +#define BPF_PSEUDO_MAP_VALUE 2 + +/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative + * offset to another bpf function + */ +#define BPF_PSEUDO_CALL 1 + +/* flags for BPF_MAP_UPDATE_ELEM command */ +enum { + BPF_ANY = 0, /* create new element or update existing */ + BPF_NOEXIST = 1, /* create new element if it didn't exist */ + BPF_EXIST = 2, /* update existing element */ + BPF_F_LOCK = 4, /* spin_lock-ed map_lookup/map_update */ +}; + +/* flags for BPF_MAP_CREATE command */ +enum { + BPF_F_NO_PREALLOC = (1U << 0), +/* Instead of having one common LRU list in the + * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list + * which can scale and perform better. + * Note, the LRU nodes (including free nodes) cannot be moved + * across different LRU lists. + */ + BPF_F_NO_COMMON_LRU = (1U << 1), +/* Specify numa node during map creation */ + BPF_F_NUMA_NODE = (1U << 2), + +/* Flags for accessing BPF object from syscall side. */ + BPF_F_RDONLY = (1U << 3), + BPF_F_WRONLY = (1U << 4), + +/* Flag for stack_map, store build_id+offset instead of pointer */ + BPF_F_STACK_BUILD_ID = (1U << 5), + +/* Zero-initialize hash function seed. This should only be used for testing. */ + BPF_F_ZERO_SEED = (1U << 6), + +/* Flags for accessing BPF object from program side. */ + BPF_F_RDONLY_PROG = (1U << 7), + BPF_F_WRONLY_PROG = (1U << 8), + +/* Clone map from listener for newly accepted socket */ + BPF_F_CLONE = (1U << 9), + +/* Enable memory-mapping BPF map */ + BPF_F_MMAPABLE = (1U << 10), +}; + +/* Flags for BPF_PROG_QUERY. */ + +/* Query effective (directly attached + inherited from ancestor cgroups) + * programs that will be executed for events within a cgroup. + * attach_flags with this flag are returned only for directly attached programs. + */ +#define BPF_F_QUERY_EFFECTIVE (1U << 0) + +/* type for BPF_ENABLE_STATS */ +enum bpf_stats_type { + /* enabled run_time_ns and run_cnt */ + BPF_STATS_RUN_TIME = 0, +}; + +enum bpf_stack_build_id_status { + /* user space need an empty entry to identify end of a trace */ + BPF_STACK_BUILD_ID_EMPTY = 0, + /* with valid build_id and offset */ + BPF_STACK_BUILD_ID_VALID = 1, + /* couldn't get build_id, fallback to ip */ + BPF_STACK_BUILD_ID_IP = 2, +}; + +#define BPF_BUILD_ID_SIZE 20 +struct bpf_stack_build_id { + __s32 status; + unsigned char build_id[BPF_BUILD_ID_SIZE]; + union { + __u64 offset; + __u64 ip; + }; +}; + +#define BPF_OBJ_NAME_LEN 16U + +union bpf_attr { + struct { /* anonymous struct used by BPF_MAP_CREATE command */ + __u32 map_type; /* one of enum bpf_map_type */ + __u32 key_size; /* size of key in bytes */ + __u32 value_size; /* size of value in bytes */ + __u32 max_entries; /* max number of entries in a map */ + __u32 map_flags; /* BPF_MAP_CREATE related + * flags defined above. + */ + __u32 inner_map_fd; /* fd pointing to the inner map */ + __u32 numa_node; /* numa node (effective only if + * BPF_F_NUMA_NODE is set). + */ + char map_name[BPF_OBJ_NAME_LEN]; + __u32 map_ifindex; /* ifindex of netdev to create on */ + __u32 btf_fd; /* fd pointing to a BTF type data */ + __u32 btf_key_type_id; /* BTF type_id of the key */ + __u32 btf_value_type_id; /* BTF type_id of the value */ + __u32 btf_vmlinux_value_type_id;/* BTF type_id of a kernel- + * struct stored as the + * map value + */ + }; + + struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ + __u32 map_fd; + __aligned_u64 key; + union { + __aligned_u64 value; + __aligned_u64 next_key; + }; + __u64 flags; + }; + + struct { /* struct used by BPF_MAP_*_BATCH commands */ + __aligned_u64 in_batch; /* start batch, + * NULL to start from beginning + */ + __aligned_u64 out_batch; /* output: next start batch */ + __aligned_u64 keys; + __aligned_u64 values; + __u32 count; /* input/output: + * input: # of key/value + * elements + * output: # of filled elements + */ + __u32 map_fd; + __u64 elem_flags; + __u64 flags; + } batch; + + struct { /* anonymous struct used by BPF_PROG_LOAD command */ + __u32 prog_type; /* one of enum bpf_prog_type */ + __u32 insn_cnt; + __aligned_u64 insns; + __aligned_u64 license; + __u32 log_level; /* verbosity level of verifier */ + __u32 log_size; /* size of user buffer */ + __aligned_u64 log_buf; /* user supplied buffer */ + __u32 kern_version; /* not used */ + __u32 prog_flags; + char prog_name[BPF_OBJ_NAME_LEN]; + __u32 prog_ifindex; /* ifindex of netdev to prep for */ + /* For some prog types expected attach type must be known at + * load time to verify attach type specific parts of prog + * (context accesses, allowed helpers, etc). + */ + __u32 expected_attach_type; + __u32 prog_btf_fd; /* fd pointing to BTF type data */ + __u32 func_info_rec_size; /* userspace bpf_func_info size */ + __aligned_u64 func_info; /* func info */ + __u32 func_info_cnt; /* number of bpf_func_info records */ + __u32 line_info_rec_size; /* userspace bpf_line_info size */ + __aligned_u64 line_info; /* line info */ + __u32 line_info_cnt; /* number of bpf_line_info records */ + __u32 attach_btf_id; /* in-kernel BTF type id to attach to */ + __u32 attach_prog_fd; /* 0 to attach to vmlinux */ + }; + + struct { /* anonymous struct used by BPF_OBJ_* commands */ + __aligned_u64 pathname; + __u32 bpf_fd; + __u32 file_flags; + }; + + struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ + __u32 target_fd; /* container object to attach to */ + __u32 attach_bpf_fd; /* eBPF program to attach */ + __u32 attach_type; + __u32 attach_flags; + __u32 replace_bpf_fd; /* previously attached eBPF + * program to replace if + * BPF_F_REPLACE is used + */ + }; + + struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ + __u32 prog_fd; + __u32 retval; + __u32 data_size_in; /* input: len of data_in */ + __u32 data_size_out; /* input/output: len of data_out + * returns ENOSPC if data_out + * is too small. + */ + __aligned_u64 data_in; + __aligned_u64 data_out; + __u32 repeat; + __u32 duration; + __u32 ctx_size_in; /* input: len of ctx_in */ + __u32 ctx_size_out; /* input/output: len of ctx_out + * returns ENOSPC if ctx_out + * is too small. + */ + __aligned_u64 ctx_in; + __aligned_u64 ctx_out; + } test; + + struct { /* anonymous struct used by BPF_*_GET_*_ID */ + union { + __u32 start_id; + __u32 prog_id; + __u32 map_id; + __u32 btf_id; + __u32 link_id; + }; + __u32 next_id; + __u32 open_flags; + }; + + struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ + __u32 bpf_fd; + __u32 info_len; + __aligned_u64 info; + } info; + + struct { /* anonymous struct used by BPF_PROG_QUERY command */ + __u32 target_fd; /* container object to query */ + __u32 attach_type; + __u32 query_flags; + __u32 attach_flags; + __aligned_u64 prog_ids; + __u32 prog_cnt; + } query; + + struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */ + __u64 name; + __u32 prog_fd; + } raw_tracepoint; + + struct { /* anonymous struct for BPF_BTF_LOAD */ + __aligned_u64 btf; + __aligned_u64 btf_log_buf; + __u32 btf_size; + __u32 btf_log_size; + __u32 btf_log_level; + }; + + struct { + __u32 pid; /* input: pid */ + __u32 fd; /* input: fd */ + __u32 flags; /* input: flags */ + __u32 buf_len; /* input/output: buf len */ + __aligned_u64 buf; /* input/output: + * tp_name for tracepoint + * symbol for kprobe + * filename for uprobe + */ + __u32 prog_id; /* output: prod_id */ + __u32 fd_type; /* output: BPF_FD_TYPE_* */ + __u64 probe_offset; /* output: probe_offset */ + __u64 probe_addr; /* output: probe_addr */ + } task_fd_query; + + struct { /* struct used by BPF_LINK_CREATE command */ + __u32 prog_fd; /* eBPF program to attach */ + union { + __u32 target_fd; /* object to attach to */ + __u32 target_ifindex; /* target ifindex */ + }; + __u32 attach_type; /* attach type */ + __u32 flags; /* extra flags */ + __aligned_u64 iter_info; /* extra bpf_iter_link_info */ + __u32 iter_info_len; /* iter_info length */ + } link_create; + + struct { /* struct used by BPF_LINK_UPDATE command */ + __u32 link_fd; /* link fd */ + /* new program fd to update link with */ + __u32 new_prog_fd; + __u32 flags; /* extra flags */ + /* expected link's program fd; is specified only if + * BPF_F_REPLACE flag is set in flags */ + __u32 old_prog_fd; + } link_update; + + struct { + __u32 link_fd; + } link_detach; + + struct { /* struct used by BPF_ENABLE_STATS command */ + __u32 type; + } enable_stats; + + struct { /* struct used by BPF_ITER_CREATE command */ + __u32 link_fd; + __u32 flags; + } iter_create; + +} __attribute__((aligned(8))); + +/* The description below is an attempt at providing documentation to eBPF + * developers about the multiple available eBPF helper functions. It can be + * parsed and used to produce a manual page. The workflow is the following, + * and requires the rst2man utility: + * + * $ ./scripts/bpf_helpers_doc.py \ + * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst + * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 + * $ man /tmp/bpf-helpers.7 + * + * Note that in order to produce this external documentation, some RST + * formatting is used in the descriptions to get "bold" and "italics" in + * manual pages. Also note that the few trailing white spaces are + * intentional, removing them would break paragraphs for rst2man. + * + * Start of BPF helper function descriptions: + * + * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key) + * Description + * Perform a lookup in *map* for an entry associated to *key*. + * Return + * Map value associated to *key*, or **NULL** if no entry was + * found. + * + * long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags) + * Description + * Add or update the value of the entry associated to *key* in + * *map* with *value*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * Flag value **BPF_NOEXIST** cannot be used for maps of types + * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all + * elements always exist), the helper would return an error. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_map_delete_elem(struct bpf_map *map, const void *key) + * Description + * Delete entry with *key* from *map*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_probe_read(void *dst, u32 size, const void *unsafe_ptr) + * Description + * For tracing programs, safely attempt to read *size* bytes from + * kernel space address *unsafe_ptr* and store the data in *dst*. + * + * Generally, use **bpf_probe_read_user**\ () or + * **bpf_probe_read_kernel**\ () instead. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_ktime_get_ns(void) + * Description + * Return the time elapsed since system boot, in nanoseconds. + * Does not include time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) + * Return + * Current *ktime*. + * + * long bpf_trace_printk(const char *fmt, u32 fmt_size, ...) + * Description + * This helper is a "printk()-like" facility for debugging. It + * prints a message defined by format *fmt* (of size *fmt_size*) + * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if + * available. It can take up to three additional **u64** + * arguments (as an eBPF helpers, the total number of arguments is + * limited to five). + * + * Each time the helper is called, it appends a line to the trace. + * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is + * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. + * The format of the trace is customizable, and the exact output + * one will get depends on the options set in + * *\/sys/kernel/debug/tracing/trace_options* (see also the + * *README* file under the same directory). However, it usually + * defaults to something like: + * + * :: + * + * telnet-470 [001] .N.. 419421.045894: 0x00000001: + * + * In the above: + * + * * ``telnet`` is the name of the current task. + * * ``470`` is the PID of the current task. + * * ``001`` is the CPU number on which the task is + * running. + * * In ``.N..``, each character refers to a set of + * options (whether irqs are enabled, scheduling + * options, whether hard/softirqs are running, level of + * preempt_disabled respectively). **N** means that + * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** + * are set. + * * ``419421.045894`` is a timestamp. + * * ``0x00000001`` is a fake value used by BPF for the + * instruction pointer register. + * * ```` is the message formatted with + * *fmt*. + * + * The conversion specifiers supported by *fmt* are similar, but + * more limited than for printk(). They are **%d**, **%i**, + * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, + * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size + * of field, padding with zeroes, etc.) is available, and the + * helper will return **-EINVAL** (but print nothing) if it + * encounters an unknown specifier. + * + * Also, note that **bpf_trace_printk**\ () is slow, and should + * only be used for debugging purposes. For this reason, a notice + * block (spanning several lines) is printed to kernel logs and + * states that the helper should not be used "for production use" + * the first time this helper is used (or more precisely, when + * **trace_printk**\ () buffers are allocated). For passing values + * to user space, perf events should be preferred. + * Return + * The number of bytes written to the buffer, or a negative error + * in case of failure. + * + * u32 bpf_get_prandom_u32(void) + * Description + * Get a pseudo-random number. + * + * From a security point of view, this helper uses its own + * pseudo-random internal state, and cannot be used to infer the + * seed of other random functions in the kernel. However, it is + * essential to note that the generator used by the helper is not + * cryptographically secure. + * Return + * A random 32-bit unsigned value. + * + * u32 bpf_get_smp_processor_id(void) + * Description + * Get the SMP (symmetric multiprocessing) processor id. Note that + * all programs run with preemption disabled, which means that the + * SMP processor id is stable during all the execution of the + * program. + * Return + * The SMP id of the processor running the program. + * + * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) + * Description + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. *flags* are a combination of + * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the + * checksum for the packet after storing the bytes) and + * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ + * **->swhash** and *skb*\ **->l4hash** to 0). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size) + * Description + * Recompute the layer 3 (e.g. IP) checksum for the packet + * associated to *skb*. Computation is incremental, so the helper + * must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored in *size*. + * Alternatively, it is possible to store the difference between + * the previous and the new values of the header field in *to*, by + * setting *from* and *size* to 0. For both methods, *offset* + * indicates the location of the IP checksum within the packet. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags) + * Description + * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the + * packet associated to *skb*. Computation is incremental, so the + * helper must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored on the lowest + * four bits of *flags*. Alternatively, it is possible to store + * the difference between the previous and the new values of the + * header field in *to*, by setting *from* and the four lowest + * bits of *flags* to 0. For both methods, *offset* indicates the + * location of the IP checksum within the packet. In addition to + * the size of the field, *flags* can be added (bitwise OR) actual + * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left + * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and + * for updates resulting in a null checksum the value is set to + * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates + * the checksum is to be computed against a pseudo-header. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index) + * Description + * This special helper is used to trigger a "tail call", or in + * other words, to jump into another eBPF program. The same stack + * frame is used (but values on stack and in registers for the + * caller are not accessible to the callee). This mechanism allows + * for program chaining, either for raising the maximum number of + * available eBPF instructions, or to execute given programs in + * conditional blocks. For security reasons, there is an upper + * limit to the number of successive tail calls that can be + * performed. + * + * Upon call of this helper, the program attempts to jump into a + * program referenced at index *index* in *prog_array_map*, a + * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes + * *ctx*, a pointer to the context. + * + * If the call succeeds, the kernel immediately runs the first + * instruction of the new program. This is not a function call, + * and it never returns to the previous program. If the call + * fails, then the helper has no effect, and the caller continues + * to run its subsequent instructions. A call can fail if the + * destination program for the jump does not exist (i.e. *index* + * is superior to the number of entries in *prog_array_map*), or + * if the maximum number of tail calls has been reached for this + * chain of programs. This limit is defined in the kernel by the + * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), + * which is currently set to 32. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags) + * Description + * Clone and redirect the packet associated to *skb* to another + * net device of index *ifindex*. Both ingress and egress + * interfaces can be used for redirection. The **BPF_F_INGRESS** + * value in *flags* is used to make the distinction (ingress path + * is selected if the flag is present, egress path otherwise). + * This is the only flag supported for now. + * + * In comparison with **bpf_redirect**\ () helper, + * **bpf_clone_redirect**\ () has the associated cost of + * duplicating the packet buffer, but this can be executed out of + * the eBPF program. Conversely, **bpf_redirect**\ () is more + * efficient, but it is handled through an action code where the + * redirection happens only after the eBPF program has returned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_get_current_pid_tgid(void) + * Return + * A 64-bit integer containing the current tgid and pid, and + * created as such: + * *current_task*\ **->tgid << 32 \|** + * *current_task*\ **->pid**. + * + * u64 bpf_get_current_uid_gid(void) + * Return + * A 64-bit integer containing the current GID and UID, and + * created as such: *current_gid* **<< 32 \|** *current_uid*. + * + * long bpf_get_current_comm(void *buf, u32 size_of_buf) + * Description + * Copy the **comm** attribute of the current task into *buf* of + * *size_of_buf*. The **comm** attribute contains the name of + * the executable (excluding the path) for the current task. The + * *size_of_buf* must be strictly positive. On success, the + * helper makes sure that the *buf* is NUL-terminated. On failure, + * it is filled with zeroes. + * Return + * 0 on success, or a negative error in case of failure. + * + * u32 bpf_get_cgroup_classid(struct sk_buff *skb) + * Description + * Retrieve the classid for the current task, i.e. for the net_cls + * cgroup to which *skb* belongs. + * + * This helper can be used on TC egress path, but not on ingress. + * + * The net_cls cgroup provides an interface to tag network packets + * based on a user-provided identifier for all traffic coming from + * the tasks belonging to the related cgroup. See also the related + * kernel documentation, available from the Linux sources in file + * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. + * + * The Linux kernel has two versions for cgroups: there are + * cgroups v1 and cgroups v2. Both are available to users, who can + * use a mixture of them, but note that the net_cls cgroup is for + * cgroup v1 only. This makes it incompatible with BPF programs + * run on cgroups, which is a cgroup-v2-only feature (a socket can + * only hold data for one version of cgroups at a time). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to + * "**y**" or to "**m**". + * Return + * The classid, or 0 for the default unconfigured classid. + * + * long bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) + * Description + * Push a *vlan_tci* (VLAN tag control information) of protocol + * *vlan_proto* to the packet associated to *skb*, then update + * the checksum. Note that if *vlan_proto* is different from + * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to + * be **ETH_P_8021Q**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_vlan_pop(struct sk_buff *skb) + * Description + * Pop a VLAN header from the packet associated to *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) + * Description + * Get tunnel metadata. This helper takes a pointer *key* to an + * empty **struct bpf_tunnel_key** of **size**, that will be + * filled with tunnel metadata for the packet associated to *skb*. + * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which + * indicates that the tunnel is based on IPv6 protocol instead of + * IPv4. + * + * The **struct bpf_tunnel_key** is an object that generalizes the + * principal parameters used by various tunneling protocols into a + * single struct. This way, it can be used to easily make a + * decision based on the contents of the encapsulation header, + * "summarized" in this struct. In particular, it holds the IP + * address of the remote end (IPv4 or IPv6, depending on the case) + * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, + * this struct exposes the *key*\ **->tunnel_id**, which is + * generally mapped to a VNI (Virtual Network Identifier), making + * it programmable together with the **bpf_skb_set_tunnel_key**\ + * () helper. + * + * Let's imagine that the following code is part of a program + * attached to the TC ingress interface, on one end of a GRE + * tunnel, and is supposed to filter out all messages coming from + * remote ends with IPv4 address other than 10.0.0.1: + * + * :: + * + * int ret; + * struct bpf_tunnel_key key = {}; + * + * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); + * if (ret < 0) + * return TC_ACT_SHOT; // drop packet + * + * if (key.remote_ipv4 != 0x0a000001) + * return TC_ACT_SHOT; // drop packet + * + * return TC_ACT_OK; // accept packet + * + * This interface can also be used with all encapsulation devices + * that can operate in "collect metadata" mode: instead of having + * one network device per specific configuration, the "collect + * metadata" mode only requires a single device where the + * configuration can be extracted from this helper. + * + * This can be used together with various tunnels such as VXLan, + * Geneve, GRE or IP in IP (IPIP). + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags) + * Description + * Populate tunnel metadata for packet associated to *skb.* The + * tunnel metadata is set to the contents of *key*, of *size*. The + * *flags* can be set to a combination of the following values: + * + * **BPF_F_TUNINFO_IPV6** + * Indicate that the tunnel is based on IPv6 protocol + * instead of IPv4. + * **BPF_F_ZERO_CSUM_TX** + * For IPv4 packets, add a flag to tunnel metadata + * indicating that checksum computation should be skipped + * and checksum set to zeroes. + * **BPF_F_DONT_FRAGMENT** + * Add a flag to tunnel metadata indicating that the + * packet should not be fragmented. + * **BPF_F_SEQ_NUMBER** + * Add a flag to tunnel metadata indicating that a + * sequence number should be added to tunnel header before + * sending the packet. This flag was added for GRE + * encapsulation, but might be used with other protocols + * as well in the future. + * + * Here is a typical usage on the transmit path: + * + * :: + * + * struct bpf_tunnel_key key; + * populate key ... + * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); + * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); + * + * See also the description of the **bpf_skb_get_tunnel_key**\ () + * helper for additional information. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags) + * Description + * Read the value of a perf event counter. This helper relies on a + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of + * the perf event counter is selected when *map* is updated with + * perf event file descriptors. The *map* is an array whose size + * is the number of available CPUs, and each cell contains a value + * relative to one CPU. The value to retrieve is indicated by + * *flags*, that contains the index of the CPU to look up, masked + * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * Note that before Linux 4.13, only hardware perf event can be + * retrieved. + * + * Also, be aware that the newer helper + * **bpf_perf_event_read_value**\ () is recommended over + * **bpf_perf_event_read**\ () in general. The latter has some ABI + * quirks where error and counter value are used as a return code + * (which is wrong to do since ranges may overlap). This issue is + * fixed with **bpf_perf_event_read_value**\ (), which at the same + * time provides more features over the **bpf_perf_event_read**\ + * () interface. Please refer to the description of + * **bpf_perf_event_read_value**\ () for details. + * Return + * The value of the perf event counter read from the map, or a + * negative error code in case of failure. + * + * long bpf_redirect(u32 ifindex, u64 flags) + * Description + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_clone_redirect**\ + * (), except that the packet is not cloned, which provides + * increased performance. + * + * Except for XDP, both ingress and egress interfaces can be used + * for redirection. The **BPF_F_INGRESS** value in *flags* is used + * to make the distinction (ingress path is selected if the flag + * is present, egress path otherwise). Currently, XDP only + * supports redirection to the egress interface, and accepts no + * flag at all. + * + * The same effect can also be attained with the more generic + * **bpf_redirect_map**\ (), which uses a BPF map to store the + * redirect target instead of providing it directly to the helper. + * Return + * For XDP, the helper returns **XDP_REDIRECT** on success or + * **XDP_ABORTED** on error. For other program types, the values + * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on + * error. + * + * u32 bpf_get_route_realm(struct sk_buff *skb) + * Description + * Retrieve the realm or the route, that is to say the + * **tclassid** field of the destination for the *skb*. The + * identifier retrieved is a user-provided tag, similar to the + * one used with the net_cls cgroup (see description for + * **bpf_get_cgroup_classid**\ () helper), but here this tag is + * held by a route (a destination entry), not by a task. + * + * Retrieving this identifier works with the clsact TC egress hook + * (see also **tc-bpf(8)**), or alternatively on conventional + * classful egress qdiscs, but not on TC ingress path. In case of + * clsact TC egress hook, this has the advantage that, internally, + * the destination entry has not been dropped yet in the transmit + * path. Therefore, the destination entry does not need to be + * artificially held via **netif_keep_dst**\ () for a classful + * qdisc until the *skb* is freed. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_IP_ROUTE_CLASSID** configuration option. + * Return + * The realm of the route for the packet associated to *skb*, or 0 + * if none was found. + * + * long bpf_perf_event_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) + * Description + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * The context of the program *ctx* needs also be passed to the + * helper. + * + * On user space, a program willing to read the values needs to + * call **perf_event_open**\ () on the perf event (either for + * one or for all CPUs) and to store the file descriptor into the + * *map*. This must be done before the eBPF program can send data + * into it. An example is available in file + * *samples/bpf/trace_output_user.c* in the Linux kernel source + * tree (the eBPF program counterpart is in + * *samples/bpf/trace_output_kern.c*). + * + * **bpf_perf_event_output**\ () achieves better performance + * than **bpf_trace_printk**\ () for sharing data with user + * space, and is much better suitable for streaming data from eBPF + * programs. + * + * Note that this helper is not restricted to tracing use cases + * and can be used with programs attached to TC or XDP as well, + * where it allows for passing data to user space listeners. Data + * can be: + * + * * Only custom structs, + * * Only the packet payload, or + * * A combination of both. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_load_bytes(const void *skb, u32 offset, void *to, u32 len) + * Description + * This helper was provided as an easy way to load data from a + * packet. It can be used to load *len* bytes from *offset* from + * the packet associated to *skb*, into the buffer pointed by + * *to*. + * + * Since Linux 4.7, usage of this helper has mostly been replaced + * by "direct packet access", enabling packet data to be + * manipulated with *skb*\ **->data** and *skb*\ **->data_end** + * pointing respectively to the first byte of packet data and to + * the byte after the last byte of packet data. However, it + * remains useful if one wishes to read large quantities of data + * at once from a packet into the eBPF stack. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_get_stackid(void *ctx, struct bpf_map *map, u64 flags) + * Description + * Walk a user or a kernel stack and return its id. To achieve + * this, the helper needs *ctx*, which is a pointer to the context + * on which the tracing program is executed, and a pointer to a + * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * a combination of the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_FAST_STACK_CMP** + * Compare stacks by hash only. + * **BPF_F_REUSE_STACKID** + * If two different stacks hash into the same *stackid*, + * discard the old one. + * + * The stack id retrieved is a 32 bit long integer handle which + * can be further combined with other data (including other stack + * ids) and used as a key into maps. This can be useful for + * generating a variety of graphs (such as flame graphs or off-cpu + * graphs). + * + * For walking a stack, this helper is an improvement over + * **bpf_probe_read**\ (), which can be used with unrolled loops + * but is not efficient and consumes a lot of eBPF instructions. + * Instead, **bpf_get_stackid**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * Return + * The positive or null stack id on success, or a negative error + * in case of failure. + * + * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed) + * Description + * Compute a checksum difference, from the raw buffer pointed by + * *from*, of length *from_size* (that must be a multiple of 4), + * towards the raw buffer pointed by *to*, of size *to_size* + * (same remark). An optional *seed* can be added to the value + * (this can be cascaded, the seed may come from a previous call + * to the helper). + * + * This is flexible enough to be used in several ways: + * + * * With *from_size* == 0, *to_size* > 0 and *seed* set to + * checksum, it can be used when pushing new data. + * * With *from_size* > 0, *to_size* == 0 and *seed* set to + * checksum, it can be used when removing data from a packet. + * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it + * can be used to compute a diff. Note that *from_size* and + * *to_size* do not need to be equal. + * + * This helper can be used in combination with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to + * which one can feed in the difference computed with + * **bpf_csum_diff**\ (). + * Return + * The checksum result, or a negative error code in case of + * failure. + * + * long bpf_skb_get_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) + * Description + * Retrieve tunnel options metadata for the packet associated to + * *skb*, and store the raw tunnel option data to the buffer *opt* + * of *size*. + * + * This helper can be used with encapsulation devices that can + * operate in "collect metadata" mode (please refer to the related + * note in the description of **bpf_skb_get_tunnel_key**\ () for + * more details). A particular example where this can be used is + * in combination with the Geneve encapsulation protocol, where it + * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) + * and retrieving arbitrary TLVs (Type-Length-Value headers) from + * the eBPF program. This allows for full customization of these + * headers. + * Return + * The size of the option data retrieved. + * + * long bpf_skb_set_tunnel_opt(struct sk_buff *skb, void *opt, u32 size) + * Description + * Set tunnel options metadata for the packet associated to *skb* + * to the option data contained in the raw buffer *opt* of *size*. + * + * See also the description of the **bpf_skb_get_tunnel_opt**\ () + * helper for additional information. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags) + * Description + * Change the protocol of the *skb* to *proto*. Currently + * supported are transition from IPv4 to IPv6, and from IPv6 to + * IPv4. The helper takes care of the groundwork for the + * transition, including resizing the socket buffer. The eBPF + * program is expected to fill the new headers, if any, via + * **skb_store_bytes**\ () and to recompute the checksums with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ + * (). The main case for this helper is to perform NAT64 + * operations out of an eBPF program. + * + * Internally, the GSO type is marked as dodgy so that headers are + * checked and segments are recalculated by the GSO/GRO engine. + * The size for GSO target is adapted as well. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_change_type(struct sk_buff *skb, u32 type) + * Description + * Change the packet type for the packet associated to *skb*. This + * comes down to setting *skb*\ **->pkt_type** to *type*, except + * the eBPF program does not have a write access to *skb*\ + * **->pkt_type** beside this helper. Using a helper here allows + * for graceful handling of errors. + * + * The major use case is to change incoming *skb*s to + * **PACKET_HOST** in a programmatic way instead of having to + * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for + * example. + * + * Note that *type* only allows certain values. At this time, they + * are: + * + * **PACKET_HOST** + * Packet is for us. + * **PACKET_BROADCAST** + * Send packet to all. + * **PACKET_MULTICAST** + * Send packet to group. + * **PACKET_OTHERHOST** + * Send packet to someone else. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index) + * Description + * Check whether *skb* is a descendant of the cgroup2 held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * Return + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* failed the cgroup2 descendant test. + * * 1, if the *skb* succeeded the cgroup2 descendant test. + * * A negative error code, if an error occurred. + * + * u32 bpf_get_hash_recalc(struct sk_buff *skb) + * Description + * Retrieve the hash of the packet, *skb*\ **->hash**. If it is + * not set, in particular if the hash was cleared due to mangling, + * recompute this hash. Later accesses to the hash can be done + * directly with *skb*\ **->hash**. + * + * Calling **bpf_set_hash_invalid**\ (), changing a packet + * prototype with **bpf_skb_change_proto**\ (), or calling + * **bpf_skb_store_bytes**\ () with the + * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear + * the hash and to trigger a new computation for the next call to + * **bpf_get_hash_recalc**\ (). + * Return + * The 32-bit hash. + * + * u64 bpf_get_current_task(void) + * Return + * A pointer to the current task struct. + * + * long bpf_probe_write_user(void *dst, const void *src, u32 len) + * Description + * Attempt in a safe way to write *len* bytes from the buffer + * *src* to *dst* in memory. It only works for threads that are in + * user context, and *dst* must be a valid user space address. + * + * This helper should not be used to implement any kind of + * security mechanism because of TOC-TOU attacks, but rather to + * debug, divert, and manipulate execution of semi-cooperative + * processes. + * + * Keep in mind that this feature is meant for experiments, and it + * has a risk of crashing the system and running programs. + * Therefore, when an eBPF program using this helper is attached, + * a warning including PID and process name is printed to kernel + * logs. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_current_task_under_cgroup(struct bpf_map *map, u32 index) + * Description + * Check whether the probe is being run is the context of a given + * subset of the cgroup2 hierarchy. The cgroup2 to test is held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * Return + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* task belongs to the cgroup2. + * * 1, if the *skb* task does not belong to the cgroup2. + * * A negative error code, if an error occurred. + * + * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags) + * Description + * Resize (trim or grow) the packet associated to *skb* to the + * new *len*. The *flags* are reserved for future usage, and must + * be left at zero. + * + * The basic idea is that the helper performs the needed work to + * change the size of the packet, then the eBPF program rewrites + * the rest via helpers like **bpf_skb_store_bytes**\ (), + * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () + * and others. This helper is a slow path utility intended for + * replies with control messages. And because it is targeted for + * slow path, the helper itself can afford to be slow: it + * implicitly linearizes, unclones and drops offloads from the + * *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_pull_data(struct sk_buff *skb, u32 len) + * Description + * Pull in non-linear data in case the *skb* is non-linear and not + * all of *len* are part of the linear section. Make *len* bytes + * from *skb* readable and writable. If a zero value is passed for + * *len*, then the whole length of the *skb* is pulled. + * + * This helper is only needed for reading and writing with direct + * packet access. + * + * For direct packet access, testing that offsets to access + * are within packet boundaries (test on *skb*\ **->data_end**) is + * susceptible to fail if offsets are invalid, or if the requested + * data is in non-linear parts of the *skb*. On failure the + * program can just bail out, or in the case of a non-linear + * buffer, use a helper to make the data available. The + * **bpf_skb_load_bytes**\ () helper is a first solution to access + * the data. Another one consists in using **bpf_skb_pull_data** + * to pull in once the non-linear parts, then retesting and + * eventually access the data. + * + * At the same time, this also makes sure the *skb* is uncloned, + * which is a necessary condition for direct write. As this needs + * to be an invariant for the write part only, the verifier + * detects writes and adds a prologue that is calling + * **bpf_skb_pull_data()** to effectively unclone the *skb* from + * the very beginning in case it is indeed cloned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum) + * Description + * Add the checksum *csum* into *skb*\ **->csum** in case the + * driver has supplied a checksum for the entire packet into that + * field. Return an error otherwise. This helper is intended to be + * used in combination with **bpf_csum_diff**\ (), in particular + * when the checksum needs to be updated after data has been + * written into the packet through direct packet access. + * Return + * The checksum on success, or a negative error code in case of + * failure. + * + * void bpf_set_hash_invalid(struct sk_buff *skb) + * Description + * Invalidate the current *skb*\ **->hash**. It can be used after + * mangling on headers through direct packet access, in order to + * indicate that the hash is outdated and to trigger a + * recalculation the next time the kernel tries to access this + * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * + * long bpf_get_numa_node_id(void) + * Description + * Return the id of the current NUMA node. The primary use case + * for this helper is the selection of sockets for the local NUMA + * node, when the program is attached to sockets using the + * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), + * but the helper is also available to other eBPF program types, + * similarly to **bpf_get_smp_processor_id**\ (). + * Return + * The id of current NUMA node. + * + * long bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags) + * Description + * Grows headroom of packet associated to *skb* and adjusts the + * offset of the MAC header accordingly, adding *len* bytes of + * space. It automatically extends and reallocates memory as + * required. + * + * This helper can be used on a layer 3 *skb* to push a MAC header + * for redirection into a layer 2 device. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta) + * Description + * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that + * it is possible to use a negative value for *delta*. This helper + * can be used to prepare the packet for pushing or popping + * headers. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_probe_read_str(void *dst, u32 size, const void *unsafe_ptr) + * Description + * Copy a NUL terminated string from an unsafe kernel address + * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for + * more details. + * + * Generally, use **bpf_probe_read_user_str**\ () or + * **bpf_probe_read_kernel_str**\ () instead. + * Return + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + * + * u64 bpf_get_socket_cookie(struct sk_buff *skb) + * Description + * If the **struct sk_buff** pointed by *skb* has a known socket, + * retrieve the cookie (generated by the kernel) of this socket. + * If no cookie has been set yet, generate a new cookie. Once + * generated, the socket cookie remains stable for the life of the + * socket. This helper can be useful for monitoring per socket + * networking traffic statistics as it provides a global socket + * identifier that can be assumed unique. + * Return + * A 8-byte long non-decreasing number on success, or 0 if the + * socket field is missing inside *skb*. + * + * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) + * Description + * Equivalent to bpf_get_socket_cookie() helper that accepts + * *skb*, but gets socket from **struct bpf_sock_addr** context. + * Return + * A 8-byte long non-decreasing number. + * + * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) + * Description + * Equivalent to **bpf_get_socket_cookie**\ () helper that accepts + * *skb*, but gets socket from **struct bpf_sock_ops** context. + * Return + * A 8-byte long non-decreasing number. + * + * u32 bpf_get_socket_uid(struct sk_buff *skb) + * Return + * The owner UID of the socket associated to *skb*. If the socket + * is **NULL**, or if it is not a full socket (i.e. if it is a + * time-wait or a request socket instead), **overflowuid** value + * is returned (note that **overflowuid** might also be the actual + * UID value for the socket). + * + * long bpf_set_hash(struct sk_buff *skb, u32 hash) + * Description + * Set the full hash for *skb* (set the field *skb*\ **->hash**) + * to value *hash*. + * Return + * 0 + * + * long bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen) + * Description + * Emulate a call to **setsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **setsockopt(2)** for more information. + * The option value of length *optlen* is pointed by *optval*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **setsockopt()**. + * It supports the following *level*\ s: + * + * * **SOL_SOCKET**, which supports the following *optname*\ s: + * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, + * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * * **IPPROTO_TCP**, which supports the following *optname*\ s: + * **TCP_CONGESTION**, **TCP_BPF_IW**, + * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, + * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags) + * Description + * Grow or shrink the room for data in the packet associated to + * *skb* by *len_diff*, and according to the selected *mode*. + * + * By default, the helper will reset any offloaded checksum + * indicator of the skb to CHECKSUM_NONE. This can be avoided + * by the following flag: + * + * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded + * checksum data of the skb to CHECKSUM_NONE. + * + * There are two supported modes at this time: + * + * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer + * (room space is added or removed below the layer 2 header). + * + * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer + * (room space is added or removed below the layer 3 header). + * + * The following flags are supported at this time: + * + * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. + * Adjusting mss in this way is not allowed for datagrams. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, + * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: + * Any new space is reserved to hold a tunnel header. + * Configure skb offsets and other fields accordingly. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, + * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: + * Use with ENCAP_L3 flags to further specify the tunnel type. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): + * Use with ENCAP_L3/L4 flags to further specify the tunnel + * type; *len* is the length of the inner MAC header. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags) + * Description + * Redirect the packet to the endpoint referenced by *map* at + * index *key*. Depending on its type, this *map* can contain + * references to net devices (for forwarding packets through other + * ports), or to CPUs (for redirecting XDP frames to another CPU; + * but this is only implemented for native XDP (with driver + * support) as of this writing). + * + * The lower two bits of *flags* are used as the return code if + * the map lookup fails. This is so that the return value can be + * one of the XDP program return codes up to **XDP_TX**, as chosen + * by the caller. Any higher bits in the *flags* argument must be + * unset. + * + * See also **bpf_redirect**\ (), which only supports redirecting + * to an ifindex, but doesn't require a map to do so. + * Return + * **XDP_REDIRECT** on success, or the value of the two lower bits + * of the *flags* argument on error. + * + * long bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags) + * Description + * Redirect the packet to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * Return + * **SK_PASS** on success, or **SK_DROP** on error. + * + * long bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) + * Description + * Add an entry to, or update a *map* referencing sockets. The + * *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta) + * Description + * Adjust the address pointed by *xdp_md*\ **->data_meta** by + * *delta* (which can be positive or negative). Note that this + * operation modifies the address stored in *xdp_md*\ **->data**, + * so the latter must be loaded only after the helper has been + * called. + * + * The use of *xdp_md*\ **->data_meta** is optional and programs + * are not required to use it. The rationale is that when the + * packet is processed with XDP (e.g. as DoS filter), it is + * possible to push further meta data along with it before passing + * to the stack, and to give the guarantee that an ingress eBPF + * program attached as a TC classifier on the same device can pick + * this up for further post-processing. Since TC works with socket + * buffers, it remains possible to set from XDP the **mark** or + * **priority** pointers, or other pointers for the socket buffer. + * Having this scratch space generic and programmable allows for + * more flexibility as the user is free to store whatever meta + * data they need. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size) + * Description + * Read the value of a perf event counter, and store it into *buf* + * of size *buf_size*. This helper relies on a *map* of type + * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event + * counter is selected when *map* is updated with perf event file + * descriptors. The *map* is an array whose size is the number of + * available CPUs, and each cell contains a value relative to one + * CPU. The value to retrieve is indicated by *flags*, that + * contains the index of the CPU to look up, masked with + * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * This helper behaves in a way close to + * **bpf_perf_event_read**\ () helper, save that instead of + * just returning the value observed, it fills the *buf* + * structure. This allows for additional data to be retrieved: in + * particular, the enabled and running times (in *buf*\ + * **->enabled** and *buf*\ **->running**, respectively) are + * copied. In general, **bpf_perf_event_read_value**\ () is + * recommended over **bpf_perf_event_read**\ (), which has some + * ABI issues and provides fewer functionalities. + * + * These values are interesting, because hardware PMU (Performance + * Monitoring Unit) counters are limited resources. When there are + * more PMU based perf events opened than available counters, + * kernel will multiplex these events so each event gets certain + * percentage (but not all) of the PMU time. In case that + * multiplexing happens, the number of samples or counter value + * will not reflect the case compared to when no multiplexing + * occurs. This makes comparison between different runs difficult. + * Typically, the counter value should be normalized before + * comparing to other experiments. The usual normalization is done + * as follows. + * + * :: + * + * normalized_counter = counter * t_enabled / t_running + * + * Where t_enabled is the time enabled for event and t_running is + * the time running for event since last normalization. The + * enabled and running times are accumulated since the perf event + * open. To achieve scaling factor between two invocations of an + * eBPF program, users can use CPU id as the key (which is + * typical for perf array usage model) to remember the previous + * value and do the calculation inside the eBPF program. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size) + * Description + * For en eBPF program attached to a perf event, retrieve the + * value of the event counter associated to *ctx* and store it in + * the structure pointed by *buf* and of size *buf_size*. Enabled + * and running times are also stored in the structure (see + * description of helper **bpf_perf_event_read_value**\ () for + * more details). + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen) + * Description + * Emulate a call to **getsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **getsockopt(2)** for more information. + * The retrieved value is stored in the structure pointed by + * *opval* and of length *optlen*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **getsockopt()**. + * It supports the following *level*\ s: + * + * * **IPPROTO_TCP**, which supports *optname* + * **TCP_CONGESTION**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_override_return(struct pt_regs *regs, u64 rc) + * Description + * Used for error injection, this helper uses kprobes to override + * the return value of the probed function, and to set it to *rc*. + * The first argument is the context *regs* on which the kprobe + * works. + * + * This helper works by setting the PC (program counter) + * to an override function which is run in place of the original + * probed function. This means the probed function is not run at + * all. The replacement function just returns with the required + * value. + * + * This helper has security implications, and thus is subject to + * restrictions. It is only available if the kernel was compiled + * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration + * option, and in this case it only works on functions tagged with + * **ALLOW_ERROR_INJECTION** in the kernel code. + * + * Also, the helper is only available for the architectures having + * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, + * x86 architecture is the only one to support this feature. + * Return + * 0 + * + * long bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval) + * Description + * Attempt to set the value of the **bpf_sock_ops_cb_flags** field + * for the full TCP socket associated to *bpf_sock_ops* to + * *argval*. + * + * The primary use of this field is to determine if there should + * be calls to eBPF programs of type + * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP + * code. A program of the same type can change its value, per + * connection and as necessary, when the connection is + * established. This field is directly accessible for reading, but + * this helper must be used for updates in order to return an + * error if an eBPF program tries to set a callback that is not + * supported in the current kernel. + * + * *argval* is a flag array which can combine these flags: + * + * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) + * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) + * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) + * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) + * + * Therefore, this function can be used to clear a callback flag by + * setting the appropriate bit to zero. e.g. to disable the RTO + * callback: + * + * **bpf_sock_ops_cb_flags_set(bpf_sock,** + * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** + * + * Here are some examples of where one could call such eBPF + * program: + * + * * When RTO fires. + * * When a packet is retransmitted. + * * When the connection terminates. + * * When a packet is sent. + * * When a packet is received. + * Return + * Code **-EINVAL** if the socket is not a full TCP socket; + * otherwise, a positive number containing the bits that could not + * be set is returned (which comes down to 0 if all bits were set + * as required). + * + * long bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags) + * Description + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * Return + * **SK_PASS** on success, or **SK_DROP** on error. + * + * long bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes) + * Description + * For socket policies, apply the verdict of the eBPF program to + * the next *bytes* (number of bytes) of message *msg*. + * + * For example, this helper can be used in the following cases: + * + * * A single **sendmsg**\ () or **sendfile**\ () system call + * contains multiple logical messages that the eBPF program is + * supposed to read and for which it should apply a verdict. + * * An eBPF program only cares to read the first *bytes* of a + * *msg*. If the message has a large payload, then setting up + * and calling the eBPF program repeatedly for all bytes, even + * though the verdict is already known, would create unnecessary + * overhead. + * + * When called from within an eBPF program, the helper sets a + * counter internal to the BPF infrastructure, that is used to + * apply the last verdict to the next *bytes*. If *bytes* is + * smaller than the current data being processed from a + * **sendmsg**\ () or **sendfile**\ () system call, the first + * *bytes* will be sent and the eBPF program will be re-run with + * the pointer for start of data pointing to byte number *bytes* + * **+ 1**. If *bytes* is larger than the current data being + * processed, then the eBPF verdict will be applied to multiple + * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are + * consumed. + * + * Note that if a socket closes with the internal counter holding + * a non-zero value, this is not a problem because data is not + * being buffered for *bytes* and is sent as it is received. + * Return + * 0 + * + * long bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes) + * Description + * For socket policies, prevent the execution of the verdict eBPF + * program for message *msg* until *bytes* (byte number) have been + * accumulated. + * + * This can be used when one needs a specific number of bytes + * before a verdict can be assigned, even if the data spans + * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme + * case would be a user calling **sendmsg**\ () repeatedly with + * 1-byte long message segments. Obviously, this is bad for + * performance, but it is still valid. If the eBPF program needs + * *bytes* bytes to validate a header, this helper can be used to + * prevent the eBPF program to be called again until *bytes* have + * been accumulated. + * Return + * 0 + * + * long bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags) + * Description + * For socket policies, pull in non-linear data from user space + * for *msg* and set pointers *msg*\ **->data** and *msg*\ + * **->data_end** to *start* and *end* bytes offsets into *msg*, + * respectively. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it can only parse data that the (**data**, **data_end**) + * pointers have already consumed. For **sendmsg**\ () hooks this + * is likely the first scatterlist element. But for calls relying + * on the **sendpage** handler (e.g. **sendfile**\ ()) this will + * be the range (**0**, **0**) because the data is shared with + * user space and by default the objective is to avoid allowing + * user space to modify data while (or after) eBPF verdict is + * being decided. This helper can be used to pull in data and to + * set the start and end pointer to given values. Data will be + * copied if necessary (i.e. if data was not linear and if start + * and end pointers do not point to the same chunk). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) + * Description + * Bind the socket associated to *ctx* to the address pointed by + * *addr*, of length *addr_len*. This allows for making outgoing + * connection from the desired IP address, which can be useful for + * example when all processes inside a cgroup should use one + * single IP address on a host that has multiple IP configured. + * + * This helper works for IPv4 and IPv6, TCP and UDP sockets. The + * domain (*addr*\ **->sa_family**) must be **AF_INET** (or + * **AF_INET6**). It's advised to pass zero port (**sin_port** + * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like + * behavior and lets the kernel efficiently pick up an unused + * port as long as 4-tuple is unique. Passing non-zero port might + * lead to degraded performance. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta) + * Description + * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is + * possible to both shrink and grow the packet tail. + * Shrink done via *delta* being a negative integer. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags) + * Description + * Retrieve the XFRM state (IP transform framework, see also + * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. + * + * The retrieved value is stored in the **struct bpf_xfrm_state** + * pointed by *xfrm_state* and of length *size*. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_XFRM** configuration option. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_get_stack(void *ctx, void *buf, u32 size, u64 flags) + * Description + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *ctx*, which is a pointer + * to the context on which the tracing program is executed. + * To store the stacktrace, the bpf program provides *buf* with + * a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * Return + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + * + * long bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header) + * Description + * This helper is similar to **bpf_skb_load_bytes**\ () in that + * it provides an easy way to load *len* bytes from *offset* + * from the packet associated to *skb*, into the buffer pointed + * by *to*. The difference to **bpf_skb_load_bytes**\ () is that + * a fifth argument *start_header* exists in order to select a + * base offset to start from. *start_header* can be one of: + * + * **BPF_HDR_START_MAC** + * Base offset to load data from is *skb*'s mac header. + * **BPF_HDR_START_NET** + * Base offset to load data from is *skb*'s network header. + * + * In general, "direct packet access" is the preferred method to + * access packet data, however, this helper is in particular useful + * in socket filters where *skb*\ **->data** does not always point + * to the start of the mac header and where "direct packet access" + * is not available. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) + * Description + * Do FIB lookup in kernel tables using parameters in *params*. + * If lookup is successful and result shows packet is to be + * forwarded, the neighbor tables are searched for the nexthop. + * If successful (ie., FIB lookup shows forwarding and nexthop + * is resolved), the nexthop address is returned in ipv4_dst + * or ipv6_dst based on family, smac is set to mac address of + * egress device, dmac is set to nexthop mac address, rt_metric + * is set to metric from route (IPv4/IPv6 only), and ifindex + * is set to the device index of the nexthop from the FIB lookup. + * + * *plen* argument is the size of the passed in struct. + * *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_FIB_LOOKUP_DIRECT** + * Do a direct table lookup vs full lookup using FIB + * rules. + * **BPF_FIB_LOOKUP_OUTPUT** + * Perform lookup from an egress perspective (default is + * ingress). + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** tc cls_act programs. + * Return + * * < 0 if any input argument is invalid + * * 0 on success (packet is forwarded, nexthop neighbor exists) + * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the + * packet is not forwarded or needs assist from full stack + * + * long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) + * Description + * Add an entry to, or update a sockhash *map* referencing sockets. + * The *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags) + * Description + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * Return + * **SK_PASS** on success, or **SK_DROP** on error. + * + * long bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags) + * Description + * This helper is used in programs implementing policies at the + * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. + * if the verdeict eBPF program returns **SK_PASS**), redirect it + * to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress otherwise). This is the only flag supported for now. + * Return + * **SK_PASS** on success, or **SK_DROP** on error. + * + * long bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len) + * Description + * Encapsulate the packet associated to *skb* within a Layer 3 + * protocol header. This header is provided in the buffer at + * address *hdr*, with *len* its size in bytes. *type* indicates + * the protocol of the header and can be one of: + * + * **BPF_LWT_ENCAP_SEG6** + * IPv6 encapsulation with Segment Routing Header + * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, + * the IPv6 header is computed by the kernel. + * **BPF_LWT_ENCAP_SEG6_INLINE** + * Only works if *skb* contains an IPv6 packet. Insert a + * Segment Routing Header (**struct ipv6_sr_hdr**) inside + * the IPv6 header. + * **BPF_LWT_ENCAP_IP** + * IP encapsulation (GRE/GUE/IPIP/etc). The outer header + * must be IPv4 or IPv6, followed by zero or more + * additional headers, up to **LWT_BPF_MAX_HEADROOM** + * total bytes in all prepended headers. Please note that + * if **skb_is_gso**\ (*skb*) is true, no more than two + * headers can be prepended, and the inner header, if + * present, should be either GRE or UDP/GUE. + * + * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs + * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can + * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and + * **BPF_PROG_TYPE_LWT_XMIT**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len) + * Description + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. Only the flags, tag and TLVs + * inside the outermost IPv6 Segment Routing Header can be + * modified through this helper. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta) + * Description + * Adjust the size allocated to TLVs in the outermost IPv6 + * Segment Routing Header contained in the packet associated to + * *skb*, at position *offset* by *delta* bytes. Only offsets + * after the segments are accepted. *delta* can be as well + * positive (growing) as negative (shrinking). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len) + * Description + * Apply an IPv6 Segment Routing action of type *action* to the + * packet associated to *skb*. Each action takes a parameter + * contained at address *param*, and of length *param_len* bytes. + * *action* can be one of: + * + * **SEG6_LOCAL_ACTION_END_X** + * End.X action: Endpoint with Layer-3 cross-connect. + * Type of *param*: **struct in6_addr**. + * **SEG6_LOCAL_ACTION_END_T** + * End.T action: Endpoint with specific IPv6 table lookup. + * Type of *param*: **int**. + * **SEG6_LOCAL_ACTION_END_B6** + * End.B6 action: Endpoint bound to an SRv6 policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * **SEG6_LOCAL_ACTION_END_B6_ENCAP** + * End.B6.Encap action: Endpoint bound to an SRv6 + * encapsulation policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_rc_repeat(void *ctx) + * Description + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded repeat key message. This delays + * the generation of a key up event for previously generated + * key down event. + * + * Some IR protocols like NEC have a special IR message for + * repeating last button, for when a button is held down. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * Return + * 0 + * + * long bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle) + * Description + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded key press with *scancode*, + * *toggle* value in the given *protocol*. The scancode will be + * translated to a keycode using the rc keymap, and reported as + * an input key down event. After a period a key up event is + * generated. This period can be extended by calling either + * **bpf_rc_keydown**\ () again with the same values, or calling + * **bpf_rc_repeat**\ (). + * + * Some protocols include a toggle bit, in case the button was + * released and pressed again between consecutive scancodes. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * The *protocol* is the decoded protocol number (see + * **enum rc_proto** for some predefined values). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * Return + * 0 + * + * u64 bpf_skb_cgroup_id(struct sk_buff *skb) + * Description + * Return the cgroup v2 id of the socket associated with the *skb*. + * This is roughly similar to the **bpf_get_cgroup_classid**\ () + * helper for cgroup v1 by providing a tag resp. identifier that + * can be matched on or used for map lookups e.g. to implement + * policy. The cgroup v2 id of a given path in the hierarchy is + * exposed in user space through the f_handle API in order to get + * to the same 64-bit id. + * + * This helper can be used on TC egress path, but not on ingress, + * and is available only if the kernel was compiled with the + * **CONFIG_SOCK_CGROUP_DATA** configuration option. + * Return + * The id is returned or 0 in case the id could not be retrieved. + * + * u64 bpf_get_current_cgroup_id(void) + * Return + * A 64-bit integer containing the current cgroup id based + * on the cgroup within which the current task is running. + * + * void *bpf_get_local_storage(void *map, u64 flags) + * Description + * Get the pointer to the local storage area. + * The type and the size of the local storage is defined + * by the *map* argument. + * The *flags* meaning is specific for each map type, + * and has to be 0 for cgroup local storage. + * + * Depending on the BPF program type, a local storage area + * can be shared between multiple instances of the BPF program, + * running simultaneously. + * + * A user should care about the synchronization by himself. + * For example, by using the **BPF_STX_XADD** instruction to alter + * the shared data. + * Return + * A pointer to the local storage area. + * + * long bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) + * Description + * Select a **SO_REUSEPORT** socket from a + * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*. + * It checks the selected socket is matching the incoming + * request in the socket buffer. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level) + * Description + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *skb* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *skb*, then return value will be same as that + * of **bpf_skb_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *skb*. + * + * The format of returned id and helper limitations are same as in + * **bpf_skb_cgroup_id**\ (). + * Return + * The id is returned or 0 in case the id could not be retrieved. + * + * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) + * Description + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * Return + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + * + * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) + * Description + * Look for UDP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * Return + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + * + * long bpf_sk_release(struct bpf_sock *sock) + * Description + * Release the reference held by *sock*. *sock* must be a + * non-**NULL** pointer that was returned from + * **bpf_sk_lookup_xxx**\ (). + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags) + * Description + * Push an element *value* in *map*. *flags* is one of: + * + * **BPF_EXIST** + * If the queue/stack is full, the oldest element is + * removed to make room for this. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_map_pop_elem(struct bpf_map *map, void *value) + * Description + * Pop an element from *map*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_map_peek_elem(struct bpf_map *map, void *value) + * Description + * Get an element from *map* without removing it. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_msg_push_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) + * Description + * For socket policies, insert *len* bytes into *msg* at offset + * *start*. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it may want to insert metadata or options into the *msg*. + * This can later be read and used by any of the lower layer BPF + * hooks. + * + * This helper may fail if under memory pressure (a malloc + * fails) in these cases BPF programs will get an appropriate + * error and BPF programs will need to handle them. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags) + * Description + * Will remove *len* bytes from a *msg* starting at byte *start*. + * This may result in **ENOMEM** errors under certain situations if + * an allocation and copy are required due to a full ring buffer. + * However, the helper will try to avoid doing the allocation + * if possible. Other errors can occur if input parameters are + * invalid either due to *start* byte not being valid part of *msg* + * payload and/or *pop* value being to large. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y) + * Description + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded pointer movement. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * Return + * 0 + * + * long bpf_spin_lock(struct bpf_spin_lock *lock) + * Description + * Acquire a spinlock represented by the pointer *lock*, which is + * stored as part of a value of a map. Taking the lock allows to + * safely update the rest of the fields in that value. The + * spinlock can (and must) later be released with a call to + * **bpf_spin_unlock**\ (\ *lock*\ ). + * + * Spinlocks in BPF programs come with a number of restrictions + * and constraints: + * + * * **bpf_spin_lock** objects are only allowed inside maps of + * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this + * list could be extended in the future). + * * BTF description of the map is mandatory. + * * The BPF program can take ONE lock at a time, since taking two + * or more could cause dead locks. + * * Only one **struct bpf_spin_lock** is allowed per map element. + * * When the lock is taken, calls (either BPF to BPF or helpers) + * are not allowed. + * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not + * allowed inside a spinlock-ed region. + * * The BPF program MUST call **bpf_spin_unlock**\ () to release + * the lock, on all execution paths, before it returns. + * * The BPF program can access **struct bpf_spin_lock** only via + * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () + * helpers. Loading or storing data into the **struct + * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. + * * To use the **bpf_spin_lock**\ () helper, the BTF description + * of the map value must be a struct and have **struct + * bpf_spin_lock** *anyname*\ **;** field at the top level. + * Nested lock inside another struct is not allowed. + * * The **struct bpf_spin_lock** *lock* field in a map value must + * be aligned on a multiple of 4 bytes in that value. + * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy + * the **bpf_spin_lock** field to user space. + * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from + * a BPF program, do not update the **bpf_spin_lock** field. + * * **bpf_spin_lock** cannot be on the stack or inside a + * networking packet (it can only be inside of a map values). + * * **bpf_spin_lock** is available to root only. + * * Tracing programs and socket filter programs cannot use + * **bpf_spin_lock**\ () due to insufficient preemption checks + * (but this may change in the future). + * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. + * Return + * 0 + * + * long bpf_spin_unlock(struct bpf_spin_lock *lock) + * Description + * Release the *lock* previously locked by a call to + * **bpf_spin_lock**\ (\ *lock*\ ). + * Return + * 0 + * + * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk) + * Description + * This helper gets a **struct bpf_sock** pointer such + * that all the fields in this **bpf_sock** can be accessed. + * Return + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + * + * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk) + * Description + * This helper gets a **struct bpf_tcp_sock** pointer from a + * **struct bpf_sock** pointer. + * Return + * A **struct bpf_tcp_sock** pointer on success, or **NULL** in + * case of failure. + * + * long bpf_skb_ecn_set_ce(struct sk_buff *skb) + * Description + * Set ECN (Explicit Congestion Notification) field of IP header + * to **CE** (Congestion Encountered) if current value is **ECT** + * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 + * and IPv4. + * Return + * 1 if the **CE** flag is set (either by the current helper call + * or because it was already present), 0 if it is not set. + * + * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk) + * Description + * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. + * **bpf_sk_release**\ () is unnecessary and not allowed. + * Return + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + * + * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags) + * Description + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * This function is identical to **bpf_sk_lookup_tcp**\ (), except + * that it also returns timewait or request sockets. Use + * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the + * full structure. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * Return + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + * + * long bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) + * Description + * Check whether *iph* and *th* contain a valid SYN cookie ACK for + * the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains **sizeof**\ (**struct tcphdr**). + * Return + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + * + * long bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags) + * Description + * Get name of sysctl in /proc/sys/ and copy it into provided by + * program buffer *buf* of size *buf_len*. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is + * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name + * only (e.g. "tcp_mem"). + * Return + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * long bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) + * Description + * Get current value of sysctl as it is presented in /proc/sys + * (incl. newline, etc), and copy it as a string into provided + * by program buffer *buf* of size *buf_len*. + * + * The whole value is copied, no matter what file position user + * space issued e.g. sys_read at. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * Return + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if current value was unavailable, e.g. because + * sysctl is uninitialized and read returns -EIO for it. + * + * long bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len) + * Description + * Get new value being written by user space to sysctl (before + * the actual write happens) and copy it as a string into + * provided by program buffer *buf* of size *buf_len*. + * + * User space may write new value at file position > 0. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * Return + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if sysctl is being read. + * + * long bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len) + * Description + * Override new value being written by user space to sysctl with + * value provided by program in buffer *buf* of size *buf_len*. + * + * *buf* should contain a string in same form as provided by user + * space on sysctl write. + * + * User space may write new value at file position > 0. To override + * the whole sysctl value file position should be set to zero. + * Return + * 0 on success. + * + * **-E2BIG** if the *buf_len* is too big. + * + * **-EINVAL** if sysctl is being read. + * + * long bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res) + * Description + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to a long integer according to the given base + * and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)) followed by a single + * optional '**-**' sign. + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtol**\ (3). + * Return + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + * + * long bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res) + * Description + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to an unsigned long integer according to the + * given base and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)). + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtoul**\ (3). + * Return + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + * + * void *bpf_sk_storage_get(struct bpf_map *map, struct bpf_sock *sk, void *value, u64 flags) + * Description + * Get a bpf-local-storage from a *sk*. + * + * Logically, it could be thought of getting the value from + * a *map* with *sk* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this + * helper enforces the key must be a full socket and the map must + * be a **BPF_MAP_TYPE_SK_STORAGE** also. + * + * Underneath, the value is stored locally at *sk* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf-local-storages residing at *sk*. + * + * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf-local-storage will be + * created if one does not exist. *value* can be used + * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf-local-storage. If *value* is + * **NULL**, the new bpf-local-storage will be zero initialized. + * Return + * A bpf-local-storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf-local-storage. + * + * long bpf_sk_storage_delete(struct bpf_map *map, struct bpf_sock *sk) + * Description + * Delete a bpf-local-storage from a *sk*. + * Return + * 0 on success. + * + * **-ENOENT** if the bpf-local-storage cannot be found. + * + * long bpf_send_signal(u32 sig) + * Description + * Send signal *sig* to the process of the current task. + * The signal may be delivered to any of this process's threads. + * Return + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + * + * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len) + * Description + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header. + * Return + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 + * + * long bpf_skb_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) + * Description + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct sk_buff. + * + * This helper is similar to **bpf_perf_event_output**\ () but + * restricted to raw_tracepoint bpf programs. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr) + * Description + * Safely attempt to read *size* bytes from user space address + * *unsafe_ptr* and store the data in *dst*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr) + * Description + * Safely attempt to read *size* bytes from kernel space address + * *unsafe_ptr* and store the data in *dst*. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr) + * Description + * Copy a NUL terminated string from an unsafe user address + * *unsafe_ptr* to *dst*. The *size* should include the + * terminating NUL byte. In case the string length is smaller than + * *size*, the target is not padded with further NUL bytes. If the + * string length is larger than *size*, just *size*-1 bytes are + * copied and the last byte is set to NUL. + * + * On success, the length of the copied string is returned. This + * makes this helper useful in tracing programs for reading + * strings, and more importantly to get its length at runtime. See + * the following snippet: + * + * :: + * + * SEC("kprobe/sys_open") + * void bpf_sys_open(struct pt_regs *ctx) + * { + * char buf[PATHLEN]; // PATHLEN is defined to 256 + * int res = bpf_probe_read_user_str(buf, sizeof(buf), + * ctx->di); + * + * // Consume buf, for example push it to + * // userspace via bpf_perf_event_output(); we + * // can use res (the string length) as event + * // size, after checking its boundaries. + * } + * + * In comparison, using **bpf_probe_read_user**\ () helper here + * instead to read the string would require to estimate the length + * at compile time, and would often result in copying more memory + * than necessary. + * + * Another useful use case is when parsing individual process + * arguments or individual environment variables navigating + * *current*\ **->mm->arg_start** and *current*\ + * **->mm->env_start**: using this helper and the return value, + * one can quickly iterate at the right offset of the memory area. + * Return + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + * + * long bpf_probe_read_kernel_str(void *dst, u32 size, const void *unsafe_ptr) + * Description + * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* + * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. + * Return + * On success, the strictly positive length of the string, including + * the trailing NUL character. On error, a negative value. + * + * long bpf_tcp_send_ack(void *tp, u32 rcv_nxt) + * Description + * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. + * *rcv_nxt* is the ack_seq to be sent out. + * Return + * 0 on success, or a negative error in case of failure. + * + * long bpf_send_signal_thread(u32 sig) + * Description + * Send signal *sig* to the thread corresponding to the current task. + * Return + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + * + * u64 bpf_jiffies64(void) + * Description + * Obtain the 64bit jiffies + * Return + * The 64 bit jiffies + * + * long bpf_read_branch_records(struct bpf_perf_event_data *ctx, void *buf, u32 size, u64 flags) + * Description + * For an eBPF program attached to a perf event, retrieve the + * branch records (**struct perf_branch_entry**) associated to *ctx* + * and store it in the buffer pointed by *buf* up to size + * *size* bytes. + * Return + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to + * instead return the number of bytes required to store all the + * branch entries. If this flag is set, *buf* may be NULL. + * + * **-EINVAL** if arguments invalid or **size** not a multiple + * of **sizeof**\ (**struct perf_branch_entry**\ ). + * + * **-ENOENT** if architecture does not support branch records. + * + * long bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size) + * Description + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * Return + * 0 on success, or one of the following in case of failure: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + * + * long bpf_xdp_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) + * Description + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * Return + * 0 on success, or a negative error in case of failure. + * + * u64 bpf_get_netns_cookie(void *ctx) + * Description + * Retrieve the cookie (generated by the kernel) of the network + * namespace the input *ctx* is associated with. The network + * namespace cookie remains stable for its lifetime and provides + * a global identifier that can be assumed unique. If *ctx* is + * NULL, then the helper returns the cookie for the initial + * network namespace. The cookie itself is very similar to that + * of **bpf_get_socket_cookie**\ () helper, but for network + * namespaces instead of sockets. + * Return + * A 8-byte long opaque number. + * + * u64 bpf_get_current_ancestor_cgroup_id(int ancestor_level) + * Description + * Return id of cgroup v2 that is ancestor of the cgroup associated + * with the current task at the *ancestor_level*. The root cgroup + * is at *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with the current task, then return value will be the + * same as that of **bpf_get_current_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with the current task. + * + * The format of returned id and helper limitations are same as in + * **bpf_get_current_cgroup_id**\ (). + * Return + * The id is returned or 0 in case the id could not be retrieved. + * + * long bpf_sk_assign(struct sk_buff *skb, struct bpf_sock *sk, u64 flags) + * Description + * Helper is overloaded depending on BPF program type. This + * description applies to **BPF_PROG_TYPE_SCHED_CLS** and + * **BPF_PROG_TYPE_SCHED_ACT** programs. + * + * Assign the *sk* to the *skb*. When combined with appropriate + * routing configuration to receive the packet towards the socket, + * will cause *skb* to be delivered to the specified socket. + * Subsequent redirection of *skb* via **bpf_redirect**\ (), + * **bpf_clone_redirect**\ () or other methods outside of BPF may + * interfere with successful delivery to the socket. + * + * This operation is only valid from TC ingress path. + * + * The *flags* argument must be zero. + * Return + * 0 on success, or a negative error in case of failure: + * + * **-EINVAL** if specified *flags* are not supported. + * + * **-ENOENT** if the socket is unavailable for assignment. + * + * **-ENETUNREACH** if the socket is unreachable (wrong netns). + * + * **-EOPNOTSUPP** if the operation is not supported, for example + * a call from outside of TC ingress. + * + * **-ESOCKTNOSUPPORT** if the socket type is not supported + * (reuseport). + * + * long bpf_sk_assign(struct bpf_sk_lookup *ctx, struct bpf_sock *sk, u64 flags) + * Description + * Helper is overloaded depending on BPF program type. This + * description applies to **BPF_PROG_TYPE_SK_LOOKUP** programs. + * + * Select the *sk* as a result of a socket lookup. + * + * For the operation to succeed passed socket must be compatible + * with the packet description provided by the *ctx* object. + * + * L4 protocol (**IPPROTO_TCP** or **IPPROTO_UDP**) must + * be an exact match. While IP family (**AF_INET** or + * **AF_INET6**) must be compatible, that is IPv6 sockets + * that are not v6-only can be selected for IPv4 packets. + * + * Only TCP listeners and UDP unconnected sockets can be + * selected. *sk* can also be NULL to reset any previous + * selection. + * + * *flags* argument can combination of following values: + * + * * **BPF_SK_LOOKUP_F_REPLACE** to override the previous + * socket selection, potentially done by a BPF program + * that ran before us. + * + * * **BPF_SK_LOOKUP_F_NO_REUSEPORT** to skip + * load-balancing within reuseport group for the socket + * being selected. + * + * On success *ctx->sk* will point to the selected socket. + * + * Return + * 0 on success, or a negative errno in case of failure. + * + * * **-EAFNOSUPPORT** if socket family (*sk->family*) is + * not compatible with packet family (*ctx->family*). + * + * * **-EEXIST** if socket has been already selected, + * potentially by another program, and + * **BPF_SK_LOOKUP_F_REPLACE** flag was not specified. + * + * * **-EINVAL** if unsupported flags were specified. + * + * * **-EPROTOTYPE** if socket L4 protocol + * (*sk->protocol*) doesn't match packet protocol + * (*ctx->protocol*). + * + * * **-ESOCKTNOSUPPORT** if socket is not in allowed + * state (TCP listening or UDP unconnected). + * + * u64 bpf_ktime_get_boot_ns(void) + * Description + * Return the time elapsed since system boot, in nanoseconds. + * Does include the time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) + * Return + * Current *ktime*. + * + * long bpf_seq_printf(struct seq_file *m, const char *fmt, u32 fmt_size, const void *data, u32 data_len) + * Description + * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print + * out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a **u64** array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the size of *data* in bytes. + * + * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. + * Reading kernel memory may fail due to either invalid address or + * valid address but requiring a major memory fault. If reading kernel memory + * fails, the string for **%s** will be an empty string, and the ip + * address for **%p{i,I}{4,6}** will be 0. Not returning error to + * bpf program is consistent with what **bpf_trace_printk**\ () does for now. + * Return + * 0 on success, or a negative error in case of failure: + * + * **-EBUSY** if per-CPU memory copy buffer is busy, can try again + * by returning 1 from bpf program. + * + * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. + * + * **-E2BIG** if *fmt* contains too many format specifiers. + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + * + * long bpf_seq_write(struct seq_file *m, const void *data, u32 len) + * Description + * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * Return + * 0 on success, or a negative error in case of failure: + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + * + * u64 bpf_sk_cgroup_id(struct bpf_sock *sk) + * Description + * Return the cgroup v2 id of the socket *sk*. + * + * *sk* must be a non-**NULL** pointer to a full socket, e.g. one + * returned from **bpf_sk_lookup_xxx**\ (), + * **bpf_sk_fullsock**\ (), etc. The format of returned id is + * same as in **bpf_skb_cgroup_id**\ (). + * + * This helper is available only if the kernel was compiled with + * the **CONFIG_SOCK_CGROUP_DATA** configuration option. + * Return + * The id is returned or 0 in case the id could not be retrieved. + * + * u64 bpf_sk_ancestor_cgroup_id(struct bpf_sock *sk, int ancestor_level) + * Description + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *sk* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *sk*, then return value will be same as that + * of **bpf_sk_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *sk*. + * + * The format of returned id and helper limitations are same as in + * **bpf_sk_cgroup_id**\ (). + * Return + * The id is returned or 0 in case the id could not be retrieved. + * + * long bpf_ringbuf_output(void *ringbuf, void *data, u64 size, u64 flags) + * Description + * Copy *size* bytes from *data* into a ring buffer *ringbuf*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * Return + * 0 on success, or a negative error in case of failure. + * + * void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags) + * Description + * Reserve *size* bytes of payload in a ring buffer *ringbuf*. + * Return + * Valid pointer with *size* bytes of memory available; NULL, + * otherwise. + * + * void bpf_ringbuf_submit(void *data, u64 flags) + * Description + * Submit reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * Return + * Nothing. Always succeeds. + * + * void bpf_ringbuf_discard(void *data, u64 flags) + * Description + * Discard reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * Return + * Nothing. Always succeeds. + * + * u64 bpf_ringbuf_query(void *ringbuf, u64 flags) + * Description + * Query various characteristics of provided ring buffer. What + * exactly is queries is determined by *flags*: + * + * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. + * * **BPF_RB_RING_SIZE**: The size of ring buffer. + * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). + * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). + * + * Data returned is just a momentary snapshot of actual values + * and could be inaccurate, so this facility should be used to + * power heuristics and for reporting, not to make 100% correct + * calculation. + * Return + * Requested value, or 0, if *flags* are not recognized. + * + * long bpf_csum_level(struct sk_buff *skb, u64 level) + * Description + * Change the skbs checksum level by one layer up or down, or + * reset it entirely to none in order to have the stack perform + * checksum validation. The level is applicable to the following + * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of + * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | + * through **bpf_skb_adjust_room**\ () helper with passing in + * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call + * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since + * the UDP header is removed. Similarly, an encap of the latter + * into the former could be accompanied by a helper call to + * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the + * skb is still intended to be processed in higher layers of the + * stack instead of just egressing at tc. + * + * There are three supported level settings at this time: + * + * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and + * sets CHECKSUM_NONE to force checksum validation by the stack. + * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current + * skb->csum_level. + * Return + * 0 on success, or a negative error in case of failure. In the + * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level + * is returned or the error code -EACCES in case the skb is not + * subject to CHECKSUM_UNNECESSARY. + * + * struct tcp6_sock *bpf_skc_to_tcp6_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. + * Return + * *sk* if casting is valid, or NULL otherwise. + * + * struct tcp_sock *bpf_skc_to_tcp_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. + * Return + * *sk* if casting is valid, or NULL otherwise. + * + * struct tcp_timewait_sock *bpf_skc_to_tcp_timewait_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. + * Return + * *sk* if casting is valid, or NULL otherwise. + * + * struct tcp_request_sock *bpf_skc_to_tcp_request_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. + * Return + * *sk* if casting is valid, or NULL otherwise. + * + * struct udp6_sock *bpf_skc_to_udp6_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. + * Return + * *sk* if casting is valid, or NULL otherwise. + * + * long bpf_get_task_stack(struct task_struct *task, void *buf, u32 size, u64 flags) + * Description + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *task*, which is a valid + * pointer to struct task_struct. To store the stacktrace, the + * bpf program provides *buf* with a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_task_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * Return + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + * + */ +#define __BPF_FUNC_MAPPER(FN) \ + FN(unspec), \ + FN(map_lookup_elem), \ + FN(map_update_elem), \ + FN(map_delete_elem), \ + FN(probe_read), \ + FN(ktime_get_ns), \ + FN(trace_printk), \ + FN(get_prandom_u32), \ + FN(get_smp_processor_id), \ + FN(skb_store_bytes), \ + FN(l3_csum_replace), \ + FN(l4_csum_replace), \ + FN(tail_call), \ + FN(clone_redirect), \ + FN(get_current_pid_tgid), \ + FN(get_current_uid_gid), \ + FN(get_current_comm), \ + FN(get_cgroup_classid), \ + FN(skb_vlan_push), \ + FN(skb_vlan_pop), \ + FN(skb_get_tunnel_key), \ + FN(skb_set_tunnel_key), \ + FN(perf_event_read), \ + FN(redirect), \ + FN(get_route_realm), \ + FN(perf_event_output), \ + FN(skb_load_bytes), \ + FN(get_stackid), \ + FN(csum_diff), \ + FN(skb_get_tunnel_opt), \ + FN(skb_set_tunnel_opt), \ + FN(skb_change_proto), \ + FN(skb_change_type), \ + FN(skb_under_cgroup), \ + FN(get_hash_recalc), \ + FN(get_current_task), \ + FN(probe_write_user), \ + FN(current_task_under_cgroup), \ + FN(skb_change_tail), \ + FN(skb_pull_data), \ + FN(csum_update), \ + FN(set_hash_invalid), \ + FN(get_numa_node_id), \ + FN(skb_change_head), \ + FN(xdp_adjust_head), \ + FN(probe_read_str), \ + FN(get_socket_cookie), \ + FN(get_socket_uid), \ + FN(set_hash), \ + FN(setsockopt), \ + FN(skb_adjust_room), \ + FN(redirect_map), \ + FN(sk_redirect_map), \ + FN(sock_map_update), \ + FN(xdp_adjust_meta), \ + FN(perf_event_read_value), \ + FN(perf_prog_read_value), \ + FN(getsockopt), \ + FN(override_return), \ + FN(sock_ops_cb_flags_set), \ + FN(msg_redirect_map), \ + FN(msg_apply_bytes), \ + FN(msg_cork_bytes), \ + FN(msg_pull_data), \ + FN(bind), \ + FN(xdp_adjust_tail), \ + FN(skb_get_xfrm_state), \ + FN(get_stack), \ + FN(skb_load_bytes_relative), \ + FN(fib_lookup), \ + FN(sock_hash_update), \ + FN(msg_redirect_hash), \ + FN(sk_redirect_hash), \ + FN(lwt_push_encap), \ + FN(lwt_seg6_store_bytes), \ + FN(lwt_seg6_adjust_srh), \ + FN(lwt_seg6_action), \ + FN(rc_repeat), \ + FN(rc_keydown), \ + FN(skb_cgroup_id), \ + FN(get_current_cgroup_id), \ + FN(get_local_storage), \ + FN(sk_select_reuseport), \ + FN(skb_ancestor_cgroup_id), \ + FN(sk_lookup_tcp), \ + FN(sk_lookup_udp), \ + FN(sk_release), \ + FN(map_push_elem), \ + FN(map_pop_elem), \ + FN(map_peek_elem), \ + FN(msg_push_data), \ + FN(msg_pop_data), \ + FN(rc_pointer_rel), \ + FN(spin_lock), \ + FN(spin_unlock), \ + FN(sk_fullsock), \ + FN(tcp_sock), \ + FN(skb_ecn_set_ce), \ + FN(get_listener_sock), \ + FN(skc_lookup_tcp), \ + FN(tcp_check_syncookie), \ + FN(sysctl_get_name), \ + FN(sysctl_get_current_value), \ + FN(sysctl_get_new_value), \ + FN(sysctl_set_new_value), \ + FN(strtol), \ + FN(strtoul), \ + FN(sk_storage_get), \ + FN(sk_storage_delete), \ + FN(send_signal), \ + FN(tcp_gen_syncookie), \ + FN(skb_output), \ + FN(probe_read_user), \ + FN(probe_read_kernel), \ + FN(probe_read_user_str), \ + FN(probe_read_kernel_str), \ + FN(tcp_send_ack), \ + FN(send_signal_thread), \ + FN(jiffies64), \ + FN(read_branch_records), \ + FN(get_ns_current_pid_tgid), \ + FN(xdp_output), \ + FN(get_netns_cookie), \ + FN(get_current_ancestor_cgroup_id), \ + FN(sk_assign), \ + FN(ktime_get_boot_ns), \ + FN(seq_printf), \ + FN(seq_write), \ + FN(sk_cgroup_id), \ + FN(sk_ancestor_cgroup_id), \ + FN(ringbuf_output), \ + FN(ringbuf_reserve), \ + FN(ringbuf_submit), \ + FN(ringbuf_discard), \ + FN(ringbuf_query), \ + FN(csum_level), \ + FN(skc_to_tcp6_sock), \ + FN(skc_to_tcp_sock), \ + FN(skc_to_tcp_timewait_sock), \ + FN(skc_to_tcp_request_sock), \ + FN(skc_to_udp6_sock), \ + FN(get_task_stack), \ + /* */ + +/* integer value in 'imm' field of BPF_CALL instruction selects which helper + * function eBPF program intends to call + */ +#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x +enum bpf_func_id { + __BPF_FUNC_MAPPER(__BPF_ENUM_FN) + __BPF_FUNC_MAX_ID, +}; +#undef __BPF_ENUM_FN + +/* All flags used by eBPF helper functions, placed here. */ + +/* BPF_FUNC_skb_store_bytes flags. */ +enum { + BPF_F_RECOMPUTE_CSUM = (1ULL << 0), + BPF_F_INVALIDATE_HASH = (1ULL << 1), +}; + +/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. + * First 4 bits are for passing the header field size. + */ +enum { + BPF_F_HDR_FIELD_MASK = 0xfULL, +}; + +/* BPF_FUNC_l4_csum_replace flags. */ +enum { + BPF_F_PSEUDO_HDR = (1ULL << 4), + BPF_F_MARK_MANGLED_0 = (1ULL << 5), + BPF_F_MARK_ENFORCE = (1ULL << 6), +}; + +/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ +enum { + BPF_F_INGRESS = (1ULL << 0), +}; + +/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ +enum { + BPF_F_TUNINFO_IPV6 = (1ULL << 0), +}; + +/* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ +enum { + BPF_F_SKIP_FIELD_MASK = 0xffULL, + BPF_F_USER_STACK = (1ULL << 8), +/* flags used by BPF_FUNC_get_stackid only. */ + BPF_F_FAST_STACK_CMP = (1ULL << 9), + BPF_F_REUSE_STACKID = (1ULL << 10), +/* flags used by BPF_FUNC_get_stack only. */ + BPF_F_USER_BUILD_ID = (1ULL << 11), +}; + +/* BPF_FUNC_skb_set_tunnel_key flags. */ +enum { + BPF_F_ZERO_CSUM_TX = (1ULL << 1), + BPF_F_DONT_FRAGMENT = (1ULL << 2), + BPF_F_SEQ_NUMBER = (1ULL << 3), +}; + +/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and + * BPF_FUNC_perf_event_read_value flags. + */ +enum { + BPF_F_INDEX_MASK = 0xffffffffULL, + BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK, +/* BPF_FUNC_perf_event_output for sk_buff input context. */ + BPF_F_CTXLEN_MASK = (0xfffffULL << 32), +}; + +/* Current network namespace */ +enum { + BPF_F_CURRENT_NETNS = (-1L), +}; + +/* BPF_FUNC_csum_level level values. */ +enum { + BPF_CSUM_LEVEL_QUERY, + BPF_CSUM_LEVEL_INC, + BPF_CSUM_LEVEL_DEC, + BPF_CSUM_LEVEL_RESET, +}; + +/* BPF_FUNC_skb_adjust_room flags. */ +enum { + BPF_F_ADJ_ROOM_FIXED_GSO = (1ULL << 0), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = (1ULL << 1), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = (1ULL << 2), + BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3), + BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4), + BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5), +}; + +enum { + BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff, + BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56, +}; + +#define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \ + BPF_ADJ_ROOM_ENCAP_L2_MASK) \ + << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) + +/* BPF_FUNC_sysctl_get_name flags. */ +enum { + BPF_F_SYSCTL_BASE_NAME = (1ULL << 0), +}; + +/* BPF_FUNC_sk_storage_get flags */ +enum { + BPF_SK_STORAGE_GET_F_CREATE = (1ULL << 0), +}; + +/* BPF_FUNC_read_branch_records flags. */ +enum { + BPF_F_GET_BRANCH_RECORDS_SIZE = (1ULL << 0), +}; + +/* BPF_FUNC_bpf_ringbuf_commit, BPF_FUNC_bpf_ringbuf_discard, and + * BPF_FUNC_bpf_ringbuf_output flags. + */ +enum { + BPF_RB_NO_WAKEUP = (1ULL << 0), + BPF_RB_FORCE_WAKEUP = (1ULL << 1), +}; + +/* BPF_FUNC_bpf_ringbuf_query flags */ +enum { + BPF_RB_AVAIL_DATA = 0, + BPF_RB_RING_SIZE = 1, + BPF_RB_CONS_POS = 2, + BPF_RB_PROD_POS = 3, +}; + +/* BPF ring buffer constants */ +enum { + BPF_RINGBUF_BUSY_BIT = (1U << 31), + BPF_RINGBUF_DISCARD_BIT = (1U << 30), + BPF_RINGBUF_HDR_SZ = 8, +}; + +/* BPF_FUNC_sk_assign flags in bpf_sk_lookup context. */ +enum { + BPF_SK_LOOKUP_F_REPLACE = (1ULL << 0), + BPF_SK_LOOKUP_F_NO_REUSEPORT = (1ULL << 1), +}; + +/* Mode for BPF_FUNC_skb_adjust_room helper. */ +enum bpf_adj_room_mode { + BPF_ADJ_ROOM_NET, + BPF_ADJ_ROOM_MAC, +}; + +/* Mode for BPF_FUNC_skb_load_bytes_relative helper. */ +enum bpf_hdr_start_off { + BPF_HDR_START_MAC, + BPF_HDR_START_NET, +}; + +/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */ +enum bpf_lwt_encap_mode { + BPF_LWT_ENCAP_SEG6, + BPF_LWT_ENCAP_SEG6_INLINE, + BPF_LWT_ENCAP_IP, +}; + +#define __bpf_md_ptr(type, name) \ +union { \ + type name; \ + __u64 :64; \ +} __attribute__((aligned(8))) + +/* user accessible mirror of in-kernel sk_buff. + * new fields can only be added to the end of this structure + */ +struct __sk_buff { + __u32 len; + __u32 pkt_type; + __u32 mark; + __u32 queue_mapping; + __u32 protocol; + __u32 vlan_present; + __u32 vlan_tci; + __u32 vlan_proto; + __u32 priority; + __u32 ingress_ifindex; + __u32 ifindex; + __u32 tc_index; + __u32 cb[5]; + __u32 hash; + __u32 tc_classid; + __u32 data; + __u32 data_end; + __u32 napi_id; + + /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ + __u32 family; + __u32 remote_ip4; /* Stored in network byte order */ + __u32 local_ip4; /* Stored in network byte order */ + __u32 remote_ip6[4]; /* Stored in network byte order */ + __u32 local_ip6[4]; /* Stored in network byte order */ + __u32 remote_port; /* Stored in network byte order */ + __u32 local_port; /* stored in host byte order */ + /* ... here. */ + + __u32 data_meta; + __bpf_md_ptr(struct bpf_flow_keys *, flow_keys); + __u64 tstamp; + __u32 wire_len; + __u32 gso_segs; + __bpf_md_ptr(struct bpf_sock *, sk); + __u32 gso_size; +}; + +struct bpf_tunnel_key { + __u32 tunnel_id; + union { + __u32 remote_ipv4; + __u32 remote_ipv6[4]; + }; + __u8 tunnel_tos; + __u8 tunnel_ttl; + __u16 tunnel_ext; /* Padding, future use. */ + __u32 tunnel_label; +}; + +/* user accessible mirror of in-kernel xfrm_state. + * new fields can only be added to the end of this structure + */ +struct bpf_xfrm_state { + __u32 reqid; + __u32 spi; /* Stored in network byte order */ + __u16 family; + __u16 ext; /* Padding, future use. */ + union { + __u32 remote_ipv4; /* Stored in network byte order */ + __u32 remote_ipv6[4]; /* Stored in network byte order */ + }; +}; + +/* Generic BPF return codes which all BPF program types may support. + * The values are binary compatible with their TC_ACT_* counter-part to + * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT + * programs. + * + * XDP is handled seprately, see XDP_*. + */ +enum bpf_ret_code { + BPF_OK = 0, + /* 1 reserved */ + BPF_DROP = 2, + /* 3-6 reserved */ + BPF_REDIRECT = 7, + /* >127 are reserved for prog type specific return codes. + * + * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and + * BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been + * changed and should be routed based on its new L3 header. + * (This is an L3 redirect, as opposed to L2 redirect + * represented by BPF_REDIRECT above). + */ + BPF_LWT_REROUTE = 128, +}; + +struct bpf_sock { + __u32 bound_dev_if; + __u32 family; + __u32 type; + __u32 protocol; + __u32 mark; + __u32 priority; + /* IP address also allows 1 and 2 bytes access */ + __u32 src_ip4; + __u32 src_ip6[4]; + __u32 src_port; /* host byte order */ + __u32 dst_port; /* network byte order */ + __u32 dst_ip4; + __u32 dst_ip6[4]; + __u32 state; + __s32 rx_queue_mapping; +}; + +struct bpf_tcp_sock { + __u32 snd_cwnd; /* Sending congestion window */ + __u32 srtt_us; /* smoothed round trip time << 3 in usecs */ + __u32 rtt_min; + __u32 snd_ssthresh; /* Slow start size threshold */ + __u32 rcv_nxt; /* What we want to receive next */ + __u32 snd_nxt; /* Next sequence we send */ + __u32 snd_una; /* First byte we want an ack for */ + __u32 mss_cache; /* Cached effective mss, not including SACKS */ + __u32 ecn_flags; /* ECN status bits. */ + __u32 rate_delivered; /* saved rate sample: packets delivered */ + __u32 rate_interval_us; /* saved rate sample: time elapsed */ + __u32 packets_out; /* Packets which are "in flight" */ + __u32 retrans_out; /* Retransmitted packets out */ + __u32 total_retrans; /* Total retransmits for entire connection */ + __u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn + * total number of segments in. + */ + __u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn + * total number of data segments in. + */ + __u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut + * The total number of segments sent. + */ + __u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut + * total number of data segments sent. + */ + __u32 lost_out; /* Lost packets */ + __u32 sacked_out; /* SACK'd packets */ + __u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived + * sum(delta(rcv_nxt)), or how many bytes + * were acked. + */ + __u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked + * sum(delta(snd_una)), or how many bytes + * were acked. + */ + __u32 dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups + * total number of DSACK blocks received + */ + __u32 delivered; /* Total data packets delivered incl. rexmits */ + __u32 delivered_ce; /* Like the above but only ECE marked packets */ + __u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */ +}; + +struct bpf_sock_tuple { + union { + struct { + __be32 saddr; + __be32 daddr; + __be16 sport; + __be16 dport; + } ipv4; + struct { + __be32 saddr[4]; + __be32 daddr[4]; + __be16 sport; + __be16 dport; + } ipv6; + }; +}; + +struct bpf_xdp_sock { + __u32 queue_id; +}; + +#define XDP_PACKET_HEADROOM 256 + +/* User return codes for XDP prog type. + * A valid XDP program must return one of these defined values. All other + * return codes are reserved for future use. Unknown return codes will + * result in packet drops and a warning via bpf_warn_invalid_xdp_action(). + */ +enum xdp_action { + XDP_ABORTED = 0, + XDP_DROP, + XDP_PASS, + XDP_TX, + XDP_REDIRECT, +}; + +/* user accessible metadata for XDP packet hook + * new fields must be added to the end of this structure + */ +struct xdp_md { + __u32 data; + __u32 data_end; + __u32 data_meta; + /* Below access go through struct xdp_rxq_info */ + __u32 ingress_ifindex; /* rxq->dev->ifindex */ + __u32 rx_queue_index; /* rxq->queue_index */ + + __u32 egress_ifindex; /* txq->dev->ifindex */ +}; + +/* DEVMAP map-value layout + * + * The struct data-layout of map-value is a configuration interface. + * New members can only be added to the end of this structure. + */ +struct bpf_devmap_val { + __u32 ifindex; /* device index */ + union { + int fd; /* prog fd on map write */ + __u32 id; /* prog id on map read */ + } bpf_prog; +}; + +/* CPUMAP map-value layout + * + * The struct data-layout of map-value is a configuration interface. + * New members can only be added to the end of this structure. + */ +struct bpf_cpumap_val { + __u32 qsize; /* queue size to remote target CPU */ + union { + int fd; /* prog fd on map write */ + __u32 id; /* prog id on map read */ + } bpf_prog; +}; + +enum sk_action { + SK_DROP = 0, + SK_PASS, +}; + +/* user accessible metadata for SK_MSG packet hook, new fields must + * be added to the end of this structure + */ +struct sk_msg_md { + __bpf_md_ptr(void *, data); + __bpf_md_ptr(void *, data_end); + + __u32 family; + __u32 remote_ip4; /* Stored in network byte order */ + __u32 local_ip4; /* Stored in network byte order */ + __u32 remote_ip6[4]; /* Stored in network byte order */ + __u32 local_ip6[4]; /* Stored in network byte order */ + __u32 remote_port; /* Stored in network byte order */ + __u32 local_port; /* stored in host byte order */ + __u32 size; /* Total size of sk_msg */ + + __bpf_md_ptr(struct bpf_sock *, sk); /* current socket */ +}; + +struct sk_reuseport_md { + /* + * Start of directly accessible data. It begins from + * the tcp/udp header. + */ + __bpf_md_ptr(void *, data); + /* End of directly accessible data */ + __bpf_md_ptr(void *, data_end); + /* + * Total length of packet (starting from the tcp/udp header). + * Note that the directly accessible bytes (data_end - data) + * could be less than this "len". Those bytes could be + * indirectly read by a helper "bpf_skb_load_bytes()". + */ + __u32 len; + /* + * Eth protocol in the mac header (network byte order). e.g. + * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD) + */ + __u32 eth_protocol; + __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ + __u32 bind_inany; /* Is sock bound to an INANY address? */ + __u32 hash; /* A hash of the packet 4 tuples */ +}; + +#define BPF_TAG_SIZE 8 + +struct bpf_prog_info { + __u32 type; + __u32 id; + __u8 tag[BPF_TAG_SIZE]; + __u32 jited_prog_len; + __u32 xlated_prog_len; + __aligned_u64 jited_prog_insns; + __aligned_u64 xlated_prog_insns; + __u64 load_time; /* ns since boottime */ + __u32 created_by_uid; + __u32 nr_map_ids; + __aligned_u64 map_ids; + char name[BPF_OBJ_NAME_LEN]; + __u32 ifindex; + __u32 gpl_compatible:1; + __u32 :31; /* alignment pad */ + __u64 netns_dev; + __u64 netns_ino; + __u32 nr_jited_ksyms; + __u32 nr_jited_func_lens; + __aligned_u64 jited_ksyms; + __aligned_u64 jited_func_lens; + __u32 btf_id; + __u32 func_info_rec_size; + __aligned_u64 func_info; + __u32 nr_func_info; + __u32 nr_line_info; + __aligned_u64 line_info; + __aligned_u64 jited_line_info; + __u32 nr_jited_line_info; + __u32 line_info_rec_size; + __u32 jited_line_info_rec_size; + __u32 nr_prog_tags; + __aligned_u64 prog_tags; + __u64 run_time_ns; + __u64 run_cnt; +} __attribute__((aligned(8))); + +struct bpf_map_info { + __u32 type; + __u32 id; + __u32 key_size; + __u32 value_size; + __u32 max_entries; + __u32 map_flags; + char name[BPF_OBJ_NAME_LEN]; + __u32 ifindex; + __u32 btf_vmlinux_value_type_id; + __u64 netns_dev; + __u64 netns_ino; + __u32 btf_id; + __u32 btf_key_type_id; + __u32 btf_value_type_id; +} __attribute__((aligned(8))); + +struct bpf_btf_info { + __aligned_u64 btf; + __u32 btf_size; + __u32 id; +} __attribute__((aligned(8))); + +struct bpf_link_info { + __u32 type; + __u32 id; + __u32 prog_id; + union { + struct { + __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */ + __u32 tp_name_len; /* in/out: tp_name buffer len */ + } raw_tracepoint; + struct { + __u32 attach_type; + } tracing; + struct { + __u64 cgroup_id; + __u32 attach_type; + } cgroup; + struct { + __u32 netns_ino; + __u32 attach_type; + } netns; + struct { + __u32 ifindex; + } xdp; + }; +} __attribute__((aligned(8))); + +/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed + * by user and intended to be used by socket (e.g. to bind to, depends on + * attach type). + */ +struct bpf_sock_addr { + __u32 user_family; /* Allows 4-byte read, but no write. */ + __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write. + * Stored in network byte order. + */ + __u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. + * Stored in network byte order. + */ + __u32 user_port; /* Allows 1,2,4-byte read and 4-byte write. + * Stored in network byte order + */ + __u32 family; /* Allows 4-byte read, but no write */ + __u32 type; /* Allows 4-byte read, but no write */ + __u32 protocol; /* Allows 4-byte read, but no write */ + __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write. + * Stored in network byte order. + */ + __u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write. + * Stored in network byte order. + */ + __bpf_md_ptr(struct bpf_sock *, sk); +}; + +/* User bpf_sock_ops struct to access socket values and specify request ops + * and their replies. + * Some of this fields are in network (bigendian) byte order and may need + * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h). + * New fields can only be added at the end of this structure + */ +struct bpf_sock_ops { + __u32 op; + union { + __u32 args[4]; /* Optionally passed to bpf program */ + __u32 reply; /* Returned by bpf program */ + __u32 replylong[4]; /* Optionally returned by bpf prog */ + }; + __u32 family; + __u32 remote_ip4; /* Stored in network byte order */ + __u32 local_ip4; /* Stored in network byte order */ + __u32 remote_ip6[4]; /* Stored in network byte order */ + __u32 local_ip6[4]; /* Stored in network byte order */ + __u32 remote_port; /* Stored in network byte order */ + __u32 local_port; /* stored in host byte order */ + __u32 is_fullsock; /* Some TCP fields are only valid if + * there is a full socket. If not, the + * fields read as zero. + */ + __u32 snd_cwnd; + __u32 srtt_us; /* Averaged RTT << 3 in usecs */ + __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */ + __u32 state; + __u32 rtt_min; + __u32 snd_ssthresh; + __u32 rcv_nxt; + __u32 snd_nxt; + __u32 snd_una; + __u32 mss_cache; + __u32 ecn_flags; + __u32 rate_delivered; + __u32 rate_interval_us; + __u32 packets_out; + __u32 retrans_out; + __u32 total_retrans; + __u32 segs_in; + __u32 data_segs_in; + __u32 segs_out; + __u32 data_segs_out; + __u32 lost_out; + __u32 sacked_out; + __u32 sk_txhash; + __u64 bytes_received; + __u64 bytes_acked; + __bpf_md_ptr(struct bpf_sock *, sk); +}; + +/* Definitions for bpf_sock_ops_cb_flags */ +enum { + BPF_SOCK_OPS_RTO_CB_FLAG = (1<<0), + BPF_SOCK_OPS_RETRANS_CB_FLAG = (1<<1), + BPF_SOCK_OPS_STATE_CB_FLAG = (1<<2), + BPF_SOCK_OPS_RTT_CB_FLAG = (1<<3), +/* Mask of all currently supported cb flags */ + BPF_SOCK_OPS_ALL_CB_FLAGS = 0xF, +}; + +/* List of known BPF sock_ops operators. + * New entries can only be added at the end + */ +enum { + BPF_SOCK_OPS_VOID, + BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or + * -1 if default value should be used + */ + BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized + * window (in packets) or -1 if default + * value should be used + */ + BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an + * active connection is initialized + */ + BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an + * active connection is + * established + */ + BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a + * passive connection is + * established + */ + BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control + * needs ECN + */ + BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is + * based on the path and may be + * dependent on the congestion control + * algorithm. In general it indicates + * a congestion threshold. RTTs above + * this indicate congestion + */ + BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered. + * Arg1: value of icsk_retransmits + * Arg2: value of icsk_rto + * Arg3: whether RTO has expired + */ + BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted. + * Arg1: sequence number of 1st byte + * Arg2: # segments + * Arg3: return value of + * tcp_transmit_skb (0 => success) + */ + BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state. + * Arg1: old_state + * Arg2: new_state + */ + BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after + * socket transition to LISTEN state. + */ + BPF_SOCK_OPS_RTT_CB, /* Called on every RTT. + */ +}; + +/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect + * changes between the TCP and BPF versions. Ideally this should never happen. + * If it does, we need to add code to convert them before calling + * the BPF sock_ops function. + */ +enum { + BPF_TCP_ESTABLISHED = 1, + BPF_TCP_SYN_SENT, + BPF_TCP_SYN_RECV, + BPF_TCP_FIN_WAIT1, + BPF_TCP_FIN_WAIT2, + BPF_TCP_TIME_WAIT, + BPF_TCP_CLOSE, + BPF_TCP_CLOSE_WAIT, + BPF_TCP_LAST_ACK, + BPF_TCP_LISTEN, + BPF_TCP_CLOSING, /* Now a valid state */ + BPF_TCP_NEW_SYN_RECV, + + BPF_TCP_MAX_STATES /* Leave at the end! */ +}; + +enum { + TCP_BPF_IW = 1001, /* Set TCP initial congestion window */ + TCP_BPF_SNDCWND_CLAMP = 1002, /* Set sndcwnd_clamp */ +}; + +struct bpf_perf_event_value { + __u64 counter; + __u64 enabled; + __u64 running; +}; + +enum { + BPF_DEVCG_ACC_MKNOD = (1ULL << 0), + BPF_DEVCG_ACC_READ = (1ULL << 1), + BPF_DEVCG_ACC_WRITE = (1ULL << 2), +}; + +enum { + BPF_DEVCG_DEV_BLOCK = (1ULL << 0), + BPF_DEVCG_DEV_CHAR = (1ULL << 1), +}; + +struct bpf_cgroup_dev_ctx { + /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ + __u32 access_type; + __u32 major; + __u32 minor; +}; + +struct bpf_raw_tracepoint_args { + __u64 args[0]; +}; + +/* DIRECT: Skip the FIB rules and go to FIB table associated with device + * OUTPUT: Do lookup from egress perspective; default is ingress + */ +enum { + BPF_FIB_LOOKUP_DIRECT = (1U << 0), + BPF_FIB_LOOKUP_OUTPUT = (1U << 1), +}; + +enum { + BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ + BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */ + BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */ + BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */ + BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */ + BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */ + BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */ + BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */ + BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */ +}; + +struct bpf_fib_lookup { + /* input: network family for lookup (AF_INET, AF_INET6) + * output: network family of egress nexthop + */ + __u8 family; + + /* set if lookup is to consider L4 data - e.g., FIB rules */ + __u8 l4_protocol; + __be16 sport; + __be16 dport; + + /* total length of packet from network header - used for MTU check */ + __u16 tot_len; + + /* input: L3 device index for lookup + * output: device index from FIB lookup + */ + __u32 ifindex; + + union { + /* inputs to lookup */ + __u8 tos; /* AF_INET */ + __be32 flowinfo; /* AF_INET6, flow_label + priority */ + + /* output: metric of fib result (IPv4/IPv6 only) */ + __u32 rt_metric; + }; + + union { + __be32 ipv4_src; + __u32 ipv6_src[4]; /* in6_addr; network order */ + }; + + /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in + * network header. output: bpf_fib_lookup sets to gateway address + * if FIB lookup returns gateway route + */ + union { + __be32 ipv4_dst; + __u32 ipv6_dst[4]; /* in6_addr; network order */ + }; + + /* output */ + __be16 h_vlan_proto; + __be16 h_vlan_TCI; + __u8 smac[6]; /* ETH_ALEN */ + __u8 dmac[6]; /* ETH_ALEN */ +}; + +enum bpf_task_fd_type { + BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */ + BPF_FD_TYPE_TRACEPOINT, /* tp name */ + BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */ + BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */ + BPF_FD_TYPE_UPROBE, /* filename + offset */ + BPF_FD_TYPE_URETPROBE, /* filename + offset */ +}; + +enum { + BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = (1U << 0), + BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = (1U << 1), + BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = (1U << 2), +}; + +struct bpf_flow_keys { + __u16 nhoff; + __u16 thoff; + __u16 addr_proto; /* ETH_P_* of valid addrs */ + __u8 is_frag; + __u8 is_first_frag; + __u8 is_encap; + __u8 ip_proto; + __be16 n_proto; + __be16 sport; + __be16 dport; + union { + struct { + __be32 ipv4_src; + __be32 ipv4_dst; + }; + struct { + __u32 ipv6_src[4]; /* in6_addr; network order */ + __u32 ipv6_dst[4]; /* in6_addr; network order */ + }; + }; + __u32 flags; + __be32 flow_label; +}; + +struct bpf_func_info { + __u32 insn_off; + __u32 type_id; +}; + +#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10) +#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff) + +struct bpf_line_info { + __u32 insn_off; + __u32 file_name_off; + __u32 line_off; + __u32 line_col; +}; + +struct bpf_spin_lock { + __u32 val; +}; + +struct bpf_sysctl { + __u32 write; /* Sysctl is being read (= 0) or written (= 1). + * Allows 1,2,4-byte read, but no write. + */ + __u32 file_pos; /* Sysctl file position to read from, write to. + * Allows 1,2,4-byte read an 4-byte write. + */ +}; + +struct bpf_sockopt { + __bpf_md_ptr(struct bpf_sock *, sk); + __bpf_md_ptr(void *, optval); + __bpf_md_ptr(void *, optval_end); + + __s32 level; + __s32 optname; + __s32 optlen; + __s32 retval; +}; + +struct bpf_pidns_info { + __u32 pid; + __u32 tgid; +}; + +/* User accessible data for SK_LOOKUP programs. Add new fields at the end. */ +struct bpf_sk_lookup { + __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */ + + __u32 family; /* Protocol family (AF_INET, AF_INET6) */ + __u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */ + __u32 remote_ip4; /* Network byte order */ + __u32 remote_ip6[4]; /* Network byte order */ + __u32 remote_port; /* Network byte order */ + __u32 local_ip4; /* Network byte order */ + __u32 local_ip6[4]; /* Network byte order */ + __u32 local_port; /* Host byte order */ +}; + +#endif /* __LINUX_BPF_H__ */ diff --git a/include/uapi/linux/bpf_common.h b/include/uapi/linux/bpf_common.h new file mode 100644 index 0000000..f0fe139 --- /dev/null +++ b/include/uapi/linux/bpf_common.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_BPF_COMMON_H__ +#define __LINUX_BPF_COMMON_H__ + +/* Instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 /* 32-bit */ +#define BPF_H 0x08 /* 16-bit */ +#define BPF_B 0x10 /* 8-bit */ +/* eBPF BPF_DW 0x18 64-bit */ +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_MOD 0x90 +#define BPF_XOR 0xa0 + +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +#ifndef BPF_MAXINSNS +#define BPF_MAXINSNS 4096 +#endif + +#endif /* __LINUX_BPF_COMMON_H__ */ diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h new file mode 100644 index 0000000..4a42eb4 --- /dev/null +++ b/include/uapi/linux/btf.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2018 Facebook */ +#ifndef __LINUX_BTF_H__ +#define __LINUX_BTF_H__ + +#include + +#define BTF_MAGIC 0xeB9F +#define BTF_VERSION 1 + +struct btf_header { + __u16 magic; + __u8 version; + __u8 flags; + __u32 hdr_len; + + /* All offsets are in bytes relative to the end of this header */ + __u32 type_off; /* offset of type section */ + __u32 type_len; /* length of type section */ + __u32 str_off; /* offset of string section */ + __u32 str_len; /* length of string section */ +}; + +/* Max # of type identifier */ +#define BTF_MAX_TYPE 0x000fffff +/* Max offset into the string section */ +#define BTF_MAX_NAME_OFFSET 0x00ffffff +/* Max # of struct/union/enum members or func args */ +#define BTF_MAX_VLEN 0xffff + +struct btf_type { + __u32 name_off; + /* "info" bits arrangement + * bits 0-15: vlen (e.g. # of struct's members) + * bits 16-23: unused + * bits 24-27: kind (e.g. int, ptr, array...etc) + * bits 28-30: unused + * bit 31: kind_flag, currently used by + * struct, union and fwd + */ + __u32 info; + /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC. + * "size" tells the size of the type it is describing. + * + * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, + * FUNC, FUNC_PROTO and VAR. + * "type" is a type_id referring to another type. + */ + union { + __u32 size; + __u32 type; + }; +}; + +#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f) +#define BTF_INFO_VLEN(info) ((info) & 0xffff) +#define BTF_INFO_KFLAG(info) ((info) >> 31) + +#define BTF_KIND_UNKN 0 /* Unknown */ +#define BTF_KIND_INT 1 /* Integer */ +#define BTF_KIND_PTR 2 /* Pointer */ +#define BTF_KIND_ARRAY 3 /* Array */ +#define BTF_KIND_STRUCT 4 /* Struct */ +#define BTF_KIND_UNION 5 /* Union */ +#define BTF_KIND_ENUM 6 /* Enumeration */ +#define BTF_KIND_FWD 7 /* Forward */ +#define BTF_KIND_TYPEDEF 8 /* Typedef */ +#define BTF_KIND_VOLATILE 9 /* Volatile */ +#define BTF_KIND_CONST 10 /* Const */ +#define BTF_KIND_RESTRICT 11 /* Restrict */ +#define BTF_KIND_FUNC 12 /* Function */ +#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */ +#define BTF_KIND_VAR 14 /* Variable */ +#define BTF_KIND_DATASEC 15 /* Section */ +#define BTF_KIND_MAX BTF_KIND_DATASEC +#define NR_BTF_KINDS (BTF_KIND_MAX + 1) + +/* For some specific BTF_KIND, "struct btf_type" is immediately + * followed by extra data. + */ + +/* BTF_KIND_INT is followed by a u32 and the following + * is the 32 bits arrangement: + */ +#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24) +#define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16) +#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff) + +/* Attributes stored in the BTF_INT_ENCODING */ +#define BTF_INT_SIGNED (1 << 0) +#define BTF_INT_CHAR (1 << 1) +#define BTF_INT_BOOL (1 << 2) + +/* BTF_KIND_ENUM is followed by multiple "struct btf_enum". + * The exact number of btf_enum is stored in the vlen (of the + * info in "struct btf_type"). + */ +struct btf_enum { + __u32 name_off; + __s32 val; +}; + +/* BTF_KIND_ARRAY is followed by one "struct btf_array" */ +struct btf_array { + __u32 type; + __u32 index_type; + __u32 nelems; +}; + +/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed + * by multiple "struct btf_member". The exact number + * of btf_member is stored in the vlen (of the info in + * "struct btf_type"). + */ +struct btf_member { + __u32 name_off; + __u32 type; + /* If the type info kind_flag is set, the btf_member offset + * contains both member bitfield size and bit offset. The + * bitfield size is set for bitfield members. If the type + * info kind_flag is not set, the offset contains only bit + * offset. + */ + __u32 offset; +}; + +/* If the struct/union type info kind_flag is set, the + * following two macros are used to access bitfield_size + * and bit_offset from btf_member.offset. + */ +#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24) +#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff) + +/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". + * The exact number of btf_param is stored in the vlen (of the + * info in "struct btf_type"). + */ +struct btf_param { + __u32 name_off; + __u32 type; +}; + +enum { + BTF_VAR_STATIC = 0, + BTF_VAR_GLOBAL_ALLOCATED = 1, + BTF_VAR_GLOBAL_EXTERN = 2, +}; + +enum btf_func_linkage { + BTF_FUNC_STATIC = 0, + BTF_FUNC_GLOBAL = 1, + BTF_FUNC_EXTERN = 2, +}; + +/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe + * additional information related to the variable such as its linkage. + */ +struct btf_var { + __u32 linkage; +}; + +/* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo" + * to describe all BTF_KIND_VAR types it contains along with it's + * in-section offset as well as size. + */ +struct btf_var_secinfo { + __u32 type; + __u32 offset; + __u32 size; +}; + +#endif /* __LINUX_BTF_H__ */ diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h new file mode 100644 index 0000000..55b6660 --- /dev/null +++ b/include/uapi/linux/can.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: ((GPL-2.0-only WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * linux/can.h + * + * Definitions for CAN network layer (socket addr / CAN frame / CAN filter) + * + * Authors: Oliver Hartkopp + * Urs Thuermann + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef _CAN_H +#define _CAN_H + +#include +#include + +/* controller area network (CAN) kernel definitions */ + +/* special address description flags for the CAN_ID */ +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error message frame */ + +/* valid bits in CAN ID for frame formats */ +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */ +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ + +/* + * Controller Area Network Identifier structure + * + * bit 0-28 : CAN identifier (11/29 bit) + * bit 29 : error message frame flag (0 = data frame, 1 = error message) + * bit 30 : remote transmission request flag (1 = rtr frame) + * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit) + */ +typedef __u32 canid_t; + +#define CAN_SFF_ID_BITS 11 +#define CAN_EFF_ID_BITS 29 + +/* + * Controller Area Network Error Message Frame Mask structure + * + * bit 0-28 : error class mask (see include/uapi/linux/can/error.h) + * bit 29-31 : set to zero + */ +typedef __u32 can_err_mask_t; + +/* CAN payload length and DLC definitions according to ISO 11898-1 */ +#define CAN_MAX_DLC 8 +#define CAN_MAX_DLEN 8 + +/* CAN FD payload length and DLC definitions according to ISO 11898-7 */ +#define CANFD_MAX_DLC 15 +#define CANFD_MAX_DLEN 64 + +/** + * struct can_frame - basic CAN frame structure + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @can_dlc: frame payload length in byte (0 .. 8) aka data length code + * N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1 + * mapping of the 'data length code' to the real payload length + * @__pad: padding + * @__res0: reserved / padding + * @__res1: reserved / padding + * @data: CAN frame payload (up to 8 byte) + */ +struct can_frame { + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + __u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */ + __u8 __pad; /* padding */ + __u8 __res0; /* reserved / padding */ + __u8 __res1; /* reserved / padding */ + __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8))); +}; + +/* + * defined bits for canfd_frame.flags + * + * The use of struct canfd_frame implies the Extended Data Length (EDL) bit to + * be set in the CAN frame bitstream on the wire. The EDL bit switch turns + * the CAN controllers bitstream processor into the CAN FD mode which creates + * two new options within the CAN FD frame specification: + * + * Bit Rate Switch - to indicate a second bitrate is/was used for the payload + * Error State Indicator - represents the error state of the transmitting node + * + * As the CANFD_ESI bit is internally generated by the transmitting CAN + * controller only the CANFD_BRS bit is relevant for real CAN controllers when + * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make + * sense for virtual CAN interfaces to test applications with echoed frames. + */ +#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ +#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ + +/** + * struct canfd_frame - CAN flexible data rate frame structure + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN) + * @flags: additional flags for CAN FD + * @__res0: reserved / padding + * @__res1: reserved / padding + * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte) + */ +struct canfd_frame { + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + __u8 len; /* frame payload length in byte */ + __u8 flags; /* additional flags for CAN FD */ + __u8 __res0; /* reserved / padding */ + __u8 __res1; /* reserved / padding */ + __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8))); +}; + +#define CAN_MTU (sizeof(struct can_frame)) +#define CANFD_MTU (sizeof(struct canfd_frame)) + +/* particular protocols of the protocol family PF_CAN */ +#define CAN_RAW 1 /* RAW sockets */ +#define CAN_BCM 2 /* Broadcast Manager */ +#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */ +#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */ +#define CAN_MCNET 5 /* Bosch MCNet */ +#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */ +#define CAN_J1939 7 /* SAE J1939 */ +#define CAN_NPROTO 8 + +#define SOL_CAN_BASE 100 + +/** + * struct sockaddr_can - the sockaddr structure for CAN sockets + * @can_family: address family number AF_CAN. + * @can_ifindex: CAN network interface index. + * @can_addr: protocol specific address information + */ +struct sockaddr_can { + __kernel_sa_family_t can_family; + int can_ifindex; + union { + /* transport protocol class address information (e.g. ISOTP) */ + struct { canid_t rx_id, tx_id; } tp; + + /* J1939 address information */ + struct { + /* 8 byte name when using dynamic addressing */ + __u64 name; + + /* pgn: + * 8 bit: PS in PDU2 case, else 0 + * 8 bit: PF + * 1 bit: DP + * 1 bit: reserved + */ + __u32 pgn; + + /* 1 byte address */ + __u8 addr; + } j1939; + + /* reserved for future CAN protocols address information */ + } can_addr; +}; + +/** + * struct can_filter - CAN ID based filter in can_register(). + * @can_id: relevant bits of CAN ID which are not masked out. + * @can_mask: CAN mask (see description) + * + * Description: + * A filter matches, when + * + * & mask == can_id & mask + * + * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can + * filter for error message frames (CAN_ERR_FLAG bit set in mask). + */ +struct can_filter { + canid_t can_id; + canid_t can_mask; +}; + +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ +#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */ + +#endif /* !_UAPI_CAN_H */ diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h new file mode 100644 index 0000000..7159dc6 --- /dev/null +++ b/include/uapi/linux/can/netlink.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * linux/can/netlink.h + * + * Definitions for the CAN netlink interface + * + * Copyright (c) 2009 Wolfgang Grandegger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CAN_NETLINK_H +#define _CAN_NETLINK_H + +#include + +/* + * CAN bit-timing parameters + * + * For further information, please read chapter "8 BIT TIMING + * REQUIREMENTS" of the "Bosch CAN Specification version 2.0" + * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf. + */ +struct can_bittiming { + __u32 bitrate; /* Bit-rate in bits/second */ + __u32 sample_point; /* Sample point in one-tenth of a percent */ + __u32 tq; /* Time quanta (TQ) in nanoseconds */ + __u32 prop_seg; /* Propagation segment in TQs */ + __u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + __u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + __u32 sjw; /* Synchronisation jump width in TQs */ + __u32 brp; /* Bit-rate prescaler */ +}; + +/* + * CAN hardware-dependent bit-timing constant + * + * Used for calculating and checking bit-timing parameters + */ +struct can_bittiming_const { + char name[16]; /* Name of the CAN controller hardware */ + __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */ + __u32 tseg1_max; + __u32 tseg2_min; /* Time segment 2 = phase_seg2 */ + __u32 tseg2_max; + __u32 sjw_max; /* Synchronisation jump width */ + __u32 brp_min; /* Bit-rate prescaler */ + __u32 brp_max; + __u32 brp_inc; +}; + +/* + * CAN clock parameters + */ +struct can_clock { + __u32 freq; /* CAN system clock frequency in Hz */ +}; + +/* + * CAN operational and error states + */ +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +/* + * CAN bus error counters + */ +struct can_berr_counter { + __u16 txerr; + __u16 rxerr; +}; + +/* + * CAN controller mode + */ +struct can_ctrlmode { + __u32 mask; + __u32 flags; +}; + +#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ +#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ +#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ +#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ +#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ + +/* + * CAN device statistics + */ +struct can_device_stats { + __u32 bus_error; /* Bus errors */ + __u32 error_warning; /* Changes to error warning state */ + __u32 error_passive; /* Changes to error passive state */ + __u32 bus_off; /* Changes to bus off state */ + __u32 arbitration_lost; /* Arbitration lost errors */ + __u32 restarts; /* CAN controller re-starts */ +}; + +/* + * CAN netlink interface + */ +enum { + IFLA_CAN_UNSPEC, + IFLA_CAN_BITTIMING, + IFLA_CAN_BITTIMING_CONST, + IFLA_CAN_CLOCK, + IFLA_CAN_STATE, + IFLA_CAN_CTRLMODE, + IFLA_CAN_RESTART_MS, + IFLA_CAN_RESTART, + IFLA_CAN_BERR_COUNTER, + IFLA_CAN_DATA_BITTIMING, + IFLA_CAN_DATA_BITTIMING_CONST, + IFLA_CAN_TERMINATION, + IFLA_CAN_TERMINATION_CONST, + IFLA_CAN_BITRATE_CONST, + IFLA_CAN_DATA_BITRATE_CONST, + IFLA_CAN_BITRATE_MAX, + __IFLA_CAN_MAX +}; + +#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) + +/* u16 termination range: 1..65535 Ohms */ +#define CAN_TERMINATION_DISABLED 0 + +#endif /* !_UAPI_CAN_NETLINK_H */ diff --git a/include/uapi/linux/can/vxcan.h b/include/uapi/linux/can/vxcan.h new file mode 100644 index 0000000..3e3d2eb --- /dev/null +++ b/include/uapi/linux/can/vxcan.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +#ifndef _CAN_VXCAN_H +#define _CAN_VXCAN_H + +enum { + VXCAN_INFO_UNSPEC, + VXCAN_INFO_PEER, + + __VXCAN_INFO_MAX +#define VXCAN_INFO_MAX (__VXCAN_INFO_MAX - 1) +}; + +#endif diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h new file mode 100644 index 0000000..fd885c7 --- /dev/null +++ b/include/uapi/linux/const.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* const.h: Macros for dealing with constants. */ + +#ifndef _LINUX_CONST_H +#define _LINUX_CONST_H + +/* Some constant macros are used in both assembler and + * C code. Therefore we cannot annotate them always with + * 'UL' and other type specifiers unilaterally. We + * use the following macros to deal with this. + * + * Similarly, _AT() will cast an expression with a type in C, but + * leave it unchanged in asm. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) + +#endif /* _LINUX_CONST_H */ diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h new file mode 100644 index 0000000..b7f23fa --- /dev/null +++ b/include/uapi/linux/devlink.h @@ -0,0 +1,515 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/uapi/linux/devlink.h - Network physical device Netlink interface + * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016 Jiri Pirko + * + * 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_DEVLINK_H_ +#define _LINUX_DEVLINK_H_ + +#define DEVLINK_GENL_NAME "devlink" +#define DEVLINK_GENL_VERSION 0x1 +#define DEVLINK_GENL_MCGRP_CONFIG_NAME "config" + +enum devlink_command { + /* don't change the order or add anything between, this is ABI! */ + DEVLINK_CMD_UNSPEC, + + DEVLINK_CMD_GET, /* can dump */ + DEVLINK_CMD_SET, + DEVLINK_CMD_NEW, + DEVLINK_CMD_DEL, + + DEVLINK_CMD_PORT_GET, /* can dump */ + DEVLINK_CMD_PORT_SET, + DEVLINK_CMD_PORT_NEW, + DEVLINK_CMD_PORT_DEL, + + DEVLINK_CMD_PORT_SPLIT, + DEVLINK_CMD_PORT_UNSPLIT, + + DEVLINK_CMD_SB_GET, /* can dump */ + DEVLINK_CMD_SB_SET, + DEVLINK_CMD_SB_NEW, + DEVLINK_CMD_SB_DEL, + + DEVLINK_CMD_SB_POOL_GET, /* can dump */ + DEVLINK_CMD_SB_POOL_SET, + DEVLINK_CMD_SB_POOL_NEW, + DEVLINK_CMD_SB_POOL_DEL, + + DEVLINK_CMD_SB_PORT_POOL_GET, /* can dump */ + DEVLINK_CMD_SB_PORT_POOL_SET, + DEVLINK_CMD_SB_PORT_POOL_NEW, + DEVLINK_CMD_SB_PORT_POOL_DEL, + + DEVLINK_CMD_SB_TC_POOL_BIND_GET, /* can dump */ + DEVLINK_CMD_SB_TC_POOL_BIND_SET, + DEVLINK_CMD_SB_TC_POOL_BIND_NEW, + DEVLINK_CMD_SB_TC_POOL_BIND_DEL, + + /* Shared buffer occupancy monitoring commands */ + DEVLINK_CMD_SB_OCC_SNAPSHOT, + DEVLINK_CMD_SB_OCC_MAX_CLEAR, + + DEVLINK_CMD_ESWITCH_GET, +#define DEVLINK_CMD_ESWITCH_MODE_GET /* obsolete, never use this! */ \ + DEVLINK_CMD_ESWITCH_GET + + DEVLINK_CMD_ESWITCH_SET, +#define DEVLINK_CMD_ESWITCH_MODE_SET /* obsolete, never use this! */ \ + DEVLINK_CMD_ESWITCH_SET + + DEVLINK_CMD_DPIPE_TABLE_GET, + DEVLINK_CMD_DPIPE_ENTRIES_GET, + DEVLINK_CMD_DPIPE_HEADERS_GET, + DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET, + DEVLINK_CMD_RESOURCE_SET, + DEVLINK_CMD_RESOURCE_DUMP, + + /* Hot driver reload, makes configuration changes take place. The + * devlink instance is not released during the process. + */ + DEVLINK_CMD_RELOAD, + + DEVLINK_CMD_PARAM_GET, /* can dump */ + DEVLINK_CMD_PARAM_SET, + DEVLINK_CMD_PARAM_NEW, + DEVLINK_CMD_PARAM_DEL, + + DEVLINK_CMD_REGION_GET, + DEVLINK_CMD_REGION_SET, + DEVLINK_CMD_REGION_NEW, + DEVLINK_CMD_REGION_DEL, + DEVLINK_CMD_REGION_READ, + + DEVLINK_CMD_PORT_PARAM_GET, /* can dump */ + DEVLINK_CMD_PORT_PARAM_SET, + DEVLINK_CMD_PORT_PARAM_NEW, + DEVLINK_CMD_PORT_PARAM_DEL, + + DEVLINK_CMD_INFO_GET, /* can dump */ + + DEVLINK_CMD_HEALTH_REPORTER_GET, + DEVLINK_CMD_HEALTH_REPORTER_SET, + DEVLINK_CMD_HEALTH_REPORTER_RECOVER, + DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, + DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, + DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR, + + DEVLINK_CMD_FLASH_UPDATE, + DEVLINK_CMD_FLASH_UPDATE_END, /* notification only */ + DEVLINK_CMD_FLASH_UPDATE_STATUS, /* notification only */ + + DEVLINK_CMD_TRAP_GET, /* can dump */ + DEVLINK_CMD_TRAP_SET, + DEVLINK_CMD_TRAP_NEW, + DEVLINK_CMD_TRAP_DEL, + + DEVLINK_CMD_TRAP_GROUP_GET, /* can dump */ + DEVLINK_CMD_TRAP_GROUP_SET, + DEVLINK_CMD_TRAP_GROUP_NEW, + DEVLINK_CMD_TRAP_GROUP_DEL, + + DEVLINK_CMD_TRAP_POLICER_GET, /* can dump */ + DEVLINK_CMD_TRAP_POLICER_SET, + DEVLINK_CMD_TRAP_POLICER_NEW, + DEVLINK_CMD_TRAP_POLICER_DEL, + + /* add new commands above here */ + __DEVLINK_CMD_MAX, + DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 +}; + +enum devlink_port_type { + DEVLINK_PORT_TYPE_NOTSET, + DEVLINK_PORT_TYPE_AUTO, + DEVLINK_PORT_TYPE_ETH, + DEVLINK_PORT_TYPE_IB, +}; + +enum devlink_sb_pool_type { + DEVLINK_SB_POOL_TYPE_INGRESS, + DEVLINK_SB_POOL_TYPE_EGRESS, +}; + +/* static threshold - limiting the maximum number of bytes. + * dynamic threshold - limiting the maximum number of bytes + * based on the currently available free space in the shared buffer pool. + * In this mode, the maximum quota is calculated based + * on the following formula: + * max_quota = alpha / (1 + alpha) * Free_Buffer + * While Free_Buffer is the amount of none-occupied buffer associated to + * the relevant pool. + * The value range which can be passed is 0-20 and serves + * for computation of alpha by following formula: + * alpha = 2 ^ (passed_value - 10) + */ + +enum devlink_sb_threshold_type { + DEVLINK_SB_THRESHOLD_TYPE_STATIC, + DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC, +}; + +#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20 + +enum devlink_eswitch_mode { + DEVLINK_ESWITCH_MODE_LEGACY, + DEVLINK_ESWITCH_MODE_SWITCHDEV, +}; + +enum devlink_eswitch_inline_mode { + DEVLINK_ESWITCH_INLINE_MODE_NONE, + DEVLINK_ESWITCH_INLINE_MODE_LINK, + DEVLINK_ESWITCH_INLINE_MODE_NETWORK, + DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT, +}; + +enum devlink_eswitch_encap_mode { + DEVLINK_ESWITCH_ENCAP_MODE_NONE, + DEVLINK_ESWITCH_ENCAP_MODE_BASIC, +}; + +enum devlink_port_flavour { + DEVLINK_PORT_FLAVOUR_PHYSICAL, /* Any kind of a port physically + * facing the user. + */ + DEVLINK_PORT_FLAVOUR_CPU, /* CPU port */ + DEVLINK_PORT_FLAVOUR_DSA, /* Distributed switch architecture + * interconnect port. + */ + DEVLINK_PORT_FLAVOUR_PCI_PF, /* Represents eswitch port for + * the PCI PF. It is an internal + * port that faces the PCI PF. + */ + DEVLINK_PORT_FLAVOUR_PCI_VF, /* Represents eswitch port + * for the PCI VF. It is an internal + * port that faces the PCI VF. + */ + DEVLINK_PORT_FLAVOUR_VIRTUAL, /* Any virtual port facing the user. */ +}; + +enum devlink_param_cmode { + DEVLINK_PARAM_CMODE_RUNTIME, + DEVLINK_PARAM_CMODE_DRIVERINIT, + DEVLINK_PARAM_CMODE_PERMANENT, + + /* Add new configuration modes above */ + __DEVLINK_PARAM_CMODE_MAX, + DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1 +}; + +enum devlink_param_fw_load_policy_value { + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER, + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH, + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK, + DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN, +}; + +enum devlink_param_reset_dev_on_drv_probe_value { + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN, + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS, + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER, + DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK, +}; + +enum { + DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */ + DEVLINK_ATTR_STATS_RX_BYTES, /* u64 */ + DEVLINK_ATTR_STATS_RX_DROPPED, /* u64 */ + + __DEVLINK_ATTR_STATS_MAX, + DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1 +}; + +/** + * enum devlink_trap_action - Packet trap action. + * @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not + * sent to the CPU. + * @DEVLINK_TRAP_ACTION_TRAP: The sole copy of the packet is sent to the CPU. + * @DEVLINK_TRAP_ACTION_MIRROR: Packet is forwarded by the device and a copy is + * sent to the CPU. + */ +enum devlink_trap_action { + DEVLINK_TRAP_ACTION_DROP, + DEVLINK_TRAP_ACTION_TRAP, + DEVLINK_TRAP_ACTION_MIRROR, +}; + +/** + * enum devlink_trap_type - Packet trap type. + * @DEVLINK_TRAP_TYPE_DROP: Trap reason is a drop. Trapped packets are only + * processed by devlink and not injected to the + * kernel's Rx path. + * @DEVLINK_TRAP_TYPE_EXCEPTION: Trap reason is an exception. Packet was not + * forwarded as intended due to an exception + * (e.g., missing neighbour entry) and trapped to + * control plane for resolution. Trapped packets + * are processed by devlink and injected to + * the kernel's Rx path. + * @DEVLINK_TRAP_TYPE_CONTROL: Packet was trapped because it is required for + * the correct functioning of the control plane. + * For example, an ARP request packet. Trapped + * packets are injected to the kernel's Rx path, + * but not reported to drop monitor. + */ +enum devlink_trap_type { + DEVLINK_TRAP_TYPE_DROP, + DEVLINK_TRAP_TYPE_EXCEPTION, + DEVLINK_TRAP_TYPE_CONTROL, +}; + +enum { + /* Trap can report input port as metadata */ + DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT, + /* Trap can report flow action cookie as metadata */ + DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE, +}; + +enum devlink_attr { + /* don't change the order or add anything between, this is ABI! */ + DEVLINK_ATTR_UNSPEC, + + /* bus name + dev name together are a handle for devlink entity */ + DEVLINK_ATTR_BUS_NAME, /* string */ + DEVLINK_ATTR_DEV_NAME, /* string */ + + DEVLINK_ATTR_PORT_INDEX, /* u32 */ + DEVLINK_ATTR_PORT_TYPE, /* u16 */ + DEVLINK_ATTR_PORT_DESIRED_TYPE, /* u16 */ + DEVLINK_ATTR_PORT_NETDEV_IFINDEX, /* u32 */ + DEVLINK_ATTR_PORT_NETDEV_NAME, /* string */ + DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */ + DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */ + DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */ + DEVLINK_ATTR_SB_INDEX, /* u32 */ + DEVLINK_ATTR_SB_SIZE, /* u32 */ + DEVLINK_ATTR_SB_INGRESS_POOL_COUNT, /* u16 */ + DEVLINK_ATTR_SB_EGRESS_POOL_COUNT, /* u16 */ + DEVLINK_ATTR_SB_INGRESS_TC_COUNT, /* u16 */ + DEVLINK_ATTR_SB_EGRESS_TC_COUNT, /* u16 */ + DEVLINK_ATTR_SB_POOL_INDEX, /* u16 */ + DEVLINK_ATTR_SB_POOL_TYPE, /* u8 */ + DEVLINK_ATTR_SB_POOL_SIZE, /* u32 */ + DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, /* u8 */ + DEVLINK_ATTR_SB_THRESHOLD, /* u32 */ + DEVLINK_ATTR_SB_TC_INDEX, /* u16 */ + DEVLINK_ATTR_SB_OCC_CUR, /* u32 */ + DEVLINK_ATTR_SB_OCC_MAX, /* u32 */ + DEVLINK_ATTR_ESWITCH_MODE, /* u16 */ + DEVLINK_ATTR_ESWITCH_INLINE_MODE, /* u8 */ + + DEVLINK_ATTR_DPIPE_TABLES, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_NAME, /* string */ + DEVLINK_ATTR_DPIPE_TABLE_SIZE, /* u64 */ + DEVLINK_ATTR_DPIPE_TABLE_MATCHES, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_ACTIONS, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, /* u8 */ + + DEVLINK_ATTR_DPIPE_ENTRIES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_INDEX, /* u64 */ + DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, /* u64 */ + + DEVLINK_ATTR_DPIPE_MATCH, /* nested */ + DEVLINK_ATTR_DPIPE_MATCH_VALUE, /* nested */ + DEVLINK_ATTR_DPIPE_MATCH_TYPE, /* u32 */ + + DEVLINK_ATTR_DPIPE_ACTION, /* nested */ + DEVLINK_ATTR_DPIPE_ACTION_VALUE, /* nested */ + DEVLINK_ATTR_DPIPE_ACTION_TYPE, /* u32 */ + + DEVLINK_ATTR_DPIPE_VALUE, + DEVLINK_ATTR_DPIPE_VALUE_MASK, + DEVLINK_ATTR_DPIPE_VALUE_MAPPING, /* u32 */ + + DEVLINK_ATTR_DPIPE_HEADERS, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER_NAME, /* string */ + DEVLINK_ATTR_DPIPE_HEADER_ID, /* u32 */ + DEVLINK_ATTR_DPIPE_HEADER_FIELDS, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, /* u8 */ + DEVLINK_ATTR_DPIPE_HEADER_INDEX, /* u32 */ + + DEVLINK_ATTR_DPIPE_FIELD, /* nested */ + DEVLINK_ATTR_DPIPE_FIELD_NAME, /* string */ + DEVLINK_ATTR_DPIPE_FIELD_ID, /* u32 */ + DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, /* u32 */ + DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, /* u32 */ + + DEVLINK_ATTR_PAD, + + DEVLINK_ATTR_ESWITCH_ENCAP_MODE, /* u8 */ + DEVLINK_ATTR_RESOURCE_LIST, /* nested */ + DEVLINK_ATTR_RESOURCE, /* nested */ + DEVLINK_ATTR_RESOURCE_NAME, /* string */ + DEVLINK_ATTR_RESOURCE_ID, /* u64 */ + DEVLINK_ATTR_RESOURCE_SIZE, /* u64 */ + DEVLINK_ATTR_RESOURCE_SIZE_NEW, /* u64 */ + DEVLINK_ATTR_RESOURCE_SIZE_VALID, /* u8 */ + DEVLINK_ATTR_RESOURCE_SIZE_MIN, /* u64 */ + DEVLINK_ATTR_RESOURCE_SIZE_MAX, /* u64 */ + DEVLINK_ATTR_RESOURCE_SIZE_GRAN, /* u64 */ + DEVLINK_ATTR_RESOURCE_UNIT, /* u8 */ + DEVLINK_ATTR_RESOURCE_OCC, /* u64 */ + DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID, /* u64 */ + DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,/* u64 */ + + DEVLINK_ATTR_PORT_FLAVOUR, /* u16 */ + DEVLINK_ATTR_PORT_NUMBER, /* u32 */ + DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER, /* u32 */ + + DEVLINK_ATTR_PARAM, /* nested */ + DEVLINK_ATTR_PARAM_NAME, /* string */ + DEVLINK_ATTR_PARAM_GENERIC, /* flag */ + DEVLINK_ATTR_PARAM_TYPE, /* u8 */ + DEVLINK_ATTR_PARAM_VALUES_LIST, /* nested */ + DEVLINK_ATTR_PARAM_VALUE, /* nested */ + DEVLINK_ATTR_PARAM_VALUE_DATA, /* dynamic */ + DEVLINK_ATTR_PARAM_VALUE_CMODE, /* u8 */ + + DEVLINK_ATTR_REGION_NAME, /* string */ + DEVLINK_ATTR_REGION_SIZE, /* u64 */ + DEVLINK_ATTR_REGION_SNAPSHOTS, /* nested */ + DEVLINK_ATTR_REGION_SNAPSHOT, /* nested */ + DEVLINK_ATTR_REGION_SNAPSHOT_ID, /* u32 */ + + DEVLINK_ATTR_REGION_CHUNKS, /* nested */ + DEVLINK_ATTR_REGION_CHUNK, /* nested */ + DEVLINK_ATTR_REGION_CHUNK_DATA, /* binary */ + DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */ + DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */ + + DEVLINK_ATTR_INFO_DRIVER_NAME, /* string */ + DEVLINK_ATTR_INFO_SERIAL_NUMBER, /* string */ + DEVLINK_ATTR_INFO_VERSION_FIXED, /* nested */ + DEVLINK_ATTR_INFO_VERSION_RUNNING, /* nested */ + DEVLINK_ATTR_INFO_VERSION_STORED, /* nested */ + DEVLINK_ATTR_INFO_VERSION_NAME, /* string */ + DEVLINK_ATTR_INFO_VERSION_VALUE, /* string */ + + DEVLINK_ATTR_SB_POOL_CELL_SIZE, /* u32 */ + + DEVLINK_ATTR_FMSG, /* nested */ + DEVLINK_ATTR_FMSG_OBJ_NEST_START, /* flag */ + DEVLINK_ATTR_FMSG_PAIR_NEST_START, /* flag */ + DEVLINK_ATTR_FMSG_ARR_NEST_START, /* flag */ + DEVLINK_ATTR_FMSG_NEST_END, /* flag */ + DEVLINK_ATTR_FMSG_OBJ_NAME, /* string */ + DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE, /* u8 */ + DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA, /* dynamic */ + + DEVLINK_ATTR_HEALTH_REPORTER, /* nested */ + DEVLINK_ATTR_HEALTH_REPORTER_NAME, /* string */ + DEVLINK_ATTR_HEALTH_REPORTER_STATE, /* u8 */ + DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT, /* u64 */ + DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT, /* u64 */ + DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS, /* u64 */ + DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD, /* u64 */ + DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, /* u8 */ + + DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME, /* string */ + DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, /* string */ + DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG, /* string */ + DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE, /* u64 */ + DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL, /* u64 */ + + DEVLINK_ATTR_PORT_PCI_PF_NUMBER, /* u16 */ + DEVLINK_ATTR_PORT_PCI_VF_NUMBER, /* u16 */ + + DEVLINK_ATTR_STATS, /* nested */ + + DEVLINK_ATTR_TRAP_NAME, /* string */ + /* enum devlink_trap_action */ + DEVLINK_ATTR_TRAP_ACTION, /* u8 */ + /* enum devlink_trap_type */ + DEVLINK_ATTR_TRAP_TYPE, /* u8 */ + DEVLINK_ATTR_TRAP_GENERIC, /* flag */ + DEVLINK_ATTR_TRAP_METADATA, /* nested */ + DEVLINK_ATTR_TRAP_GROUP_NAME, /* string */ + + DEVLINK_ATTR_RELOAD_FAILED, /* u8 0 or 1 */ + + DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS, /* u64 */ + + DEVLINK_ATTR_NETNS_FD, /* u32 */ + DEVLINK_ATTR_NETNS_PID, /* u32 */ + DEVLINK_ATTR_NETNS_ID, /* u32 */ + + DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, /* u8 */ + + DEVLINK_ATTR_TRAP_POLICER_ID, /* u32 */ + DEVLINK_ATTR_TRAP_POLICER_RATE, /* u64 */ + DEVLINK_ATTR_TRAP_POLICER_BURST, /* u64 */ + + DEVLINK_ATTR_PORT_FUNCTION, /* nested */ + + DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, /* string */ + + DEVLINK_ATTR_PORT_LANES, /* u32 */ + DEVLINK_ATTR_PORT_SPLITTABLE, /* u8 */ + + /* add new attributes above here, update the policy in devlink.c */ + + __DEVLINK_ATTR_MAX, + DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 +}; + +/* Mapping between internal resource described by the field and system + * structure + */ +enum devlink_dpipe_field_mapping_type { + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE, + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX, +}; + +/* Match type - specify the type of the match */ +enum devlink_dpipe_match_type { + DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT, +}; + +/* Action type - specify the action type */ +enum devlink_dpipe_action_type { + DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY, +}; + +enum devlink_dpipe_field_ethernet_id { + DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, +}; + +enum devlink_dpipe_field_ipv4_id { + DEVLINK_DPIPE_FIELD_IPV4_DST_IP, +}; + +enum devlink_dpipe_field_ipv6_id { + DEVLINK_DPIPE_FIELD_IPV6_DST_IP, +}; + +enum devlink_dpipe_header_id { + DEVLINK_DPIPE_HEADER_ETHERNET, + DEVLINK_DPIPE_HEADER_IPV4, + DEVLINK_DPIPE_HEADER_IPV6, +}; + +enum devlink_resource_unit { + DEVLINK_RESOURCE_UNIT_ENTRY, +}; + +enum devlink_port_function_attr { + DEVLINK_PORT_FUNCTION_ATTR_UNSPEC, + DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */ + + __DEVLINK_PORT_FUNCTION_ATTR_MAX, + DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1 +}; + +#endif /* _LINUX_DEVLINK_H_ */ diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h new file mode 100644 index 0000000..f47e853 --- /dev/null +++ b/include/uapi/linux/elf-em.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_ELF_EM_H +#define _LINUX_ELF_EM_H + +/* These constants define the various ELF target machines */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 +#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ + /* Next two are historical and binaries and + modules of these types will be rejected by + Linux. */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ +#define EM_SPU 23 /* Cell BE SPU */ +#define EM_ARM 40 /* ARM 32 bit */ +#define EM_SH 42 /* SuperH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_H8_300 46 /* Renesas H8/300 */ +#define EM_IA_64 50 /* HP/Intel IA-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_S390 22 /* IBM S/390 */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_M32R 88 /* Renesas M32R */ +#define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARCOMPACT 93 /* ARCompact processor */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_BLACKFIN 106 /* ADI Blackfin Processor */ +#define EM_UNICORE 110 /* UniCore-32 */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ +#define EM_TI_C6000 140 /* TI C6X DSPs */ +#define EM_HEXAGON 164 /* QUALCOMM Hexagon */ +#define EM_NDS32 167 /* Andes Technology compact code size + embedded RISC processor family */ +#define EM_AARCH64 183 /* ARM 64 bit */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_ARCV2 195 /* ARCv2 Cores */ +#define EM_RISCV 243 /* RISC-V */ +#define EM_BPF 247 /* Linux BPF - in-kernel virtual machine */ +#define EM_CSKY 252 /* C-SKY */ +#define EM_FRV 0x5441 /* Fujitsu FR-V */ + +/* + * This is an interim value that we will use until the committee comes + * up with a final number. + */ +#define EM_ALPHA 0x9026 + +/* Bogus old m32r magic number, used by old tools. */ +#define EM_CYGNUS_M32R 0x9041 +/* This is the old interim value for S/390 architecture */ +#define EM_S390_OLD 0xA390 +/* Also Panasonic/MEI MN10300, AM33 */ +#define EM_CYGNUS_MN10300 0xbeef + + +#endif /* _LINUX_ELF_EM_H */ diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h new file mode 100644 index 0000000..232df14 --- /dev/null +++ b/include/uapi/linux/fib_rules.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_FIB_RULES_H +#define __LINUX_FIB_RULES_H + +#include +#include + +/* rule is permanent, and cannot be deleted */ +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_IIF_DETACHED 0x00000008 +#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED +#define FIB_RULE_OIF_DETACHED 0x00000010 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 + +struct fib_rule_hdr { + __u8 family; + __u8 dst_len; + __u8 src_len; + __u8 tos; + + __u8 table; + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ + __u8 action; + + __u32 flags; +}; + +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + +enum { + FRA_UNSPEC, + FRA_DST, /* destination address */ + FRA_SRC, /* source address */ + FRA_IIFNAME, /* interface name */ +#define FRA_IFNAME FRA_IIFNAME + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2, + FRA_PRIORITY, /* priority/preference */ + FRA_UNUSED3, + FRA_UNUSED4, + FRA_UNUSED5, + FRA_FWMARK, /* mark */ + FRA_FLOW, /* flow/class id */ + FRA_TUN_ID, + FRA_SUPPRESS_IFGROUP, + FRA_SUPPRESS_PREFIXLEN, + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ + FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ + __FRA_MAX +}; + +#define FRA_MAX (__FRA_MAX - 1) + +enum { + FR_ACT_UNSPEC, + FR_ACT_TO_TBL, /* Pass to fixed table */ + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ + FR_ACT_RES3, + FR_ACT_RES4, + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ + __FR_ACT_MAX, +}; + +#define FR_ACT_MAX (__FR_ACT_MAX - 1) + +#endif diff --git a/include/uapi/linux/filter.h b/include/uapi/linux/filter.h new file mode 100644 index 0000000..eaef459 --- /dev/null +++ b/include/uapi/linux/filter.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Linux Socket Filter Data Structures + */ + +#ifndef __LINUX_FILTER_H__ +#define __LINUX_FILTER_H__ + + +#include +#include + +/* + * Current version of the filter code architecture. + */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * Try and keep these values and structures similar to BSD, especially + * the BPF code definitions which need to match so you can share filters + */ + +struct sock_filter { /* Filter block */ + __u16 code; /* Actual filter code */ + __u8 jt; /* Jump true */ + __u8 jf; /* Jump false */ + __u32 k; /* Generic multiuse field */ +}; + +struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ + unsigned short len; /* Number of filter blocks */ + struct sock_filter *filter; +}; + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * Macros for filter block array initializers. + */ +#ifndef BPF_STMT +#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k } +#endif +#ifndef BPF_JUMP +#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k } +#endif + +/* + * Number of scratch memory words for: BPF_ST and BPF_STX + */ +#define BPF_MEMWORDS 16 + +/* RATIONALE. Negative offsets are invalid in BPF. + We use them to reference ancillary data. + Unlike introduction new instructions, it does not break + existing compilers/optimizers. + */ +#define SKF_AD_OFF (-0x1000) +#define SKF_AD_PROTOCOL 0 +#define SKF_AD_PKTTYPE 4 +#define SKF_AD_IFINDEX 8 +#define SKF_AD_NLATTR 12 +#define SKF_AD_NLATTR_NEST 16 +#define SKF_AD_MARK 20 +#define SKF_AD_QUEUE 24 +#define SKF_AD_HATYPE 28 +#define SKF_AD_RXHASH 32 +#define SKF_AD_CPU 36 +#define SKF_AD_ALU_XOR_X 40 +#define SKF_AD_VLAN_TAG 44 +#define SKF_AD_VLAN_TAG_PRESENT 48 +#define SKF_AD_PAY_OFFSET 52 +#define SKF_AD_RANDOM 56 +#define SKF_AD_VLAN_TPID 60 +#define SKF_AD_MAX 64 + +#define SKF_NET_OFF (-0x100000) +#define SKF_LL_OFF (-0x200000) + +#define BPF_NET_OFF SKF_NET_OFF +#define BPF_LL_OFF SKF_LL_OFF + +#endif /* __LINUX_FILTER_H__ */ diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h new file mode 100644 index 0000000..9f91511 --- /dev/null +++ b/include/uapi/linux/fou.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* fou.h - FOU Interface */ + +#ifndef _LINUX_FOU_H +#define _LINUX_FOU_H + +/* NETLINK_GENERIC related info + */ +#define FOU_GENL_NAME "fou" +#define FOU_GENL_VERSION 0x1 + +enum { + FOU_ATTR_UNSPEC, + FOU_ATTR_PORT, /* u16 */ + FOU_ATTR_AF, /* u8 */ + FOU_ATTR_IPPROTO, /* u8 */ + FOU_ATTR_TYPE, /* u8 */ + FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */ + FOU_ATTR_LOCAL_V4, /* u32 */ + FOU_ATTR_LOCAL_V6, /* in6_addr */ + FOU_ATTR_PEER_V4, /* u32 */ + FOU_ATTR_PEER_V6, /* in6_addr */ + FOU_ATTR_PEER_PORT, /* u16 */ + FOU_ATTR_IFINDEX, /* s32 */ + + __FOU_ATTR_MAX, +}; + +#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1) + +enum { + FOU_CMD_UNSPEC, + FOU_CMD_ADD, + FOU_CMD_DEL, + FOU_CMD_GET, + + __FOU_CMD_MAX, +}; + +enum { + FOU_ENCAP_UNSPEC, + FOU_ENCAP_DIRECT, + FOU_ENCAP_GUE, +}; + +#define FOU_CMD_MAX (__FOU_CMD_MAX - 1) + +#endif /* _LINUX_FOU_H */ diff --git a/include/uapi/linux/gen_stats.h b/include/uapi/linux/gen_stats.h new file mode 100644 index 0000000..852f234 --- /dev/null +++ b/include/uapi/linux/gen_stats.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + TCA_STATS_RATE_EST64, + TCA_STATS_PAD, + TCA_STATS_BASIC_HW, + TCA_STATS_PKT64, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * struct gnet_stats_basic - byte/packet throughput statistics + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic { + __u64 bytes; + __u32 packets; +}; + +/** + * struct gnet_stats_rate_est - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est { + __u32 bps; + __u32 pps; +}; + +/** + * struct gnet_stats_rate_est64 - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est64 { + __u64 bps; + __u64 pps; +}; + +/** + * struct gnet_stats_queue - queuing statistics + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + * @overlimits: number of enqueues over the limit + */ +struct gnet_stats_queue { + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * struct gnet_estimator - rate estimator configuration + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator { + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h new file mode 100644 index 0000000..7c6c390 --- /dev/null +++ b/include/uapi/linux/genetlink.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 +#define GENL_UNS_ADMIN_PERM 0x10 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_CTRL NLMSG_MIN_TYPE +#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1) +#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2) +/* must be last reserved + 1 */ +#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3) + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + CTRL_CMD_NEWMCAST_GRP, + CTRL_CMD_DELMCAST_GRP, + CTRL_CMD_GETMCAST_GRP, /* unused */ + CTRL_CMD_GETPOLICY, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + CTRL_ATTR_VERSION, + CTRL_ATTR_HDRSIZE, + CTRL_ATTR_MAXATTR, + CTRL_ATTR_OPS, + CTRL_ATTR_MCAST_GROUPS, + CTRL_ATTR_POLICY, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +enum { + CTRL_ATTR_OP_UNSPEC, + CTRL_ATTR_OP_ID, + CTRL_ATTR_OP_FLAGS, + __CTRL_ATTR_OP_MAX, +}; + +#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) + +enum { + CTRL_ATTR_MCAST_GRP_UNSPEC, + CTRL_ATTR_MCAST_GRP_NAME, + CTRL_ATTR_MCAST_GRP_ID, + __CTRL_ATTR_MCAST_GRP_MAX, +}; + +#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) + + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/include/uapi/linux/hdlc/ioctl.h b/include/uapi/linux/hdlc/ioctl.h new file mode 100644 index 0000000..b06341a --- /dev/null +++ b/include/uapi/linux/hdlc/ioctl.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __HDLC_IOCTL_H__ +#define __HDLC_IOCTL_H__ + + +#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */ + +#define CLOCK_DEFAULT 0 /* Default setting */ +#define CLOCK_EXT 1 /* External TX and RX clock - DTE */ +#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */ +#define CLOCK_TXINT 3 /* Internal TX and external RX clock */ +#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */ + + +#define ENCODING_DEFAULT 0 /* Default setting */ +#define ENCODING_NRZ 1 +#define ENCODING_NRZI 2 +#define ENCODING_FM_MARK 3 +#define ENCODING_FM_SPACE 4 +#define ENCODING_MANCHESTER 5 + + +#define PARITY_DEFAULT 0 /* Default setting */ +#define PARITY_NONE 1 /* No parity */ +#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */ +#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */ +#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */ +#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */ +#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */ +#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */ + +#define LMI_DEFAULT 0 /* Default setting */ +#define LMI_NONE 1 /* No LMI, all PVCs are static */ +#define LMI_ANSI 2 /* ANSI Annex D */ +#define LMI_CCITT 3 /* ITU-T Annex A */ +#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; +} sync_serial_settings; /* V.35, V.24, X.21 */ + +typedef struct { + unsigned int clock_rate; /* bits per second */ + unsigned int clock_type; /* internal, external, TX-internal etc. */ + unsigned short loopback; + unsigned int slot_map; +} te1_settings; /* T1, E1 */ + +typedef struct { + unsigned short encoding; + unsigned short parity; +} raw_hdlc_proto; + +typedef struct { + unsigned int t391; + unsigned int t392; + unsigned int n391; + unsigned int n392; + unsigned int n393; + unsigned short lmi; + unsigned short dce; /* 1 for DCE (network side) operation */ +} fr_proto; + +typedef struct { + unsigned int dlci; +} fr_proto_pvc; /* for creating/deleting FR PVCs */ + +typedef struct { + unsigned int dlci; + char master[IFNAMSIZ]; /* Name of master FRAD device */ +}fr_proto_pvc_info; /* for returning PVC information only */ + +typedef struct { + unsigned int interval; + unsigned int timeout; +} cisco_proto; + +typedef struct { + unsigned short dce; /* 1 for DCE (network side) operation */ + unsigned int modulo; /* modulo (8 = basic / 128 = extended) */ + unsigned int window; /* frame window size */ + unsigned int t1; /* timeout t1 */ + unsigned int t2; /* timeout t2 */ + unsigned int n2; /* frame retry counter */ +} x25_hdlc_proto; + +/* PPP doesn't need any info now - supply length = 0 to ioctl */ + +#endif /* __ASSEMBLY__ */ +#endif /* __HDLC_IOCTL_H__ */ diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h new file mode 100644 index 0000000..70b283f --- /dev/null +++ b/include/uapi/linux/icmpv6.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_ICMPV6_H +#define _LINUX_ICMPV6_H + +#include +#include + +struct icmp6hdr { + + __u8 icmp6_type; + __u8 icmp6_code; + __sum16 icmp6_cksum; + + + union { + __be32 un_data32[1]; + __be16 un_data16[2]; + __u8 un_data8[4]; + + struct icmpv6_echo { + __be16 identifier; + __be16 sequence; + } u_echo; + + struct icmpv6_nd_advt { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 reserved:5, + override:1, + solicited:1, + router:1, + reserved2:24; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 router:1, + solicited:1, + override:1, + reserved:29; +#else +#error "Please fix " +#endif + } u_nd_advt; + + struct icmpv6_nd_ra { + __u8 hop_limit; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 reserved:3, + router_pref:2, + home_agent:1, + other:1, + managed:1; + +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 managed:1, + other:1, + home_agent:1, + router_pref:2, + reserved:3; +#else +#error "Please fix " +#endif + __be16 rt_lifetime; + } u_nd_ra; + + } icmp6_dataun; + +#define icmp6_identifier icmp6_dataun.u_echo.identifier +#define icmp6_sequence icmp6_dataun.u_echo.sequence +#define icmp6_pointer icmp6_dataun.un_data32[0] +#define icmp6_mtu icmp6_dataun.un_data32[0] +#define icmp6_unused icmp6_dataun.un_data32[0] +#define icmp6_maxdelay icmp6_dataun.un_data16[0] +#define icmp6_datagram_len icmp6_dataun.un_data8[0] +#define icmp6_router icmp6_dataun.u_nd_advt.router +#define icmp6_solicited icmp6_dataun.u_nd_advt.solicited +#define icmp6_override icmp6_dataun.u_nd_advt.override +#define icmp6_ndiscreserved icmp6_dataun.u_nd_advt.reserved +#define icmp6_hop_limit icmp6_dataun.u_nd_ra.hop_limit +#define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed +#define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other +#define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime +#define icmp6_router_pref icmp6_dataun.u_nd_ra.router_pref +}; + + +#define ICMPV6_ROUTER_PREF_LOW 0x3 +#define ICMPV6_ROUTER_PREF_MEDIUM 0x0 +#define ICMPV6_ROUTER_PREF_HIGH 0x1 +#define ICMPV6_ROUTER_PREF_INVALID 0x2 + +#define ICMPV6_DEST_UNREACH 1 +#define ICMPV6_PKT_TOOBIG 2 +#define ICMPV6_TIME_EXCEED 3 +#define ICMPV6_PARAMPROB 4 + +#define ICMPV6_ERRMSG_MAX 127 + +#define ICMPV6_INFOMSG_MASK 0x80 + +#define ICMPV6_ECHO_REQUEST 128 +#define ICMPV6_ECHO_REPLY 129 +#define ICMPV6_MGM_QUERY 130 +#define ICMPV6_MGM_REPORT 131 +#define ICMPV6_MGM_REDUCTION 132 + +#define ICMPV6_NI_QUERY 139 +#define ICMPV6_NI_REPLY 140 + +#define ICMPV6_MLD2_REPORT 143 + +#define ICMPV6_DHAAD_REQUEST 144 +#define ICMPV6_DHAAD_REPLY 145 +#define ICMPV6_MOBILE_PREFIX_SOL 146 +#define ICMPV6_MOBILE_PREFIX_ADV 147 + +#define ICMPV6_MRDISC_ADV 151 + +#define ICMPV6_MSG_MAX 255 + +/* + * Codes for Destination Unreachable + */ +#define ICMPV6_NOROUTE 0 +#define ICMPV6_ADM_PROHIBITED 1 +#define ICMPV6_NOT_NEIGHBOUR 2 +#define ICMPV6_ADDR_UNREACH 3 +#define ICMPV6_PORT_UNREACH 4 +#define ICMPV6_POLICY_FAIL 5 +#define ICMPV6_REJECT_ROUTE 6 + +/* + * Codes for Time Exceeded + */ +#define ICMPV6_EXC_HOPLIMIT 0 +#define ICMPV6_EXC_FRAGTIME 1 + +/* + * Codes for Parameter Problem + */ +#define ICMPV6_HDR_FIELD 0 +#define ICMPV6_UNK_NEXTHDR 1 +#define ICMPV6_UNK_OPTION 2 + +/* + * constants for (set|get)sockopt + */ + +#define ICMPV6_FILTER 1 + +/* + * ICMPV6 filter + */ + +#define ICMPV6_FILTER_BLOCK 1 +#define ICMPV6_FILTER_PASS 2 +#define ICMPV6_FILTER_BLOCKOTHERS 3 +#define ICMPV6_FILTER_PASSONLY 4 + +struct icmp6_filter { + __u32 data[8]; +}; + +/* + * Definitions for MLDv2 + */ +#define MLD2_MODE_IS_INCLUDE 1 +#define MLD2_MODE_IS_EXCLUDE 2 +#define MLD2_CHANGE_TO_INCLUDE 3 +#define MLD2_CHANGE_TO_EXCLUDE 4 +#define MLD2_ALLOW_NEW_SOURCES 5 +#define MLD2_BLOCK_OLD_SOURCES 6 + +#define MLD2_ALL_MCR_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x16 } } } + + +#endif /* _LINUX_ICMPV6_H */ diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h new file mode 100644 index 0000000..b287b2a --- /dev/null +++ b/include/uapi/linux/if.h @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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 compatibility with glibc */ +#include /* for "__kernel_caddr_t" et al */ +#include /* for "struct sockaddr" et al */ + /* for "__user" et al */ + +#include /* for struct sockaddr. */ + +#if __UAPI_DEF_IF_IFNAMSIZ +#define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ +#define IFALIASZ 256 +#define ALTIFNAMSIZ 128 +#include + +/* For glibc compatibility. An empty enum does not compile. */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \ + __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 +/** + * enum net_device_flags - &struct net_device flags + * + * These are the &struct net_device flags, they can be set by drivers, the + * kernel and some can be triggered by userspace. Userspace can query and + * set these flags using userspace utilities but there is also a sysfs + * entry available for all dev flags which can be queried and set. These flags + * are shared for all types of net_devices. The sysfs entries are available + * via /sys/class/net//flags. Flags which can be toggled through sysfs + * are annotated below, note that only a few flags can be toggled and some + * other flags are always preserved from the original net_device flags + * even if you try to set them via sysfs. Flags which are always preserved + * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__ + * are annotated below as such. + * + * You should have a pretty good reason to be extending these flags. + * + * @IFF_UP: interface is up. Can be toggled through sysfs. + * @IFF_BROADCAST: broadcast address valid. Volatile. + * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs. + * @IFF_LOOPBACK: is a loopback net. Volatile. + * @IFF_POINTOPOINT: interface is has p-p link. Volatile. + * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs. + * Volatile. + * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile. + * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile. + * @IFF_PROMISC: receive all packets. Can be toggled through sysfs. + * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through + * sysfs. + * @IFF_MASTER: master of a load balancer. Volatile. + * @IFF_SLAVE: slave of a load balancer. Volatile. + * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs. + * @IFF_PORTSEL: can set media type. Can be toggled through sysfs. + * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs. + * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled + * through sysfs. + * @IFF_LOWER_UP: driver signals L1 up. Volatile. + * @IFF_DORMANT: driver signals dormant. Volatile. + * @IFF_ECHO: echo sent packets. Volatile. + */ +enum net_device_flags { +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS + IFF_UP = 1<<0, /* sysfs */ + IFF_BROADCAST = 1<<1, /* __volatile__ */ + IFF_DEBUG = 1<<2, /* sysfs */ + IFF_LOOPBACK = 1<<3, /* __volatile__ */ + IFF_POINTOPOINT = 1<<4, /* __volatile__ */ + IFF_NOTRAILERS = 1<<5, /* sysfs */ + IFF_RUNNING = 1<<6, /* __volatile__ */ + IFF_NOARP = 1<<7, /* sysfs */ + IFF_PROMISC = 1<<8, /* sysfs */ + IFF_ALLMULTI = 1<<9, /* sysfs */ + IFF_MASTER = 1<<10, /* __volatile__ */ + IFF_SLAVE = 1<<11, /* __volatile__ */ + IFF_MULTICAST = 1<<12, /* sysfs */ + IFF_PORTSEL = 1<<13, /* sysfs */ + IFF_AUTOMEDIA = 1<<14, /* sysfs */ + IFF_DYNAMIC = 1<<15, /* sysfs */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO + IFF_LOWER_UP = 1<<16, /* __volatile__ */ + IFF_DORMANT = 1<<17, /* __volatile__ */ + IFF_ECHO = 1<<18, /* __volatile__ */ +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ +}; +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define IFF_UP IFF_UP +#define IFF_BROADCAST IFF_BROADCAST +#define IFF_DEBUG IFF_DEBUG +#define IFF_LOOPBACK IFF_LOOPBACK +#define IFF_POINTOPOINT IFF_POINTOPOINT +#define IFF_NOTRAILERS IFF_NOTRAILERS +#define IFF_RUNNING IFF_RUNNING +#define IFF_NOARP IFF_NOARP +#define IFF_PROMISC IFF_PROMISC +#define IFF_ALLMULTI IFF_ALLMULTI +#define IFF_MASTER IFF_MASTER +#define IFF_SLAVE IFF_SLAVE +#define IFF_MULTICAST IFF_MULTICAST +#define IFF_PORTSEL IFF_PORTSEL +#define IFF_AUTOMEDIA IFF_AUTOMEDIA +#define IFF_DYNAMIC IFF_DYNAMIC +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */ + +#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define IFF_LOWER_UP IFF_LOWER_UP +#define IFF_DORMANT IFF_DORMANT +#define IFF_ECHO IFF_ECHO +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ + IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) + +#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 */ + IF_LINK_MODE_TESTING, /* limit upward transition to testing */ +}; + +/* + * 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. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFMAP +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 */ +}; +#endif /* __UAPI_DEF_IF_IFMAP */ + +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; + x25_hdlc_proto *x25; + + /* 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. + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFREQ +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; +}; +#endif /* __UAPI_DEF_IF_IFREQ */ + +#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). + */ + +/* for compatibility with glibc net/if.h */ +#if __UAPI_DEF_IF_IFCONF +struct ifconf { + int ifc_len; /* size of buffer */ + union { + char *ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +}; +#endif /* __UAPI_DEF_IF_IFCONF */ + +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ + +#endif /* _LINUX_IF_H */ diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h new file mode 100644 index 0000000..c4dd87f --- /dev/null +++ b/include/uapi/linux/if_addr.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include +#include + +struct ifaddrmsg { + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + * + * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. + * If present, the value from struct ifaddrmsg will be ignored. + */ +enum { + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + IFA_FLAGS, + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_TARGET_NETNSID, + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_DADFAILED 0x08 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 +#define IFA_F_MANAGETEMPADDR 0x100 +#define IFA_F_NOPREFIXROUTE 0x200 +#define IFA_F_MCAUTOJOIN 0x400 +#define IFA_F_STABLE_PRIVACY 0x800 + +struct ifa_cacheinfo { + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + +/* backwards compatibility for userspace */ +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +#endif diff --git a/include/uapi/linux/if_addrlabel.h b/include/uapi/linux/if_addrlabel.h new file mode 100644 index 0000000..d1f5974 --- /dev/null +++ b/include/uapi/linux/if_addrlabel.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * if_addrlabel.h - netlink interface for address labels + * + * Copyright (C)2007 USAGI/WIDE Project, All Rights Reserved. + * + * Authors: + * YOSHIFUJI Hideaki @ USAGI/WIDE + */ + +#ifndef __LINUX_IF_ADDRLABEL_H +#define __LINUX_IF_ADDRLABEL_H + +#include + +struct ifaddrlblmsg { + __u8 ifal_family; /* Address family */ + __u8 __ifal_reserved; /* Reserved */ + __u8 ifal_prefixlen; /* Prefix length */ + __u8 ifal_flags; /* Flags */ + __u32 ifal_index; /* Link index */ + __u32 ifal_seq; /* sequence number */ +}; + +enum { + IFAL_ADDRESS = 1, + IFAL_LABEL = 2, + __IFAL_MAX +}; + +#define IFAL_MAX (__IFAL_MAX - 1) + +#endif diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h new file mode 100644 index 0000000..bc2bcde --- /dev/null +++ b/include/uapi/linux/if_alg.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * if_alg: User-space algorithm interface + * + * Copyright (c) 2010 Herbert Xu + * + * 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_ALG_H +#define _LINUX_IF_ALG_H + +#include + +struct sockaddr_alg { + __u16 salg_family; + __u8 salg_type[14]; + __u32 salg_feat; + __u32 salg_mask; + __u8 salg_name[64]; +}; + +struct af_alg_iv { + __u32 ivlen; + __u8 iv[0]; +}; + +/* Socket options */ +#define ALG_SET_KEY 1 +#define ALG_SET_IV 2 +#define ALG_SET_OP 3 +#define ALG_SET_AEAD_ASSOCLEN 4 +#define ALG_SET_AEAD_AUTHSIZE 5 + +/* Operations */ +#define ALG_OP_DECRYPT 0 +#define ALG_OP_ENCRYPT 1 + +#endif /* _LINUX_IF_ALG_H */ diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h new file mode 100644 index 0000000..dbfbc22 --- /dev/null +++ b/include/uapi/linux/if_arp.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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 ARP (RFC 826) protocol. + * + * Version: @(#)if_arp.h 1.0.1 04/16/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 + * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. + * Ross Biro + * Fred N. van Kempen, + * Florian La Roche, + * Jonathan Layes + * Arnaldo Carvalho de Melo ARPHRD_HWX25 + * + * 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_ARP_H +#define _LINUX_IF_ARP_H + +#include + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 19 /* ATM */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /* EUI-64 */ +#define ARPHRD_INFINIBAND 32 /* InfiniBand */ + +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_RSRVD 260 /* Notional KISS type */ +#define ARPHRD_ADAPT 264 +#define ARPHRD_ROSE 270 +#define ARPHRD_X25 271 /* CCITT X.25 */ +#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +#define ARPHRD_CAN 280 /* Controller Area Network */ +#define ARPHRD_PPP 512 +#define ARPHRD_CISCO 513 /* Cisco HDLC */ +#define ARPHRD_HDLC ARPHRD_CISCO +#define ARPHRD_LAPB 516 /* LAPB */ +#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ +#define ARPHRD_RAWHDLC 518 /* Raw HDLC */ +#define ARPHRD_RAWIP 519 /* Raw IP */ + +#define ARPHRD_TUNNEL 768 /* IPIP tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ +#define ARPHRD_FRAD 770 /* Frame Relay Access Device */ +#define ARPHRD_SKIP 771 /* SKIP vif */ +#define ARPHRD_LOOPBACK 772 /* Loopback device */ +#define ARPHRD_LOCALTLK 773 /* Localtalk device */ +#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ +#define ARPHRD_BIF 775 /* AP1000 BIF */ +#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */ +#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */ +#define ARPHRD_IPGRE 778 /* GRE over IP */ +#define ARPHRD_PIMREG 779 /* PIMSM register interface */ +#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */ +#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ +#define ARPHRD_ECONET 782 /* Acorn Econet */ +#define ARPHRD_IRDA 783 /* Linux-IrDA */ +/* ARP works differently on different FC media .. so */ +#define ARPHRD_FCPP 784 /* Point to point fibrechannel */ +#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */ + /* 787->799 reserved for fibrechannel media types */ +#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */ +#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */ +#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ +#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ +#define ARPHRD_IEEE802154 804 +#define ARPHRD_IEEE802154_MONITOR 805 /* IEEE 802.15.4 network monitor */ + +#define ARPHRD_PHONET 820 /* PhoNet media type */ +#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ +#define ARPHRD_CAIF 822 /* CAIF media type */ +#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ +#define ARPHRD_NETLINK 824 /* Netlink header */ +#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ +#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */ + +#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ +#define ARPHRD_NONE 0xFFFE /* zero header length */ + +/* ARP protocol opcodes. */ +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ +#define ARPOP_RREQUEST 3 /* RARP request */ +#define ARPOP_RREPLY 4 /* RARP reply */ +#define ARPOP_InREQUEST 8 /* InARP request */ +#define ARPOP_InREPLY 9 /* InARP reply */ +#define ARPOP_NAK 10 /* (ATM)ARP NAK */ + + +/* ARP ioctl request. */ +struct arpreq { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ + char arp_dev[IFNAMSIZ]; +}; + +struct arpreq_old { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ +}; + +/* ARP Flag values. */ +#define ATF_COM 0x02 /* completed entry (ha valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ +#define ATF_NETMASK 0x20 /* want to use a netmask (only + for proxy entries) */ +#define ATF_DONTPUB 0x40 /* don't answer this addresses */ + +/* + * This structure defines an ethernet arp header. + */ + +struct arphdr { + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + +#if 0 + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +#endif + +}; + + +#endif /* _LINUX_IF_ARP_H */ diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h new file mode 100644 index 0000000..45f3750 --- /dev/null +++ b/include/uapi/linux/if_bonding.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ +/* + * 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 */ + +#define BOND_DEFAULT_RESEND_IGMP 1 /* Default number of IGMP membership reports */ + +/* 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) */ +#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ +#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ + +/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */ +#define LACP_STATE_LACP_ACTIVITY 0x1 +#define LACP_STATE_LACP_TIMEOUT 0x2 +#define LACP_STATE_AGGREGATION 0x4 +#define LACP_STATE_SYNCHRONIZATION 0x8 +#define LACP_STATE_COLLECTING 0x10 +#define LACP_STATE_DISTRIBUTING 0x20 +#define LACP_STATE_DEFAULTED 0x40 +#define LACP_STATE_EXPIRED 0x80 + +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]; +}; + +/* Embedded inside LINK_XSTATS_TYPE_BOND */ +enum { + BOND_XSTATS_UNSPEC, + BOND_XSTATS_3AD, + __BOND_XSTATS_MAX +}; +#define BOND_XSTATS_MAX (__BOND_XSTATS_MAX - 1) + +/* Embedded inside BOND_XSTATS_3AD */ +enum { + BOND_3AD_STAT_LACPDU_RX, + BOND_3AD_STAT_LACPDU_TX, + BOND_3AD_STAT_LACPDU_UNKNOWN_RX, + BOND_3AD_STAT_LACPDU_ILLEGAL_RX, + BOND_3AD_STAT_MARKER_RX, + BOND_3AD_STAT_MARKER_TX, + BOND_3AD_STAT_MARKER_RESP_RX, + BOND_3AD_STAT_MARKER_RESP_TX, + BOND_3AD_STAT_MARKER_UNKNOWN_RX, + BOND_3AD_STAT_PAD, + __BOND_3AD_STAT_MAX +}; +#define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1) + +#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: + */ + diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h new file mode 100644 index 0000000..0490db9 --- /dev/null +++ b/include/uapi/linux/if_bridge.h @@ -0,0 +1,575 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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 +#include +#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[ETH_ALEN]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u8 port_hi; + __u8 pad0; + __u16 unused; +}; + +/* Bridge Flags */ +#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ +#define BRIDGE_FLAGS_SELF 2 /* Bridge command to/from lowerdev */ + +#define BRIDGE_MODE_VEB 0 /* Default loopback mode */ +#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */ +#define BRIDGE_MODE_UNDEF 0xFFFF /* mode undefined */ + +/* Bridge management nested attributes + * [IFLA_AF_SPEC] = { + * [IFLA_BRIDGE_FLAGS] + * [IFLA_BRIDGE_MODE] + * [IFLA_BRIDGE_VLAN_INFO] + * } + */ +enum { + IFLA_BRIDGE_FLAGS, + IFLA_BRIDGE_MODE, + IFLA_BRIDGE_VLAN_INFO, + IFLA_BRIDGE_VLAN_TUNNEL_INFO, + IFLA_BRIDGE_MRP, + __IFLA_BRIDGE_MAX, +}; +#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) + +#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ +#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ +#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ +#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ +#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ +#define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */ + +struct bridge_vlan_info { + __u16 flags; + __u16 vid; +}; + +enum { + IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC, + IFLA_BRIDGE_VLAN_TUNNEL_ID, + IFLA_BRIDGE_VLAN_TUNNEL_VID, + IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, + __IFLA_BRIDGE_VLAN_TUNNEL_MAX, +}; + +#define IFLA_BRIDGE_VLAN_TUNNEL_MAX (__IFLA_BRIDGE_VLAN_TUNNEL_MAX - 1) + +struct bridge_vlan_xstats { + __u64 rx_bytes; + __u64 rx_packets; + __u64 tx_bytes; + __u64 tx_packets; + __u16 vid; + __u16 flags; + __u32 pad2; +}; + +enum { + IFLA_BRIDGE_MRP_UNSPEC, + IFLA_BRIDGE_MRP_INSTANCE, + IFLA_BRIDGE_MRP_PORT_STATE, + IFLA_BRIDGE_MRP_PORT_ROLE, + IFLA_BRIDGE_MRP_RING_STATE, + IFLA_BRIDGE_MRP_RING_ROLE, + IFLA_BRIDGE_MRP_START_TEST, + IFLA_BRIDGE_MRP_INFO, + IFLA_BRIDGE_MRP_IN_ROLE, + IFLA_BRIDGE_MRP_IN_STATE, + IFLA_BRIDGE_MRP_START_IN_TEST, + __IFLA_BRIDGE_MRP_MAX, +}; + +#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_INSTANCE_UNSPEC, + IFLA_BRIDGE_MRP_INSTANCE_RING_ID, + IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX, + IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX, + IFLA_BRIDGE_MRP_INSTANCE_PRIO, + __IFLA_BRIDGE_MRP_INSTANCE_MAX, +}; + +#define IFLA_BRIDGE_MRP_INSTANCE_MAX (__IFLA_BRIDGE_MRP_INSTANCE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_PORT_STATE_UNSPEC, + IFLA_BRIDGE_MRP_PORT_STATE_STATE, + __IFLA_BRIDGE_MRP_PORT_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_PORT_STATE_MAX (__IFLA_BRIDGE_MRP_PORT_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_PORT_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_PORT_ROLE_ROLE, + __IFLA_BRIDGE_MRP_PORT_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_PORT_ROLE_MAX (__IFLA_BRIDGE_MRP_PORT_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_RING_STATE_UNSPEC, + IFLA_BRIDGE_MRP_RING_STATE_RING_ID, + IFLA_BRIDGE_MRP_RING_STATE_STATE, + __IFLA_BRIDGE_MRP_RING_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_RING_STATE_MAX (__IFLA_BRIDGE_MRP_RING_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_RING_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_RING_ROLE_RING_ID, + IFLA_BRIDGE_MRP_RING_ROLE_ROLE, + __IFLA_BRIDGE_MRP_RING_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_RING_ROLE_MAX (__IFLA_BRIDGE_MRP_RING_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_START_TEST_UNSPEC, + IFLA_BRIDGE_MRP_START_TEST_RING_ID, + IFLA_BRIDGE_MRP_START_TEST_INTERVAL, + IFLA_BRIDGE_MRP_START_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_START_TEST_PERIOD, + IFLA_BRIDGE_MRP_START_TEST_MONITOR, + __IFLA_BRIDGE_MRP_START_TEST_MAX, +}; + +#define IFLA_BRIDGE_MRP_START_TEST_MAX (__IFLA_BRIDGE_MRP_START_TEST_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_INFO_UNSPEC, + IFLA_BRIDGE_MRP_INFO_RING_ID, + IFLA_BRIDGE_MRP_INFO_P_IFINDEX, + IFLA_BRIDGE_MRP_INFO_S_IFINDEX, + IFLA_BRIDGE_MRP_INFO_PRIO, + IFLA_BRIDGE_MRP_INFO_RING_STATE, + IFLA_BRIDGE_MRP_INFO_RING_ROLE, + IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL, + IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_INFO_TEST_MONITOR, + IFLA_BRIDGE_MRP_INFO_I_IFINDEX, + IFLA_BRIDGE_MRP_INFO_IN_STATE, + IFLA_BRIDGE_MRP_INFO_IN_ROLE, + IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL, + IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS, + __IFLA_BRIDGE_MRP_INFO_MAX, +}; + +#define IFLA_BRIDGE_MRP_INFO_MAX (__IFLA_BRIDGE_MRP_INFO_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_IN_STATE_UNSPEC, + IFLA_BRIDGE_MRP_IN_STATE_IN_ID, + IFLA_BRIDGE_MRP_IN_STATE_STATE, + __IFLA_BRIDGE_MRP_IN_STATE_MAX, +}; + +#define IFLA_BRIDGE_MRP_IN_STATE_MAX (__IFLA_BRIDGE_MRP_IN_STATE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_IN_ROLE_UNSPEC, + IFLA_BRIDGE_MRP_IN_ROLE_RING_ID, + IFLA_BRIDGE_MRP_IN_ROLE_IN_ID, + IFLA_BRIDGE_MRP_IN_ROLE_ROLE, + IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX, + __IFLA_BRIDGE_MRP_IN_ROLE_MAX, +}; + +#define IFLA_BRIDGE_MRP_IN_ROLE_MAX (__IFLA_BRIDGE_MRP_IN_ROLE_MAX - 1) + +enum { + IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC, + IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID, + IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, + IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS, + IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD, + __IFLA_BRIDGE_MRP_START_IN_TEST_MAX, +}; + +#define IFLA_BRIDGE_MRP_START_IN_TEST_MAX (__IFLA_BRIDGE_MRP_START_IN_TEST_MAX - 1) + +struct br_mrp_instance { + __u32 ring_id; + __u32 p_ifindex; + __u32 s_ifindex; + __u16 prio; +}; + +struct br_mrp_ring_state { + __u32 ring_id; + __u32 ring_state; +}; + +struct br_mrp_ring_role { + __u32 ring_id; + __u32 ring_role; +}; + +struct br_mrp_start_test { + __u32 ring_id; + __u32 interval; + __u32 max_miss; + __u32 period; + __u32 monitor; +}; + +struct br_mrp_in_state { + __u32 in_state; + __u16 in_id; +}; + +struct br_mrp_in_role { + __u32 ring_id; + __u32 in_role; + __u32 i_ifindex; + __u16 in_id; +}; + +struct br_mrp_start_in_test { + __u32 interval; + __u32 max_miss; + __u32 period; + __u16 in_id; +}; + +struct bridge_stp_xstats { + __u64 transition_blk; + __u64 transition_fwd; + __u64 rx_bpdu; + __u64 tx_bpdu; + __u64 rx_tcn; + __u64 tx_tcn; +}; + +/* Bridge vlan RTM header */ +struct br_vlan_msg { + __u8 family; + __u8 reserved1; + __u16 reserved2; + __u32 ifindex; +}; + +enum { + BRIDGE_VLANDB_DUMP_UNSPEC, + BRIDGE_VLANDB_DUMP_FLAGS, + __BRIDGE_VLANDB_DUMP_MAX, +}; +#define BRIDGE_VLANDB_DUMP_MAX (__BRIDGE_VLANDB_DUMP_MAX - 1) + +/* flags used in BRIDGE_VLANDB_DUMP_FLAGS attribute to affect dumps */ +#define BRIDGE_VLANDB_DUMPF_STATS (1 << 0) /* Include stats in the dump */ + +/* Bridge vlan RTM attributes + * [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_INFO] + * ... + * } + */ +enum { + BRIDGE_VLANDB_UNSPEC, + BRIDGE_VLANDB_ENTRY, + __BRIDGE_VLANDB_MAX, +}; +#define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1) + +enum { + BRIDGE_VLANDB_ENTRY_UNSPEC, + BRIDGE_VLANDB_ENTRY_INFO, + BRIDGE_VLANDB_ENTRY_RANGE, + BRIDGE_VLANDB_ENTRY_STATE, + BRIDGE_VLANDB_ENTRY_TUNNEL_INFO, + BRIDGE_VLANDB_ENTRY_STATS, + __BRIDGE_VLANDB_ENTRY_MAX, +}; +#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) + +/* [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { + * [BRIDGE_VLANDB_TINFO_ID] + * ... + * } + * } + */ +enum { + BRIDGE_VLANDB_TINFO_UNSPEC, + BRIDGE_VLANDB_TINFO_ID, + BRIDGE_VLANDB_TINFO_CMD, + __BRIDGE_VLANDB_TINFO_MAX, +}; +#define BRIDGE_VLANDB_TINFO_MAX (__BRIDGE_VLANDB_TINFO_MAX - 1) + +/* [BRIDGE_VLANDB_ENTRY] = { + * [BRIDGE_VLANDB_ENTRY_STATS] = { + * [BRIDGE_VLANDB_STATS_RX_BYTES] + * ... + * } + * ... + * } + */ +enum { + BRIDGE_VLANDB_STATS_UNSPEC, + BRIDGE_VLANDB_STATS_RX_BYTES, + BRIDGE_VLANDB_STATS_RX_PACKETS, + BRIDGE_VLANDB_STATS_TX_BYTES, + BRIDGE_VLANDB_STATS_TX_PACKETS, + BRIDGE_VLANDB_STATS_PAD, + __BRIDGE_VLANDB_STATS_MAX, +}; +#define BRIDGE_VLANDB_STATS_MAX (__BRIDGE_VLANDB_STATS_MAX - 1) + +/* Bridge multicast database attributes + * [MDBA_MDB] = { + * [MDBA_MDB_ENTRY] = { + * [MDBA_MDB_ENTRY_INFO] { + * struct br_mdb_entry + * [MDBA_MDB_EATTR attributes] + * } + * } + * } + * [MDBA_ROUTER] = { + * [MDBA_ROUTER_PORT] = { + * u32 ifindex + * [MDBA_ROUTER_PATTR attributes] + * } + * } + */ +enum { + MDBA_UNSPEC, + MDBA_MDB, + MDBA_ROUTER, + __MDBA_MAX, +}; +#define MDBA_MAX (__MDBA_MAX - 1) + +enum { + MDBA_MDB_UNSPEC, + MDBA_MDB_ENTRY, + __MDBA_MDB_MAX, +}; +#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1) + +enum { + MDBA_MDB_ENTRY_UNSPEC, + MDBA_MDB_ENTRY_INFO, + __MDBA_MDB_ENTRY_MAX, +}; +#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1) + +/* per mdb entry additional attributes */ +enum { + MDBA_MDB_EATTR_UNSPEC, + MDBA_MDB_EATTR_TIMER, + __MDBA_MDB_EATTR_MAX +}; +#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1) + +/* multicast router types */ +enum { + MDB_RTR_TYPE_DISABLED, + MDB_RTR_TYPE_TEMP_QUERY, + MDB_RTR_TYPE_PERM, + MDB_RTR_TYPE_TEMP +}; + +enum { + MDBA_ROUTER_UNSPEC, + MDBA_ROUTER_PORT, + __MDBA_ROUTER_MAX, +}; +#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1) + +/* router port attributes */ +enum { + MDBA_ROUTER_PATTR_UNSPEC, + MDBA_ROUTER_PATTR_TIMER, + MDBA_ROUTER_PATTR_TYPE, + __MDBA_ROUTER_PATTR_MAX +}; +#define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1) + +struct br_port_msg { + __u8 family; + __u32 ifindex; +}; + +struct br_mdb_entry { + __u32 ifindex; +#define MDB_TEMPORARY 0 +#define MDB_PERMANENT 1 + __u8 state; +#define MDB_FLAGS_OFFLOAD (1 << 0) +#define MDB_FLAGS_FAST_LEAVE (1 << 1) + __u8 flags; + __u16 vid; + struct { + union { + __be32 ip4; + struct in6_addr ip6; + } u; + __be16 proto; + } addr; +}; + +enum { + MDBA_SET_ENTRY_UNSPEC, + MDBA_SET_ENTRY, + __MDBA_SET_ENTRY_MAX, +}; +#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1) + +/* Embedded inside LINK_XSTATS_TYPE_BRIDGE */ +enum { + BRIDGE_XSTATS_UNSPEC, + BRIDGE_XSTATS_VLAN, + BRIDGE_XSTATS_MCAST, + BRIDGE_XSTATS_PAD, + BRIDGE_XSTATS_STP, + __BRIDGE_XSTATS_MAX +}; +#define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1) + +enum { + BR_MCAST_DIR_RX, + BR_MCAST_DIR_TX, + BR_MCAST_DIR_SIZE +}; + +/* IGMP/MLD statistics */ +struct br_mcast_stats { + __u64 igmp_v1queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3queries[BR_MCAST_DIR_SIZE]; + __u64 igmp_leaves[BR_MCAST_DIR_SIZE]; + __u64 igmp_v1reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v2reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_v3reports[BR_MCAST_DIR_SIZE]; + __u64 igmp_parse_errors; + + __u64 mld_v1queries[BR_MCAST_DIR_SIZE]; + __u64 mld_v2queries[BR_MCAST_DIR_SIZE]; + __u64 mld_leaves[BR_MCAST_DIR_SIZE]; + __u64 mld_v1reports[BR_MCAST_DIR_SIZE]; + __u64 mld_v2reports[BR_MCAST_DIR_SIZE]; + __u64 mld_parse_errors; + + __u64 mcast_bytes[BR_MCAST_DIR_SIZE]; + __u64 mcast_packets[BR_MCAST_DIR_SIZE]; +}; + +/* bridge boolean options + * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets + * + * IMPORTANT: if adding a new option do not forget to handle + * it in br_boolopt_toggle/get and bridge sysfs + */ +enum br_boolopt_id { + BR_BOOLOPT_NO_LL_LEARN, + BR_BOOLOPT_MAX +}; + +/* struct br_boolopt_multi - change multiple bridge boolean options + * + * @optval: new option values (bit per option) + * @optmask: options to change (bit per option) + */ +struct br_boolopt_multi { + __u32 optval; + __u32 optmask; +}; +#endif /* _LINUX_IF_BRIDGE_H */ diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h new file mode 100644 index 0000000..1a0c7df --- /dev/null +++ b/include/uapi/linux/if_ether.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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 Ethernet IEEE 802.3 interface. + * + * Version: @(#)if_ether.h 1.0.1a 02/08/94 + * + * Author: Fred N. van Kempen, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * + * 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_ETHER_H +#define _LINUX_IF_ETHER_H + +#include + +/* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble + * and FCS/CRC (frame check sequence). + */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_TLEN 2 /* Octets in ethernet type field */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define ETH_FCS_LEN 4 /* Octets in the FCS */ + +#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */ +#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */ + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */ +#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ +#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ +#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ +#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */ +#define ETH_P_TIPC 0x88CA /* TIPC */ +#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */ +#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */ +#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ +#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ +#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_NCSI 0x88F8 /* NCSI protocol */ +#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ +#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */ +#define ETH_P_TDLS 0x890D /* TDLS */ +#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ +#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ +#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ +#define ETH_P_NSH 0x894F /* Network Service Header */ +#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ +#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ +#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ + +#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is less than this value + * then the frame is Ethernet II. Else it is 802.3 */ + +/* + * Non DIX types. Won't clash for 1500 types. + */ + +#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ +#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ +#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ +#define ETH_P_802_2 0x0004 /* 802.2 frames */ +#define ETH_P_SNAP 0x0005 /* Internal only */ +#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */ +#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ +#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ +#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ +#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ +#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ +#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ +#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ +#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ +#define ETH_P_CONTROL 0x0016 /* Card specific control frames */ +#define ETH_P_IRDA 0x0017 /* Linux-IrDA */ +#define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ +#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ +#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */ +#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */ +#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */ +#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ +#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ +#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ +#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and + * aggregation protocol + */ + +/* + * This is an Ethernet frame header. + */ + +/* allow libcs like musl to deactivate this, glibc does not implement this. */ +#ifndef __UAPI_DEF_ETHHDR +#define __UAPI_DEF_ETHHDR 1 +#endif + +#if __UAPI_DEF_ETHHDR +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); +#endif + + +#endif /* _LINUX_IF_ETHER_H */ diff --git a/include/uapi/linux/if_infiniband.h b/include/uapi/linux/if_infiniband.h new file mode 100644 index 0000000..0fc33bf --- /dev/null +++ b/include/uapi/linux/if_infiniband.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ +/* + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available at + * , or the OpenIB.org BSD + * license, available in the LICENSE.TXT file accompanying this + * software. These details are also available at + * . + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Copyright (c) 2004 Topspin Communications. All rights reserved. + * + * $Id$ + */ + +#ifndef _LINUX_IF_INFINIBAND_H +#define _LINUX_IF_INFINIBAND_H + +#define INFINIBAND_ALEN 20 /* Octets in IPoIB HW addr */ + +#endif /* _LINUX_IF_INFINIBAND_H */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h new file mode 100644 index 0000000..b1bdcfb --- /dev/null +++ b/include/uapi/linux/if_link.h @@ -0,0 +1,1077 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#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; + + __u32 rx_nohandler; /* dropped, no handler found */ +}; + +/* 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; + + __u64 rx_nohandler; /* dropped, no handler found */ +}; + +/* 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; +}; + +/* + * IFLA_AF_SPEC + * Contains nested attributes for address family specific attributes. + * Each address family may create a attribute with the address family + * number as type and create its own attribute structure in it. + * + * Example: + * [IFLA_AF_SPEC] = { + * [AF_INET] = { + * [IFLA_INET_CONF] = ..., + * }, + * [AF_INET6] = { + * [IFLA_INET6_FLAGS] = ..., + * [IFLA_INET6_CONF] = ..., + * } + * } + */ + +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_PHYS_PORT_ID, + IFLA_CARRIER_CHANGES, + IFLA_PHYS_SWITCH_ID, + IFLA_LINK_NETNSID, + IFLA_PHYS_PORT_NAME, + IFLA_PROTO_DOWN, + IFLA_GSO_MAX_SEGS, + IFLA_GSO_MAX_SIZE, + IFLA_PAD, + IFLA_XDP, + IFLA_EVENT, + IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, + IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */ + IFLA_CARRIER_UP_COUNT, + IFLA_CARRIER_DOWN_COUNT, + IFLA_NEW_IFINDEX, + IFLA_MIN_MTU, + IFLA_MAX_MTU, + IFLA_PROP_LIST, + IFLA_ALT_IFNAME, /* Alternative ifname */ + IFLA_PERM_ADDRESS, + IFLA_PROTO_DOWN_REASON, + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +enum { + IFLA_PROTO_DOWN_REASON_UNSPEC, + IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */ + IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */ + + __IFLA_PROTO_DOWN_REASON_CNT, + IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1 +}; + +/* backwards compatibility for userspace */ +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +enum { + IFLA_INET_UNSPEC, + IFLA_INET_CONF, + __IFLA_INET_MAX, +}; + +#define IFLA_INET_MAX (__IFLA_INET_MAX - 1) + +/* 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_TOKEN, /* device token */ + IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +enum in6_addr_gen_mode { + IN6_ADDR_GEN_MODE_EUI64, + IN6_ADDR_GEN_MODE_NONE, + IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IN6_ADDR_GEN_MODE_RANDOM, +}; + +/* Bridge section */ + +enum { + IFLA_BR_UNSPEC, + IFLA_BR_FORWARD_DELAY, + IFLA_BR_HELLO_TIME, + IFLA_BR_MAX_AGE, + IFLA_BR_AGEING_TIME, + IFLA_BR_STP_STATE, + IFLA_BR_PRIORITY, + IFLA_BR_VLAN_FILTERING, + IFLA_BR_VLAN_PROTOCOL, + IFLA_BR_GROUP_FWD_MASK, + IFLA_BR_ROOT_ID, + IFLA_BR_BRIDGE_ID, + IFLA_BR_ROOT_PORT, + IFLA_BR_ROOT_PATH_COST, + IFLA_BR_TOPOLOGY_CHANGE, + IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + IFLA_BR_HELLO_TIMER, + IFLA_BR_TCN_TIMER, + IFLA_BR_TOPOLOGY_CHANGE_TIMER, + IFLA_BR_GC_TIMER, + IFLA_BR_GROUP_ADDR, + IFLA_BR_FDB_FLUSH, + IFLA_BR_MCAST_ROUTER, + IFLA_BR_MCAST_SNOOPING, + IFLA_BR_MCAST_QUERY_USE_IFADDR, + IFLA_BR_MCAST_QUERIER, + IFLA_BR_MCAST_HASH_ELASTICITY, + IFLA_BR_MCAST_HASH_MAX, + IFLA_BR_MCAST_LAST_MEMBER_CNT, + IFLA_BR_MCAST_STARTUP_QUERY_CNT, + IFLA_BR_MCAST_LAST_MEMBER_INTVL, + IFLA_BR_MCAST_MEMBERSHIP_INTVL, + IFLA_BR_MCAST_QUERIER_INTVL, + IFLA_BR_MCAST_QUERY_INTVL, + IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + IFLA_BR_NF_CALL_IPTABLES, + IFLA_BR_NF_CALL_IP6TABLES, + IFLA_BR_NF_CALL_ARPTABLES, + IFLA_BR_VLAN_DEFAULT_PVID, + IFLA_BR_PAD, + IFLA_BR_VLAN_STATS_ENABLED, + IFLA_BR_MCAST_STATS_ENABLED, + IFLA_BR_MCAST_IGMP_VERSION, + IFLA_BR_MCAST_MLD_VERSION, + IFLA_BR_VLAN_STATS_PER_PORT, + IFLA_BR_MULTI_BOOLOPT, + __IFLA_BR_MAX, +}; + +#define IFLA_BR_MAX (__IFLA_BR_MAX - 1) + +struct ifla_bridge_id { + __u8 prio[2]; + __u8 addr[6]; /* ETH_ALEN */ +}; + +enum { + BRIDGE_MODE_UNSPEC, + BRIDGE_MODE_HAIRPIN, +}; + +enum { + IFLA_BRPORT_UNSPEC, + IFLA_BRPORT_STATE, /* Spanning tree state */ + IFLA_BRPORT_PRIORITY, /* " priority */ + IFLA_BRPORT_COST, /* " cost */ + IFLA_BRPORT_MODE, /* mode (hairpin) */ + IFLA_BRPORT_GUARD, /* bpdu guard */ + IFLA_BRPORT_PROTECT, /* root port protection */ + IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + IFLA_BRPORT_LEARNING, /* mac learning */ + IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ + IFLA_BRPORT_PROXYARP, /* proxy ARP */ + IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */ + IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */ + IFLA_BRPORT_ROOT_ID, /* designated root */ + IFLA_BRPORT_BRIDGE_ID, /* designated bridge */ + IFLA_BRPORT_DESIGNATED_PORT, + IFLA_BRPORT_DESIGNATED_COST, + IFLA_BRPORT_ID, + IFLA_BRPORT_NO, + IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + IFLA_BRPORT_CONFIG_PENDING, + IFLA_BRPORT_MESSAGE_AGE_TIMER, + IFLA_BRPORT_FORWARD_DELAY_TIMER, + IFLA_BRPORT_HOLD_TIMER, + IFLA_BRPORT_FLUSH, + IFLA_BRPORT_MULTICAST_ROUTER, + IFLA_BRPORT_PAD, + IFLA_BRPORT_MCAST_FLOOD, + IFLA_BRPORT_MCAST_TO_UCAST, + IFLA_BRPORT_VLAN_TUNNEL, + IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, + IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, + IFLA_BRPORT_MRP_RING_OPEN, + IFLA_BRPORT_MRP_IN_OPEN, + __IFLA_BRPORT_MAX +}; +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_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_SLAVE_KIND, + IFLA_INFO_SLAVE_DATA, + __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_PROTOCOL, + __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_FLAGS, + IFLA_MACVLAN_MACADDR_MODE, + IFLA_MACVLAN_MACADDR, + IFLA_MACVLAN_MACADDR_DATA, + IFLA_MACVLAN_MACADDR_COUNT, + __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 */ + MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ + MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */ +}; + +enum macvlan_macaddr_mode { + MACVLAN_MACADDR_ADD, + MACVLAN_MACADDR_DEL, + MACVLAN_MACADDR_FLUSH, + MACVLAN_MACADDR_SET, +}; + +#define MACVLAN_FLAG_NOPROMISC 1 + +/* VRF section */ +enum { + IFLA_VRF_UNSPEC, + IFLA_VRF_TABLE, + __IFLA_VRF_MAX +}; + +#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1) + +enum { + IFLA_VRF_PORT_UNSPEC, + IFLA_VRF_PORT_TABLE, + __IFLA_VRF_PORT_MAX +}; + +#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1) + +/* MACSEC section */ +enum { + IFLA_MACSEC_UNSPEC, + IFLA_MACSEC_SCI, + IFLA_MACSEC_PORT, + IFLA_MACSEC_ICV_LEN, + IFLA_MACSEC_CIPHER_SUITE, + IFLA_MACSEC_WINDOW, + IFLA_MACSEC_ENCODING_SA, + IFLA_MACSEC_ENCRYPT, + IFLA_MACSEC_PROTECT, + IFLA_MACSEC_INC_SCI, + IFLA_MACSEC_ES, + IFLA_MACSEC_SCB, + IFLA_MACSEC_REPLAY_PROTECT, + IFLA_MACSEC_VALIDATION, + IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, + __IFLA_MACSEC_MAX, +}; + +#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) + +/* XFRM section */ +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + __IFLA_XFRM_MAX +}; + +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) + +enum macsec_validation_type { + MACSEC_VALIDATE_DISABLED = 0, + MACSEC_VALIDATE_CHECK = 1, + MACSEC_VALIDATE_STRICT = 2, + __MACSEC_VALIDATE_END, + MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, +}; + +enum macsec_offload { + MACSEC_OFFLOAD_OFF = 0, + MACSEC_OFFLOAD_PHY = 1, + MACSEC_OFFLOAD_MAC = 2, + __MACSEC_OFFLOAD_END, + MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, +}; + +/* IPVLAN section */ +enum { + IFLA_IPVLAN_UNSPEC, + IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, + __IFLA_IPVLAN_MAX +}; + +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) + +enum ipvlan_mode { + IPVLAN_MODE_L2 = 0, + IPVLAN_MODE_L3, + IPVLAN_MODE_L3S, + IPVLAN_MODE_MAX +}; + +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 + +/* VXLAN section */ +enum { + IFLA_VXLAN_UNSPEC, + IFLA_VXLAN_ID, + IFLA_VXLAN_GROUP, /* group or remote address */ + IFLA_VXLAN_LINK, + IFLA_VXLAN_LOCAL, + IFLA_VXLAN_TTL, + IFLA_VXLAN_TOS, + IFLA_VXLAN_LEARNING, + IFLA_VXLAN_AGEING, + IFLA_VXLAN_LIMIT, + IFLA_VXLAN_PORT_RANGE, /* source port */ + IFLA_VXLAN_PROXY, + IFLA_VXLAN_RSC, + IFLA_VXLAN_L2MISS, + IFLA_VXLAN_L3MISS, + IFLA_VXLAN_PORT, /* destination port */ + IFLA_VXLAN_GROUP6, + IFLA_VXLAN_LOCAL6, + IFLA_VXLAN_UDP_CSUM, + IFLA_VXLAN_UDP_ZERO_CSUM6_TX, + IFLA_VXLAN_UDP_ZERO_CSUM6_RX, + IFLA_VXLAN_REMCSUM_TX, + IFLA_VXLAN_REMCSUM_RX, + IFLA_VXLAN_GBP, + IFLA_VXLAN_REMCSUM_NOPARTIAL, + IFLA_VXLAN_COLLECT_METADATA, + IFLA_VXLAN_LABEL, + IFLA_VXLAN_GPE, + IFLA_VXLAN_TTL_INHERIT, + IFLA_VXLAN_DF, + __IFLA_VXLAN_MAX +}; +#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) + +struct ifla_vxlan_port_range { + __be16 low; + __be16 high; +}; + +enum ifla_vxlan_df { + VXLAN_DF_UNSET = 0, + VXLAN_DF_SET, + VXLAN_DF_INHERIT, + __VXLAN_DF_END, + VXLAN_DF_MAX = __VXLAN_DF_END - 1, +}; + +/* GENEVE section */ +enum { + IFLA_GENEVE_UNSPEC, + IFLA_GENEVE_ID, + IFLA_GENEVE_REMOTE, + IFLA_GENEVE_TTL, + IFLA_GENEVE_TOS, + IFLA_GENEVE_PORT, /* destination port */ + IFLA_GENEVE_COLLECT_METADATA, + IFLA_GENEVE_REMOTE6, + IFLA_GENEVE_UDP_CSUM, + IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + IFLA_GENEVE_LABEL, + IFLA_GENEVE_TTL_INHERIT, + IFLA_GENEVE_DF, + __IFLA_GENEVE_MAX +}; +#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) + +enum ifla_geneve_df { + GENEVE_DF_UNSET = 0, + GENEVE_DF_SET, + GENEVE_DF_INHERIT, + __GENEVE_DF_END, + GENEVE_DF_MAX = __GENEVE_DF_END - 1, +}; + +/* Bareudp section */ +enum { + IFLA_BAREUDP_UNSPEC, + IFLA_BAREUDP_PORT, + IFLA_BAREUDP_ETHERTYPE, + IFLA_BAREUDP_SRCPORT_MIN, + IFLA_BAREUDP_MULTIPROTO_MODE, + __IFLA_BAREUDP_MAX +}; + +#define IFLA_BAREUDP_MAX (__IFLA_BAREUDP_MAX - 1) + +/* PPP section */ +enum { + IFLA_PPP_UNSPEC, + IFLA_PPP_DEV_FD, + __IFLA_PPP_MAX +}; +#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) + +/* GTP section */ + +enum ifla_gtp_role { + GTP_ROLE_GGSN = 0, + GTP_ROLE_SGSN, +}; + +enum { + IFLA_GTP_UNSPEC, + IFLA_GTP_FD0, + IFLA_GTP_FD1, + IFLA_GTP_PDP_HASHSIZE, + IFLA_GTP_ROLE, + __IFLA_GTP_MAX, +}; +#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) + +/* Bonding section */ + +enum { + IFLA_BOND_UNSPEC, + IFLA_BOND_MODE, + IFLA_BOND_ACTIVE_SLAVE, + IFLA_BOND_MIIMON, + IFLA_BOND_UPDELAY, + IFLA_BOND_DOWNDELAY, + IFLA_BOND_USE_CARRIER, + IFLA_BOND_ARP_INTERVAL, + IFLA_BOND_ARP_IP_TARGET, + IFLA_BOND_ARP_VALIDATE, + IFLA_BOND_ARP_ALL_TARGETS, + IFLA_BOND_PRIMARY, + IFLA_BOND_PRIMARY_RESELECT, + IFLA_BOND_FAIL_OVER_MAC, + IFLA_BOND_XMIT_HASH_POLICY, + IFLA_BOND_RESEND_IGMP, + IFLA_BOND_NUM_PEER_NOTIF, + IFLA_BOND_ALL_SLAVES_ACTIVE, + IFLA_BOND_MIN_LINKS, + IFLA_BOND_LP_INTERVAL, + IFLA_BOND_PACKETS_PER_SLAVE, + IFLA_BOND_AD_LACP_RATE, + IFLA_BOND_AD_SELECT, + IFLA_BOND_AD_INFO, + IFLA_BOND_AD_ACTOR_SYS_PRIO, + IFLA_BOND_AD_USER_PORT_KEY, + IFLA_BOND_AD_ACTOR_SYSTEM, + IFLA_BOND_TLB_DYNAMIC_LB, + IFLA_BOND_PEER_NOTIF_DELAY, + __IFLA_BOND_MAX, +}; + +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) + +enum { + IFLA_BOND_AD_INFO_UNSPEC, + IFLA_BOND_AD_INFO_AGGREGATOR, + IFLA_BOND_AD_INFO_NUM_PORTS, + IFLA_BOND_AD_INFO_ACTOR_KEY, + IFLA_BOND_AD_INFO_PARTNER_KEY, + IFLA_BOND_AD_INFO_PARTNER_MAC, + __IFLA_BOND_AD_INFO_MAX, +}; + +#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1) + +enum { + IFLA_BOND_SLAVE_UNSPEC, + IFLA_BOND_SLAVE_STATE, + IFLA_BOND_SLAVE_MII_STATUS, + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, + IFLA_BOND_SLAVE_PERM_HWADDR, + IFLA_BOND_SLAVE_QUEUE_ID, + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, + __IFLA_BOND_SLAVE_MAX, +}; + +#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1) + +/* 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, /* VLAN ID and QoS */ + IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */ + IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */ + IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */ + IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */ + IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query + * on/off switch + */ + IFLA_VF_STATS, /* network device statistics */ + IFLA_VF_TRUST, /* Trust VF */ + IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */ + IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */ + IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */ + IFLA_VF_BROADCAST, /* VF broadcast */ + __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_broadcast { + __u8 broadcast[32]; +}; + +struct ifla_vf_vlan { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; +}; + +enum { + IFLA_VF_VLAN_INFO_UNSPEC, + IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */ + __IFLA_VF_VLAN_INFO_MAX, +}; + +#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1) +#define MAX_VLAN_LIST_LEN 1 + +struct ifla_vf_vlan_info { + __u32 vf; + __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + __u32 qos; + __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */ +}; + +struct ifla_vf_tx_rate { + __u32 vf; + __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */ +}; + +struct ifla_vf_rate { + __u32 vf; + __u32 min_tx_rate; /* Min Bandwidth in Mbps */ + __u32 max_tx_rate; /* Max Bandwidth in Mbps */ +}; + +struct ifla_vf_spoofchk { + __u32 vf; + __u32 setting; +}; + +struct ifla_vf_guid { + __u32 vf; + __u64 guid; +}; + +enum { + IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ + IFLA_VF_LINK_STATE_ENABLE, /* link always up */ + IFLA_VF_LINK_STATE_DISABLE, /* link always down */ + __IFLA_VF_LINK_STATE_MAX, +}; + +struct ifla_vf_link_state { + __u32 vf; + __u32 link_state; +}; + +struct ifla_vf_rss_query_en { + __u32 vf; + __u32 setting; +}; + +enum { + IFLA_VF_STATS_RX_PACKETS, + IFLA_VF_STATS_TX_PACKETS, + IFLA_VF_STATS_RX_BYTES, + IFLA_VF_STATS_TX_BYTES, + IFLA_VF_STATS_BROADCAST, + IFLA_VF_STATS_MULTICAST, + IFLA_VF_STATS_PAD, + IFLA_VF_STATS_RX_DROPPED, + IFLA_VF_STATS_TX_DROPPED, + __IFLA_VF_STATS_MAX, +}; + +#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1) + +struct ifla_vf_trust { + __u32 vf; + __u32 setting; +}; + +/* 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]; +}; + + +/* IPoIB section */ + +enum { + IFLA_IPOIB_UNSPEC, + IFLA_IPOIB_PKEY, + IFLA_IPOIB_MODE, + IFLA_IPOIB_UMCAST, + __IFLA_IPOIB_MAX +}; + +enum { + IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */ + IPOIB_MODE_CONNECTED = 1, /* using connected QPs */ +}; + +#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + + +/* HSR/PRP section, both uses same interface */ + +/* Different redundancy protocols for hsr device */ +enum { + HSR_PROTOCOL_HSR, + HSR_PROTOCOL_PRP, + HSR_PROTOCOL_MAX, +}; + +enum { + IFLA_HSR_UNSPEC, + IFLA_HSR_SLAVE1, + IFLA_HSR_SLAVE2, + IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */ + IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */ + IFLA_HSR_SEQ_NR, + IFLA_HSR_VERSION, /* HSR version */ + IFLA_HSR_PROTOCOL, /* Indicate different protocol than + * HSR. For example PRP. + */ + __IFLA_HSR_MAX, +}; + +#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) + +/* STATS section */ + +struct if_stats_msg { + __u8 family; + __u8 pad1; + __u16 pad2; + __u32 ifindex; + __u32 filter_mask; +}; + +/* A stats attribute can be netdev specific or a global stat. + * For netdev stats, lets use the prefix IFLA_STATS_LINK_* + */ +enum { + IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */ + IFLA_STATS_LINK_64, + IFLA_STATS_LINK_XSTATS, + IFLA_STATS_LINK_XSTATS_SLAVE, + IFLA_STATS_LINK_OFFLOAD_XSTATS, + IFLA_STATS_AF_SPEC, + __IFLA_STATS_MAX, +}; + +#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1) + +#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1)) + +/* These are embedded into IFLA_STATS_LINK_XSTATS: + * [IFLA_STATS_LINK_XSTATS] + * -> [LINK_XSTATS_TYPE_xxx] + * -> [rtnl link type specific attributes] + */ +enum { + LINK_XSTATS_TYPE_UNSPEC, + LINK_XSTATS_TYPE_BRIDGE, + LINK_XSTATS_TYPE_BOND, + __LINK_XSTATS_TYPE_MAX +}; +#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) + +/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */ +enum { + IFLA_OFFLOAD_XSTATS_UNSPEC, + IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */ + __IFLA_OFFLOAD_XSTATS_MAX +}; +#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1) + +/* XDP section */ + +#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) +#define XDP_FLAGS_SKB_MODE (1U << 1) +#define XDP_FLAGS_DRV_MODE (1U << 2) +#define XDP_FLAGS_HW_MODE (1U << 3) +#define XDP_FLAGS_REPLACE (1U << 4) +#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \ + XDP_FLAGS_DRV_MODE | \ + XDP_FLAGS_HW_MODE) +#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ + XDP_FLAGS_MODES | XDP_FLAGS_REPLACE) + +/* These are stored into IFLA_XDP_ATTACHED on dump. */ +enum { + XDP_ATTACHED_NONE = 0, + XDP_ATTACHED_DRV, + XDP_ATTACHED_SKB, + XDP_ATTACHED_HW, + XDP_ATTACHED_MULTI, +}; + +enum { + IFLA_XDP_UNSPEC, + IFLA_XDP_FD, + IFLA_XDP_ATTACHED, + IFLA_XDP_FLAGS, + IFLA_XDP_PROG_ID, + IFLA_XDP_DRV_PROG_ID, + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, + __IFLA_XDP_MAX, +}; + +#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) + +enum { + IFLA_EVENT_NONE, + IFLA_EVENT_REBOOT, /* internal reset / reboot */ + IFLA_EVENT_FEATURES, /* change in offload features */ + IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */ + IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */ + IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */ + IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ +}; + +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) + +enum { + IFLA_RMNET_UNSPEC, + IFLA_RMNET_MUX_ID, + IFLA_RMNET_FLAGS, + __IFLA_RMNET_MAX, +}; + +#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) + +struct ifla_rmnet_flags { + __u32 flags; + __u32 mask; +}; + +#endif /* _LINUX_IF_LINK_H */ diff --git a/include/uapi/linux/if_macsec.h b/include/uapi/linux/if_macsec.h new file mode 100644 index 0000000..eee31ce --- /dev/null +++ b/include/uapi/linux/if_macsec.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca + * + * 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 _MACSEC_H +#define _MACSEC_H + +#include + +#define MACSEC_GENL_NAME "macsec" +#define MACSEC_GENL_VERSION 1 + +#define MACSEC_MAX_KEY_LEN 128 + +#define MACSEC_KEYID_LEN 16 + +/* cipher IDs as per IEEE802.1AE-2018 (Table 14-1) */ +#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL +#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_128 0x0080C20001000003ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_256 0x0080C20001000004ULL + +/* deprecated cipher ID for GCM-AES-128 */ +#define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL +#define MACSEC_DEFAULT_CIPHER_ALT MACSEC_CIPHER_ID_GCM_AES_128 + +#define MACSEC_MIN_ICV_LEN 8 +#define MACSEC_MAX_ICV_LEN 32 +/* upper limit for ICV length as recommended by IEEE802.1AE-2006 */ +#define MACSEC_STD_ICV_LEN 16 + +enum macsec_attrs { + MACSEC_ATTR_UNSPEC, + MACSEC_ATTR_IFINDEX, /* u32, ifindex of the MACsec netdevice */ + MACSEC_ATTR_RXSC_CONFIG, /* config, nested macsec_rxsc_attrs */ + MACSEC_ATTR_SA_CONFIG, /* config, nested macsec_sa_attrs */ + MACSEC_ATTR_SECY, /* dump, nested macsec_secy_attrs */ + MACSEC_ATTR_TXSA_LIST, /* dump, nested, macsec_sa_attrs for each TXSA */ + MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ + MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ + MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + MACSEC_ATTR_OFFLOAD, /* config, nested, macsec_offload_attrs */ + __MACSEC_ATTR_END, + NUM_MACSEC_ATTR = __MACSEC_ATTR_END, + MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, +}; + +enum macsec_secy_attrs { + MACSEC_SECY_ATTR_UNSPEC, + MACSEC_SECY_ATTR_SCI, + MACSEC_SECY_ATTR_ENCODING_SA, + MACSEC_SECY_ATTR_WINDOW, + MACSEC_SECY_ATTR_CIPHER_SUITE, + MACSEC_SECY_ATTR_ICV_LEN, + MACSEC_SECY_ATTR_PROTECT, + MACSEC_SECY_ATTR_REPLAY, + MACSEC_SECY_ATTR_OPER, + MACSEC_SECY_ATTR_VALIDATE, + MACSEC_SECY_ATTR_ENCRYPT, + MACSEC_SECY_ATTR_INC_SCI, + MACSEC_SECY_ATTR_ES, + MACSEC_SECY_ATTR_SCB, + MACSEC_SECY_ATTR_PAD, + __MACSEC_SECY_ATTR_END, + NUM_MACSEC_SECY_ATTR = __MACSEC_SECY_ATTR_END, + MACSEC_SECY_ATTR_MAX = __MACSEC_SECY_ATTR_END - 1, +}; + +enum macsec_rxsc_attrs { + MACSEC_RXSC_ATTR_UNSPEC, + MACSEC_RXSC_ATTR_SCI, /* config/dump, u64 */ + MACSEC_RXSC_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_RXSC_ATTR_SA_LIST, /* dump, nested */ + MACSEC_RXSC_ATTR_STATS, /* dump, nested, macsec_rxsc_stats_attr */ + MACSEC_RXSC_ATTR_PAD, + __MACSEC_RXSC_ATTR_END, + NUM_MACSEC_RXSC_ATTR = __MACSEC_RXSC_ATTR_END, + MACSEC_RXSC_ATTR_MAX = __MACSEC_RXSC_ATTR_END - 1, +}; + +enum macsec_sa_attrs { + MACSEC_SA_ATTR_UNSPEC, + MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ + MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32/u64 (u64 if XPN) */ + MACSEC_SA_ATTR_KEY, /* config, data */ + MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ + MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ + MACSEC_SA_ATTR_PAD, + MACSEC_SA_ATTR_SSCI, /* config/dump, u32 - XPN only */ + MACSEC_SA_ATTR_SALT, /* config, 96-bit - XPN only */ + __MACSEC_SA_ATTR_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, +}; + +enum macsec_offload_attrs { + MACSEC_OFFLOAD_ATTR_UNSPEC, + MACSEC_OFFLOAD_ATTR_TYPE, /* config/dump, u8 0..2 */ + MACSEC_OFFLOAD_ATTR_PAD, + __MACSEC_OFFLOAD_ATTR_END, + NUM_MACSEC_OFFLOAD_ATTR = __MACSEC_OFFLOAD_ATTR_END, + MACSEC_OFFLOAD_ATTR_MAX = __MACSEC_OFFLOAD_ATTR_END - 1, +}; + +enum macsec_nl_commands { + MACSEC_CMD_GET_TXSC, + MACSEC_CMD_ADD_RXSC, + MACSEC_CMD_DEL_RXSC, + MACSEC_CMD_UPD_RXSC, + MACSEC_CMD_ADD_TXSA, + MACSEC_CMD_DEL_TXSA, + MACSEC_CMD_UPD_TXSA, + MACSEC_CMD_ADD_RXSA, + MACSEC_CMD_DEL_RXSA, + MACSEC_CMD_UPD_RXSA, + MACSEC_CMD_UPD_OFFLOAD, +}; + +/* u64 per-RXSC stats */ +enum macsec_rxsc_stats_attr { + MACSEC_RXSC_STATS_ATTR_UNSPEC, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, + MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_RXSC_STATS_ATTR_PAD, + __MACSEC_RXSC_STATS_ATTR_END, + NUM_MACSEC_RXSC_STATS_ATTR = __MACSEC_RXSC_STATS_ATTR_END, + MACSEC_RXSC_STATS_ATTR_MAX = __MACSEC_RXSC_STATS_ATTR_END - 1, +}; + +/* u32 per-{RX,TX}SA stats */ +enum macsec_sa_stats_attr { + MACSEC_SA_STATS_ATTR_UNSPEC, + MACSEC_SA_STATS_ATTR_IN_PKTS_OK, + MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + __MACSEC_SA_STATS_ATTR_END, + NUM_MACSEC_SA_STATS_ATTR = __MACSEC_SA_STATS_ATTR_END, + MACSEC_SA_STATS_ATTR_MAX = __MACSEC_SA_STATS_ATTR_END - 1, +}; + +/* u64 per-TXSC stats */ +enum macsec_txsc_stats_attr { + MACSEC_TXSC_STATS_ATTR_UNSPEC, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, + MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, + MACSEC_TXSC_STATS_ATTR_PAD, + __MACSEC_TXSC_STATS_ATTR_END, + NUM_MACSEC_TXSC_STATS_ATTR = __MACSEC_TXSC_STATS_ATTR_END, + MACSEC_TXSC_STATS_ATTR_MAX = __MACSEC_TXSC_STATS_ATTR_END - 1, +}; + +/* u64 per-SecY stats */ +enum macsec_secy_stats_attr { + MACSEC_SECY_STATS_ATTR_UNSPEC, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, + MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, + MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, + MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, + MACSEC_SECY_STATS_ATTR_PAD, + __MACSEC_SECY_STATS_ATTR_END, + NUM_MACSEC_SECY_STATS_ATTR = __MACSEC_SECY_STATS_ATTR_END, + MACSEC_SECY_STATS_ATTR_MAX = __MACSEC_SECY_STATS_ATTR_END - 1, +}; + +#endif /* _MACSEC_H */ diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h new file mode 100644 index 0000000..3d884d6 --- /dev/null +++ b/include/uapi/linux/if_packet.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_IF_PACKET_H +#define __LINUX_IF_PACKET_H + +#include + +struct sockaddr_pkt { + unsigned short spkt_family; + unsigned char spkt_device[14]; + __be16 spkt_protocol; +}; + +struct sockaddr_ll { + unsigned short sll_family; + __be16 sll_protocol; + int sll_ifindex; + unsigned short sll_hatype; + unsigned char sll_pkttype; + unsigned char sll_halen; + unsigned char sll_addr[8]; +}; + +/* Packet types */ + +#define PACKET_HOST 0 /* To us */ +#define PACKET_BROADCAST 1 /* To all */ +#define PACKET_MULTICAST 2 /* To group */ +#define PACKET_OTHERHOST 3 /* To someone else */ +#define PACKET_OUTGOING 4 /* Outgoing of any type */ +#define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */ +#define PACKET_USER 6 /* To user space */ +#define PACKET_KERNEL 7 /* To kernel space */ +/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ +#define PACKET_FASTROUTE 6 /* Fastrouted frame */ + +/* Packet socket options */ + +#define PACKET_ADD_MEMBERSHIP 1 +#define PACKET_DROP_MEMBERSHIP 2 +#define PACKET_RECV_OUTPUT 3 +/* Value 4 is still used by obsolete turbo-packet. */ +#define PACKET_RX_RING 5 +#define PACKET_STATISTICS 6 +#define PACKET_COPY_THRESH 7 +#define PACKET_AUXDATA 8 +#define PACKET_ORIGDEV 9 +#define PACKET_VERSION 10 +#define PACKET_HDRLEN 11 +#define PACKET_RESERVE 12 +#define PACKET_TX_RING 13 +#define PACKET_LOSS 14 +#define PACKET_VNET_HDR 15 +#define PACKET_TX_TIMESTAMP 16 +#define PACKET_TIMESTAMP 17 +#define PACKET_FANOUT 18 +#define PACKET_TX_HAS_OFF 19 +#define PACKET_QDISC_BYPASS 20 +#define PACKET_ROLLOVER_STATS 21 +#define PACKET_FANOUT_DATA 22 +#define PACKET_IGNORE_OUTGOING 23 + +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_ROLLOVER 3 +#define PACKET_FANOUT_RND 4 +#define PACKET_FANOUT_QM 5 +#define PACKET_FANOUT_CBPF 6 +#define PACKET_FANOUT_EBPF 7 +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#define PACKET_FANOUT_FLAG_UNIQUEID 0x2000 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +struct tpacket_stats { + unsigned int tp_packets; + unsigned int tp_drops; +}; + +struct tpacket_stats_v3 { + unsigned int tp_packets; + unsigned int tp_drops; + unsigned int tp_freeze_q_cnt; +}; + +struct tpacket_rollover_stats { + __aligned_u64 tp_all; + __aligned_u64 tp_huge; + __aligned_u64 tp_failed; +}; + +union tpacket_stats_u { + struct tpacket_stats stats1; + struct tpacket_stats_v3 stats3; +}; + +struct tpacket_auxdata { + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u16 tp_vlan_tci; + __u16 tp_vlan_tpid; +}; + +/* Rx ring - header status */ +#define TP_STATUS_KERNEL 0 +#define TP_STATUS_USER (1 << 0) +#define TP_STATUS_COPY (1 << 1) +#define TP_STATUS_LOSING (1 << 2) +#define TP_STATUS_CSUMNOTREADY (1 << 3) +#define TP_STATUS_VLAN_VALID (1 << 4) /* auxdata has valid tp_vlan_tci */ +#define TP_STATUS_BLK_TMO (1 << 5) +#define TP_STATUS_VLAN_TPID_VALID (1 << 6) /* auxdata has valid tp_vlan_tpid */ +#define TP_STATUS_CSUM_VALID (1 << 7) + +/* Tx ring - header status */ +#define TP_STATUS_AVAILABLE 0 +#define TP_STATUS_SEND_REQUEST (1 << 0) +#define TP_STATUS_SENDING (1 << 1) +#define TP_STATUS_WRONG_FORMAT (1 << 2) + +/* Rx and Tx ring - header status */ +#define TP_STATUS_TS_SOFTWARE (1 << 29) +#define TP_STATUS_TS_SYS_HARDWARE (1 << 30) /* deprecated, never set */ +#define TP_STATUS_TS_RAW_HARDWARE (1U << 31) + +/* Rx ring - feature request bits */ +#define TP_FT_REQ_FILL_RXHASH 0x1 + +struct tpacket_hdr { + unsigned long tp_status; + unsigned int tp_len; + unsigned int tp_snaplen; + unsigned short tp_mac; + unsigned short tp_net; + unsigned int tp_sec; + unsigned int tp_usec; +}; + +#define TPACKET_ALIGNMENT 16 +#define TPACKET_ALIGN(x) (((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1)) +#define TPACKET_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + sizeof(struct sockaddr_ll)) + +struct tpacket2_hdr { + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u32 tp_sec; + __u32 tp_nsec; + __u16 tp_vlan_tci; + __u16 tp_vlan_tpid; + __u8 tp_padding[4]; +}; + +struct tpacket_hdr_variant1 { + __u32 tp_rxhash; + __u32 tp_vlan_tci; + __u16 tp_vlan_tpid; + __u16 tp_padding; +}; + +struct tpacket3_hdr { + __u32 tp_next_offset; + __u32 tp_sec; + __u32 tp_nsec; + __u32 tp_snaplen; + __u32 tp_len; + __u32 tp_status; + __u16 tp_mac; + __u16 tp_net; + /* pkt_hdr variants */ + union { + struct tpacket_hdr_variant1 hv1; + }; + __u8 tp_padding[8]; +}; + +struct tpacket_bd_ts { + unsigned int ts_sec; + union { + unsigned int ts_usec; + unsigned int ts_nsec; + }; +}; + +struct tpacket_hdr_v1 { + __u32 block_status; + __u32 num_pkts; + __u32 offset_to_first_pkt; + + /* Number of valid bytes (including padding) + * blk_len <= tp_block_size + */ + __u32 blk_len; + + /* + * Quite a few uses of sequence number: + * 1. Make sure cache flush etc worked. + * Well, one can argue - why not use the increasing ts below? + * But look at 2. below first. + * 2. When you pass around blocks to other user space decoders, + * you can see which blk[s] is[are] outstanding etc. + * 3. Validate kernel code. + */ + __aligned_u64 seq_num; + + /* + * ts_last_pkt: + * + * Case 1. Block has 'N'(N >=1) packets and TMO'd(timed out) + * ts_last_pkt == 'time-stamp of last packet' and NOT the + * time when the timer fired and the block was closed. + * By providing the ts of the last packet we can absolutely + * guarantee that time-stamp wise, the first packet in the + * next block will never precede the last packet of the + * previous block. + * Case 2. Block has zero packets and TMO'd + * ts_last_pkt = time when the timer fired and the block + * was closed. + * Case 3. Block has 'N' packets and NO TMO. + * ts_last_pkt = time-stamp of the last pkt in the block. + * + * ts_first_pkt: + * Is always the time-stamp when the block was opened. + * Case a) ZERO packets + * No packets to deal with but atleast you know the + * time-interval of this block. + * Case b) Non-zero packets + * Use the ts of the first packet in the block. + * + */ + struct tpacket_bd_ts ts_first_pkt, ts_last_pkt; +}; + +union tpacket_bd_header_u { + struct tpacket_hdr_v1 bh1; +}; + +struct tpacket_block_desc { + __u32 version; + __u32 offset_to_priv; + union tpacket_bd_header_u hdr; +}; + +#define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) +#define TPACKET3_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket3_hdr)) + sizeof(struct sockaddr_ll)) + +enum tpacket_versions { + TPACKET_V1, + TPACKET_V2, + TPACKET_V3 +}; + +/* + Frame structure: + + - Start. Frame must be aligned to TPACKET_ALIGNMENT=16 + - struct tpacket_hdr + - pad to TPACKET_ALIGNMENT=16 + - struct sockaddr_ll + - Gap, chosen so that packet data (Start+tp_net) alignes to TPACKET_ALIGNMENT=16 + - Start+tp_mac: [ Optional MAC header ] + - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16. + - Pad to align to TPACKET_ALIGNMENT=16 + */ + +struct tpacket_req { + unsigned int tp_block_size; /* Minimal size of contiguous block */ + unsigned int tp_block_nr; /* Number of blocks */ + unsigned int tp_frame_size; /* Size of frame */ + unsigned int tp_frame_nr; /* Total number of frames */ +}; + +struct tpacket_req3 { + unsigned int tp_block_size; /* Minimal size of contiguous block */ + unsigned int tp_block_nr; /* Number of blocks */ + unsigned int tp_frame_size; /* Size of frame */ + unsigned int tp_frame_nr; /* Total number of frames */ + unsigned int tp_retire_blk_tov; /* timeout in msecs */ + unsigned int tp_sizeof_priv; /* offset to private data area */ + unsigned int tp_feature_req_word; +}; + +union tpacket_req_u { + struct tpacket_req req; + struct tpacket_req3 req3; +}; + +struct packet_mreq { + int mr_ifindex; + unsigned short mr_type; + unsigned short mr_alen; + unsigned char mr_address[8]; +}; + +#define PACKET_MR_MULTICAST 0 +#define PACKET_MR_PROMISC 1 +#define PACKET_MR_ALLMULTI 2 +#define PACKET_MR_UNICAST 3 + +#endif diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h new file mode 100644 index 0000000..8489ae0 --- /dev/null +++ b/include/uapi/linux/if_tun.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Universal TUN/TAP device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __IF_TUN_H +#define __IF_TUN_H + +#include +#include +#include + +/* Read queue size */ +#define TUN_READQ_SIZE 500 +/* TUN device type flags: deprecated. Use IFF_TUN/IFF_TAP instead. */ +#define TUN_TUN_DEV IFF_TUN +#define TUN_TAP_DEV IFF_TAP +#define TUN_TYPE_MASK 0x000f + +/* Ioctl defines */ +#define TUNSETNOCSUM _IOW('T', 200, int) +#define TUNSETDEBUG _IOW('T', 201, int) +#define TUNSETIFF _IOW('T', 202, int) +#define TUNSETPERSIST _IOW('T', 203, int) +#define TUNSETOWNER _IOW('T', 204, int) +#define TUNSETLINK _IOW('T', 205, int) +#define TUNSETGROUP _IOW('T', 206, int) +#define TUNGETFEATURES _IOR('T', 207, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETTXFILTER _IOW('T', 209, unsigned int) +#define TUNGETIFF _IOR('T', 210, unsigned int) +#define TUNGETSNDBUF _IOR('T', 211, int) +#define TUNSETSNDBUF _IOW('T', 212, int) +#define TUNATTACHFILTER _IOW('T', 213, struct sock_fprog) +#define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog) +#define TUNGETVNETHDRSZ _IOR('T', 215, int) +#define TUNSETVNETHDRSZ _IOW('T', 216, int) +#define TUNSETQUEUE _IOW('T', 217, int) +#define TUNSETIFINDEX _IOW('T', 218, unsigned int) +#define TUNGETFILTER _IOR('T', 219, struct sock_fprog) +#define TUNSETVNETLE _IOW('T', 220, int) +#define TUNGETVNETLE _IOR('T', 221, int) +/* The TUNSETVNETBE and TUNGETVNETBE ioctls are for cross-endian support on + * little-endian hosts. Not all kernel configurations support them, but all + * configurations that support SET also support GET. + */ +#define TUNSETVNETBE _IOW('T', 222, int) +#define TUNGETVNETBE _IOR('T', 223, int) +#define TUNSETSTEERINGEBPF _IOR('T', 224, int) +#define TUNSETFILTEREBPF _IOR('T', 225, int) +#define TUNSETCARRIER _IOW('T', 226, int) +#define TUNGETDEVNETNS _IO('T', 227) + +/* TUNSETIFF ifr flags */ +#define IFF_TUN 0x0001 +#define IFF_TAP 0x0002 +#define IFF_NAPI 0x0010 +#define IFF_NAPI_FRAGS 0x0020 +#define IFF_NO_PI 0x1000 +/* This flag has no real effect */ +#define IFF_ONE_QUEUE 0x2000 +#define IFF_VNET_HDR 0x4000 +#define IFF_TUN_EXCL 0x8000 +#define IFF_MULTI_QUEUE 0x0100 +#define IFF_ATTACH_QUEUE 0x0200 +#define IFF_DETACH_QUEUE 0x0400 +/* read-only flag */ +#define IFF_PERSIST 0x0800 +#define IFF_NOFILTER 0x1000 + +/* Socket options */ +#define TUN_TX_TIMESTAMP 1 + +/* Features for GSO (TUNSETOFFLOAD). */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +#define TUN_F_UFO 0x10 /* I can handle UFO packets */ + +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ +#define TUN_PKT_STRIP 0x0001 +struct tun_pi { + __u16 flags; + __be16 proto; +}; + +/* + * Filter spec (used for SETXXFILTER ioctls) + * This stuff is applicable only to the TAP (Ethernet) devices. + * If the count is zero the filter is disabled and the driver accepts + * all packets (promisc mode). + * If the filter is enabled in order to accept broadcast packets + * broadcast addr must be explicitly included in the addr list. + */ +#define TUN_FLT_ALLMULTI 0x0001 /* Accept all multicast packets */ +struct tun_filter { + __u16 flags; /* TUN_FLT_ flags see above */ + __u16 count; /* Number of addresses */ + __u8 addr[0][ETH_ALEN]; +}; + +#endif /* __IF_TUN_H */ diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h new file mode 100644 index 0000000..c7f0a5e --- /dev/null +++ b/include/uapi/linux/if_tunnel.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IF_TUNNEL_H_ +#define _IF_TUNNEL_H_ + +#include +#include +#include +#include +#include + + +#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) +#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1) +#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2) +#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3) +#define SIOCGETPRL (SIOCDEVPRIVATE + 4) +#define SIOCADDPRL (SIOCDEVPRIVATE + 5) +#define SIOCDELPRL (SIOCDEVPRIVATE + 6) +#define SIOCCHGPRL (SIOCDEVPRIVATE + 7) +#define SIOCGET6RD (SIOCDEVPRIVATE + 8) +#define SIOCADD6RD (SIOCDEVPRIVATE + 9) +#define SIOCDEL6RD (SIOCDEVPRIVATE + 10) +#define SIOCCHG6RD (SIOCDEVPRIVATE + 11) + +#define GRE_CSUM __cpu_to_be16(0x8000) +#define GRE_ROUTING __cpu_to_be16(0x4000) +#define GRE_KEY __cpu_to_be16(0x2000) +#define GRE_SEQ __cpu_to_be16(0x1000) +#define GRE_STRICT __cpu_to_be16(0x0800) +#define GRE_REC __cpu_to_be16(0x0700) +#define GRE_ACK __cpu_to_be16(0x0080) +#define GRE_FLAGS __cpu_to_be16(0x0078) +#define GRE_VERSION __cpu_to_be16(0x0007) + +#define GRE_IS_CSUM(f) ((f) & GRE_CSUM) +#define GRE_IS_ROUTING(f) ((f) & GRE_ROUTING) +#define GRE_IS_KEY(f) ((f) & GRE_KEY) +#define GRE_IS_SEQ(f) ((f) & GRE_SEQ) +#define GRE_IS_STRICT(f) ((f) & GRE_STRICT) +#define GRE_IS_REC(f) ((f) & GRE_REC) +#define GRE_IS_ACK(f) ((f) & GRE_ACK) + +#define GRE_VERSION_0 __cpu_to_be16(0x0000) +#define GRE_VERSION_1 __cpu_to_be16(0x0001) +#define GRE_PROTO_PPP __cpu_to_be16(0x880b) +#define GRE_PPTP_KEY_MASK __cpu_to_be32(0xffff) + +struct ip_tunnel_parm { + char name[IFNAMSIZ]; + int link; + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; + struct iphdr iph; +}; + +enum { + IFLA_IPTUN_UNSPEC, + IFLA_IPTUN_LINK, + IFLA_IPTUN_LOCAL, + IFLA_IPTUN_REMOTE, + IFLA_IPTUN_TTL, + IFLA_IPTUN_TOS, + IFLA_IPTUN_ENCAP_LIMIT, + IFLA_IPTUN_FLOWINFO, + IFLA_IPTUN_FLAGS, + IFLA_IPTUN_PROTO, + IFLA_IPTUN_PMTUDISC, + IFLA_IPTUN_6RD_PREFIX, + IFLA_IPTUN_6RD_RELAY_PREFIX, + IFLA_IPTUN_6RD_PREFIXLEN, + IFLA_IPTUN_6RD_RELAY_PREFIXLEN, + IFLA_IPTUN_ENCAP_TYPE, + IFLA_IPTUN_ENCAP_FLAGS, + IFLA_IPTUN_ENCAP_SPORT, + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, + __IFLA_IPTUN_MAX, +}; +#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + +enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, + TUNNEL_ENCAP_GUE, + TUNNEL_ENCAP_MPLS, +}; + +#define TUNNEL_ENCAP_FLAG_CSUM (1<<0) +#define TUNNEL_ENCAP_FLAG_CSUM6 (1<<1) +#define TUNNEL_ENCAP_FLAG_REMCSUM (1<<2) + +/* SIT-mode i_flags */ +#define SIT_ISATAP 0x0001 + +struct ip_tunnel_prl { + __be32 addr; + __u16 flags; + __u16 __reserved; + __u32 datalen; + __u32 __reserved2; + /* data follows */ +}; + +/* PRL flags */ +#define PRL_DEFAULT 0x0001 + +struct ip_tunnel_6rd { + struct in6_addr prefix; + __be32 relay_prefix; + __u16 prefixlen; + __u16 relay_prefixlen; +}; + +enum { + IFLA_GRE_UNSPEC, + IFLA_GRE_LINK, + IFLA_GRE_IFLAGS, + IFLA_GRE_OFLAGS, + IFLA_GRE_IKEY, + IFLA_GRE_OKEY, + IFLA_GRE_LOCAL, + IFLA_GRE_REMOTE, + IFLA_GRE_TTL, + IFLA_GRE_TOS, + IFLA_GRE_PMTUDISC, + IFLA_GRE_ENCAP_LIMIT, + IFLA_GRE_FLOWINFO, + IFLA_GRE_FLAGS, + IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, + IFLA_GRE_ENCAP_DPORT, + IFLA_GRE_COLLECT_METADATA, + IFLA_GRE_IGNORE_DF, + IFLA_GRE_FWMARK, + IFLA_GRE_ERSPAN_INDEX, + IFLA_GRE_ERSPAN_VER, + IFLA_GRE_ERSPAN_DIR, + IFLA_GRE_ERSPAN_HWID, + __IFLA_GRE_MAX, +}; + +#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) + +/* VTI-mode i_flags */ +#define VTI_ISVTI ((__be16)0x0001) + +enum { + IFLA_VTI_UNSPEC, + IFLA_VTI_LINK, + IFLA_VTI_IKEY, + IFLA_VTI_OKEY, + IFLA_VTI_LOCAL, + IFLA_VTI_REMOTE, + IFLA_VTI_FWMARK, + __IFLA_VTI_MAX, +}; + +#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) + +#define TUNNEL_CSUM __cpu_to_be16(0x01) +#define TUNNEL_ROUTING __cpu_to_be16(0x02) +#define TUNNEL_KEY __cpu_to_be16(0x04) +#define TUNNEL_SEQ __cpu_to_be16(0x08) +#define TUNNEL_STRICT __cpu_to_be16(0x10) +#define TUNNEL_REC __cpu_to_be16(0x20) +#define TUNNEL_VERSION __cpu_to_be16(0x40) +#define TUNNEL_NO_KEY __cpu_to_be16(0x80) +#define TUNNEL_DONT_FRAGMENT __cpu_to_be16(0x0100) +#define TUNNEL_OAM __cpu_to_be16(0x0200) +#define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400) +#define TUNNEL_GENEVE_OPT __cpu_to_be16(0x0800) +#define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000) +#define TUNNEL_NOCACHE __cpu_to_be16(0x2000) +#define TUNNEL_ERSPAN_OPT __cpu_to_be16(0x4000) + +#define TUNNEL_OPTIONS_PRESENT \ + (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT) + +#endif /* _IF_TUNNEL_H_ */ diff --git a/include/uapi/linux/if_vlan.h b/include/uapi/linux/if_vlan.h new file mode 100644 index 0000000..04bca79 --- /dev/null +++ b/include/uapi/linux/if_vlan.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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, + VLAN_FLAG_MVRP = 0x8, + VLAN_FLAG_BRIDGE_BINDING = 0x10, +}; + +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_priv flags */ + } u; + + short vlan_qos; +}; + +#endif /* _LINUX_IF_VLAN_H_ */ diff --git a/include/uapi/linux/ife.h b/include/uapi/linux/ife.h new file mode 100644 index 0000000..bdd953c --- /dev/null +++ b/include/uapi/linux/ife.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_IFE_H +#define __UAPI_IFE_H + +#define IFE_METAHDRLEN 2 + +enum { + IFE_META_SKBMARK = 1, + IFE_META_HASHID, + IFE_META_PRIO, + IFE_META_QMAP, + IFE_META_TCINDEX, + __IFE_META_MAX +}; + +/*Can be overridden at runtime by module option*/ +#define IFE_META_MAX (__IFE_META_MAX - 1) + +#endif diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h new file mode 100644 index 0000000..6a6c97c --- /dev/null +++ b/include/uapi/linux/ila.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* ila.h - ILA Interface */ + +#ifndef _LINUX_ILA_H +#define _LINUX_ILA_H + +/* NETLINK_GENERIC related info */ +#define ILA_GENL_NAME "ila" +#define ILA_GENL_VERSION 0x1 + +enum { + ILA_ATTR_UNSPEC, + ILA_ATTR_LOCATOR, /* u64 */ + ILA_ATTR_IDENTIFIER, /* u64 */ + ILA_ATTR_LOCATOR_MATCH, /* u64 */ + ILA_ATTR_IFINDEX, /* s32 */ + ILA_ATTR_DIR, /* u32 */ + ILA_ATTR_PAD, + ILA_ATTR_CSUM_MODE, /* u8 */ + ILA_ATTR_IDENT_TYPE, /* u8 */ + ILA_ATTR_HOOK_TYPE, /* u8 */ + + __ILA_ATTR_MAX, +}; + +#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1) + +enum { + ILA_CMD_UNSPEC, + ILA_CMD_ADD, + ILA_CMD_DEL, + ILA_CMD_GET, + ILA_CMD_FLUSH, + + __ILA_CMD_MAX, +}; + +#define ILA_CMD_MAX (__ILA_CMD_MAX - 1) + +#define ILA_DIR_IN (1 << 0) +#define ILA_DIR_OUT (1 << 1) + +enum { + ILA_CSUM_ADJUST_TRANSPORT, + ILA_CSUM_NEUTRAL_MAP, + ILA_CSUM_NO_ACTION, + ILA_CSUM_NEUTRAL_MAP_AUTO, +}; + +enum { + ILA_ATYPE_IID = 0, + ILA_ATYPE_LUID, + ILA_ATYPE_VIRT_V4, + ILA_ATYPE_VIRT_UNI_V6, + ILA_ATYPE_VIRT_MULTI_V6, + ILA_ATYPE_NONLOCAL_ADDR, + ILA_ATYPE_RSVD_1, + ILA_ATYPE_RSVD_2, + + ILA_ATYPE_USE_FORMAT = 32, /* Get type from type field in identifier */ +}; + +enum { + ILA_HOOK_ROUTE_OUTPUT, + ILA_HOOK_ROUTE_INPUT, +}; + +#endif /* _LINUX_ILA_H */ diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h new file mode 100644 index 0000000..32cf55c --- /dev/null +++ b/include/uapi/linux/in.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions of the Internet Protocol. + * + * Version: @(#)in.h 1.0.1 04/21/93 + * + * Authors: Original taken from the GNU Project file. + * 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_IN_H +#define _LINUX_IN_H + +#include +#include +#include + +#if __UAPI_DEF_IN_IPPROTO +/* Standard well-defined IP protocols. */ +enum { + IPPROTO_IP = 0, /* Dummy protocol for TCP */ +#define IPPROTO_IP IPPROTO_IP + IPPROTO_ICMP = 1, /* Internet Control Message Protocol */ +#define IPPROTO_ICMP IPPROTO_ICMP + IPPROTO_IGMP = 2, /* Internet Group Management Protocol */ +#define IPPROTO_IGMP IPPROTO_IGMP + IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */ +#define IPPROTO_IPIP IPPROTO_IPIP + IPPROTO_TCP = 6, /* Transmission Control Protocol */ +#define IPPROTO_TCP IPPROTO_TCP + IPPROTO_EGP = 8, /* Exterior Gateway Protocol */ +#define IPPROTO_EGP IPPROTO_EGP + IPPROTO_PUP = 12, /* PUP protocol */ +#define IPPROTO_PUP IPPROTO_PUP + IPPROTO_UDP = 17, /* User Datagram Protocol */ +#define IPPROTO_UDP IPPROTO_UDP + IPPROTO_IDP = 22, /* XNS IDP protocol */ +#define IPPROTO_IDP IPPROTO_IDP + IPPROTO_TP = 29, /* SO Transport Protocol Class 4 */ +#define IPPROTO_TP IPPROTO_TP + IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */ +#define IPPROTO_DCCP IPPROTO_DCCP + IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */ +#define IPPROTO_IPV6 IPPROTO_IPV6 + IPPROTO_RSVP = 46, /* RSVP Protocol */ +#define IPPROTO_RSVP IPPROTO_RSVP + IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */ +#define IPPROTO_GRE IPPROTO_GRE + IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ +#define IPPROTO_ESP IPPROTO_ESP + IPPROTO_AH = 51, /* Authentication Header protocol */ +#define IPPROTO_AH IPPROTO_AH + IPPROTO_MTP = 92, /* Multicast Transport Protocol */ +#define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ +#define IPPROTO_BEETPH IPPROTO_BEETPH + IPPROTO_ENCAP = 98, /* Encapsulation Header */ +#define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +#define IPPROTO_PIM IPPROTO_PIM + IPPROTO_COMP = 108, /* Compression Header Protocol */ +#define IPPROTO_COMP IPPROTO_COMP + IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */ +#define IPPROTO_SCTP IPPROTO_SCTP + IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */ +#define IPPROTO_UDPLITE IPPROTO_UDPLITE + IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */ +#define IPPROTO_MPLS IPPROTO_MPLS + IPPROTO_ETHERNET = 143, /* Ethernet-within-IPv6 Encapsulation */ +#define IPPROTO_ETHERNET IPPROTO_ETHERNET + IPPROTO_RAW = 255, /* Raw IP packets */ +#define IPPROTO_RAW IPPROTO_RAW + IPPROTO_MPTCP = 262, /* Multipath TCP connection */ +#define IPPROTO_MPTCP IPPROTO_MPTCP + IPPROTO_MAX +}; +#endif + +#if __UAPI_DEF_IN_ADDR +/* Internet address. */ +struct in_addr { + __be32 s_addr; +}; +#endif + +#define IP_TOS 1 +#define IP_TTL 2 +#define IP_HDRINCL 3 +#define IP_OPTIONS 4 +#define IP_ROUTER_ALERT 5 +#define IP_RECVOPTS 6 +#define IP_RETOPTS 7 +#define IP_PKTINFO 8 +#define IP_PKTOPTIONS 9 +#define IP_MTU_DISCOVER 10 +#define IP_RECVERR 11 +#define IP_RECVTTL 12 +#define IP_RECVTOS 13 +#define IP_MTU 14 +#define IP_FREEBIND 15 +#define IP_IPSEC_POLICY 16 +#define IP_XFRM_POLICY 17 +#define IP_PASSSEC 18 +#define IP_TRANSPARENT 19 + +/* BSD compatibility */ +#define IP_RECVRETOPTS IP_RETOPTS + +/* TProxy original addresses */ +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR + +#define IP_MINTTL 21 +#define IP_NODEFRAG 22 +#define IP_CHECKSUM 23 +#define IP_BIND_ADDRESS_NO_PORT 24 +#define IP_RECVFRAGSIZE 25 +#define IP_RECVERR_RFC4884 26 + +/* IP_MTU_DISCOVER values */ +#define IP_PMTUDISC_DONT 0 /* Never send DF frames */ +#define IP_PMTUDISC_WANT 1 /* Use per route hints */ +#define IP_PMTUDISC_DO 2 /* Always DF */ +#define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */ +/* Always use interface mtu (ignores dst pmtu) but don't set DF flag. + * Also incoming ICMP frag_needed notifications will be ignored on + * this socket to prevent accepting spoofed ones. + */ +#define IP_PMTUDISC_INTERFACE 4 +/* weaker version of IP_PMTUDISC_INTERFACE, which allows packets to get + * fragmented if they exeed the interface mtu + */ +#define IP_PMTUDISC_OMIT 5 + +#define IP_MULTICAST_IF 32 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 +#define IP_UNBLOCK_SOURCE 37 +#define IP_BLOCK_SOURCE 38 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#define IP_MSFILTER 41 +#define MCAST_JOIN_GROUP 42 +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_LEAVE_GROUP 45 +#define MCAST_JOIN_SOURCE_GROUP 46 +#define MCAST_LEAVE_SOURCE_GROUP 47 +#define MCAST_MSFILTER 48 +#define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 + +#define MCAST_EXCLUDE 0 +#define MCAST_INCLUDE 1 + +/* These need to appear somewhere around here */ +#define IP_DEFAULT_MULTICAST_TTL 1 +#define IP_DEFAULT_MULTICAST_LOOP 1 + +/* Request struct for multicast socket ops */ + +#if __UAPI_DEF_IP_MREQ +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +}; + +struct ip_mreqn { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_address; /* local IP address of interface */ + int imr_ifindex; /* Interface index */ +}; + +struct ip_mreq_source { + __be32 imr_multiaddr; + __be32 imr_interface; + __be32 imr_sourceaddr; +}; + +struct ip_msfilter { + __be32 imsf_multiaddr; + __be32 imsf_interface; + __u32 imsf_fmode; + __u32 imsf_numsrc; + __be32 imsf_slist[1]; +}; + +#define IP_MSFILTER_SIZE(numsrc) \ + (sizeof(struct ip_msfilter) - sizeof(__u32) \ + + (numsrc) * sizeof(__u32)) + +struct group_req { + __u32 gr_interface; /* interface index */ + struct __kernel_sockaddr_storage gr_group; /* group address */ +}; + +struct group_source_req { + __u32 gsr_interface; /* interface index */ + struct __kernel_sockaddr_storage gsr_group; /* group address */ + struct __kernel_sockaddr_storage gsr_source; /* source address */ +}; + +struct group_filter { + __u32 gf_interface; /* interface index */ + struct __kernel_sockaddr_storage gf_group; /* multicast address */ + __u32 gf_fmode; /* filter mode */ + __u32 gf_numsrc; /* number of sources */ + struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ +}; + +#define GROUP_FILTER_SIZE(numsrc) \ + (sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \ + + (numsrc) * sizeof(struct __kernel_sockaddr_storage)) +#endif + +#if __UAPI_DEF_IN_PKTINFO +struct in_pktinfo { + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +/* Structure describing an Internet (IP) socket address. */ +#if __UAPI_DEF_SOCKADDR_IN +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ +struct sockaddr_in { + __kernel_sa_family_t sin_family; /* Address family */ + __be16 sin_port; /* Port number */ + struct in_addr sin_addr; /* Internet address */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - + sizeof(unsigned short int) - sizeof(struct in_addr)]; +}; +#define sin_zero __pad /* for BSD UNIX comp. -FvK */ +#endif + +#if __UAPI_DEF_IN_CLASS +/* + * Definitions of the bits in an Internet address integer. + * On subnets, host and network parts are found according + * to the subnet mask, not these masks. + */ +#define IN_CLASSA(a) ((((long int) (a)) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((long int) (a)) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((long int) (a)) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(a) IN_CLASSD(a) +#define IN_MULTICAST_NET 0xe0000000 + +#define IN_BADCLASS(a) (((long int) (a) ) == (long int)0xffffffff) +#define IN_EXPERIMENTAL(a) IN_BADCLASS((a)) + +#define IN_CLASSE(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000) +#define IN_CLASSE_NET 0xffffffff +#define IN_CLASSE_NSHIFT 0 + +/* Address to accept any incoming messages. */ +#define INADDR_ANY ((unsigned long int) 0x00000000) + +/* Address to send to all hosts. */ +#define INADDR_BROADCAST ((unsigned long int) 0xffffffff) + +/* Address indicating an error return. */ +#define INADDR_NONE ((unsigned long int) 0xffffffff) + +/* Network number for local host loopback. */ +#define IN_LOOPBACKNET 127 + +/* Address to loopback in software to local host. */ +#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ +#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) + +/* Defines for Multicast INADDR */ +#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ +#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +#define INADDR_ALLSNOOPERS_GROUP 0xe000006aU /* 224.0.0.106 */ +#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ +#endif + +/* contains the htonl type stuff.. */ +#include + + +#endif /* _LINUX_IN_H */ diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h new file mode 100644 index 0000000..7e3a58e --- /dev/null +++ b/include/uapi/linux/in6.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Types and definitions for AF_INET6 + * Linux INET6 implementation + * + * Authors: + * Pedro Roque + * + * Sources: + * IPv6 Program Interfaces for BSD Systems + * + * + * Advanced Sockets API for IPv6 + * + * + * 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_IN6_H +#define _LINUX_IN6_H + +#include +#include + +/* + * IPv6 address structure + */ + +#if __UAPI_DEF_IN6_ADDR +struct in6_addr { + union { + __u8 u6_addr8[16]; +#if __UAPI_DEF_IN6_ADDR_ALT + __be16 u6_addr16[8]; + __be32 u6_addr32[4]; +#endif + } in6_u; +#define s6_addr in6_u.u6_addr8 +#if __UAPI_DEF_IN6_ADDR_ALT +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#endif +}; +#endif /* __UAPI_DEF_IN6_ADDR */ + +#if __UAPI_DEF_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short int sin6_family; /* AF_INET6 */ + __be16 sin6_port; /* Transport layer port # */ + __be32 sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + __u32 sin6_scope_id; /* scope id (new in RFC2553) */ +}; +#endif /* __UAPI_DEF_SOCKADDR_IN6 */ + +#if __UAPI_DEF_IPV6_MREQ +struct ipv6_mreq { + /* IPv6 multicast address of group */ + struct in6_addr ipv6mr_multiaddr; + + /* local IPv6 address of interface */ + int ipv6mr_ifindex; +}; +#endif /* __UAPI_DEF_IVP6_MREQ */ + +#define ipv6mr_acaddr ipv6mr_multiaddr + +struct in6_flowlabel_req { + struct in6_addr flr_dst; + __be32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_pad; + /* Options in format of IPV6_PKTOPTIONS */ +}; + +#define IPV6_FL_A_GET 0 +#define IPV6_FL_A_PUT 1 +#define IPV6_FL_A_RENEW 2 + +#define IPV6_FL_F_CREATE 1 +#define IPV6_FL_F_EXCL 2 +#define IPV6_FL_F_REFLECT 4 +#define IPV6_FL_F_REMOTE 8 + +#define IPV6_FL_S_NONE 0 +#define IPV6_FL_S_EXCL 1 +#define IPV6_FL_S_PROCESS 2 +#define IPV6_FL_S_USER 3 +#define IPV6_FL_S_ANY 255 + + +/* + * Bitmask constant declarations to help applications select out the + * flow label and priority fields. + * + * Note that this are in host byte order while the flowinfo field of + * sockaddr_in6 is in network byte order. + */ + +#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff +#define IPV6_FLOWINFO_PRIORITY 0x0ff00000 + +/* These definitions are obsolete */ +#define IPV6_PRIORITY_UNCHARACTERIZED 0x0000 +#define IPV6_PRIORITY_FILLER 0x0100 +#define IPV6_PRIORITY_UNATTENDED 0x0200 +#define IPV6_PRIORITY_RESERVED1 0x0300 +#define IPV6_PRIORITY_BULK 0x0400 +#define IPV6_PRIORITY_RESERVED2 0x0500 +#define IPV6_PRIORITY_INTERACTIVE 0x0600 +#define IPV6_PRIORITY_CONTROL 0x0700 +#define IPV6_PRIORITY_8 0x0800 +#define IPV6_PRIORITY_9 0x0900 +#define IPV6_PRIORITY_10 0x0a00 +#define IPV6_PRIORITY_11 0x0b00 +#define IPV6_PRIORITY_12 0x0c00 +#define IPV6_PRIORITY_13 0x0d00 +#define IPV6_PRIORITY_14 0x0e00 +#define IPV6_PRIORITY_15 0x0f00 + +/* + * IPV6 extension headers + */ +#if __UAPI_DEF_IPPROTO_V6 +#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ +#define IPPROTO_ROUTING 43 /* IPv6 routing header */ +#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ +#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_NONE 59 /* IPv6 no next header */ +#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ +#define IPPROTO_MH 135 /* IPv6 mobility header */ +#endif /* __UAPI_DEF_IPPROTO_V6 */ + +/* + * IPv6 TLV options. + */ +#define IPV6_TLV_PAD1 0 +#define IPV6_TLV_PADN 1 +#define IPV6_TLV_ROUTERALERT 5 +#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ +#define IPV6_TLV_JUMBO 194 +#define IPV6_TLV_HAO 201 /* home address option */ + +/* + * IPV6 socket options + */ +#if __UAPI_DEF_IPV6_OPTIONS +#define IPV6_ADDRFORM 1 +#define IPV6_2292PKTINFO 2 +#define IPV6_2292HOPOPTS 3 +#define IPV6_2292DSTOPTS 4 +#define IPV6_2292RTHDR 5 +#define IPV6_2292PKTOPTIONS 6 +#define IPV6_CHECKSUM 7 +#define IPV6_2292HOPLIMIT 8 +#define IPV6_NEXTHOP 9 +#define IPV6_AUTHHDR 10 /* obsolete */ +#define IPV6_FLOWINFO 11 + +#define IPV6_UNICAST_HOPS 16 +#define IPV6_MULTICAST_IF 17 +#define IPV6_MULTICAST_HOPS 18 +#define IPV6_MULTICAST_LOOP 19 +#define IPV6_ADD_MEMBERSHIP 20 +#define IPV6_DROP_MEMBERSHIP 21 +#define IPV6_ROUTER_ALERT 22 +#define IPV6_MTU_DISCOVER 23 +#define IPV6_MTU 24 +#define IPV6_RECVERR 25 +#define IPV6_V6ONLY 26 +#define IPV6_JOIN_ANYCAST 27 +#define IPV6_LEAVE_ANYCAST 28 +#define IPV6_MULTICAST_ALL 29 +#define IPV6_ROUTER_ALERT_ISOLATE 30 +#define IPV6_RECVERR_RFC4884 31 + +/* IPV6_MTU_DISCOVER values */ +#define IPV6_PMTUDISC_DONT 0 +#define IPV6_PMTUDISC_WANT 1 +#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3 +/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4 + * also see comments on IP_PMTUDISC_INTERFACE + */ +#define IPV6_PMTUDISC_INTERFACE 4 +/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to + * get fragmented if they exceed the interface mtu + */ +#define IPV6_PMTUDISC_OMIT 5 + +/* Flowlabel */ +#define IPV6_FLOWLABEL_MGR 32 +#define IPV6_FLOWINFO_SEND 33 + +#define IPV6_IPSEC_POLICY 34 +#define IPV6_XFRM_POLICY 35 +#define IPV6_HDRINCL 36 +#endif + +/* + * Multicast: + * Following socket options are shared between IPv4 and IPv6. + * + * MCAST_JOIN_GROUP 42 + * MCAST_BLOCK_SOURCE 43 + * MCAST_UNBLOCK_SOURCE 44 + * MCAST_LEAVE_GROUP 45 + * MCAST_JOIN_SOURCE_GROUP 46 + * MCAST_LEAVE_SOURCE_GROUP 47 + * MCAST_MSFILTER 48 + */ + +/* + * Advanced API (RFC3542) (1) + * + * Note: IPV6_RECVRTHDRDSTOPTS does not exist. see net/ipv6/datagram.c. + */ + +#define IPV6_RECVPKTINFO 49 +#define IPV6_PKTINFO 50 +#define IPV6_RECVHOPLIMIT 51 +#define IPV6_HOPLIMIT 52 +#define IPV6_RECVHOPOPTS 53 +#define IPV6_HOPOPTS 54 +#define IPV6_RTHDRDSTOPTS 55 +#define IPV6_RECVRTHDR 56 +#define IPV6_RTHDR 57 +#define IPV6_RECVDSTOPTS 58 +#define IPV6_DSTOPTS 59 +#define IPV6_RECVPATHMTU 60 +#define IPV6_PATHMTU 61 +#define IPV6_DONTFRAG 62 +#if 0 /* not yet */ +#define IPV6_USE_MIN_MTU 63 +#endif + +/* + * Netfilter (1) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_SET_REPLACE / IP6T_SO_GET_INFO 64 + * IP6T_SO_SET_ADD_COUNTERS / IP6T_SO_GET_ENTRIES 65 + */ + +/* + * Advanced API (RFC3542) (2) + */ +#define IPV6_RECVTCLASS 66 +#define IPV6_TCLASS 67 + +/* + * Netfilter (2) + * + * Following socket options are used in ip6_tables; + * see include/linux/netfilter_ipv6/ip6_tables.h. + * + * IP6T_SO_GET_REVISION_MATCH 68 + * IP6T_SO_GET_REVISION_TARGET 69 + * IP6T_SO_ORIGINAL_DST 80 + */ + +#define IPV6_AUTOFLOWLABEL 70 +/* RFC5014: Source address selection */ +#define IPV6_ADDR_PREFERENCES 72 + +#define IPV6_PREFER_SRC_TMP 0x0001 +#define IPV6_PREFER_SRC_PUBLIC 0x0002 +#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100 +#define IPV6_PREFER_SRC_COA 0x0004 +#define IPV6_PREFER_SRC_HOME 0x0400 +#define IPV6_PREFER_SRC_CGA 0x0008 +#define IPV6_PREFER_SRC_NONCGA 0x0800 + +/* RFC5082: Generalized Ttl Security Mechanism */ +#define IPV6_MINHOPCOUNT 73 + +#define IPV6_ORIGDSTADDR 74 +#define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR +#define IPV6_TRANSPARENT 75 +#define IPV6_UNICAST_IF 76 +#define IPV6_RECVFRAGSIZE 77 +#define IPV6_FREEBIND 78 + +/* + * Multicast Routing: + * see include/uapi/linux/mroute6.h. + * + * MRT6_BASE 200 + * ... + * MRT6_MAX + */ +#endif /* _LINUX_IN6_H */ diff --git a/include/uapi/linux/in_route.h b/include/uapi/linux/in_route.h new file mode 100644 index 0000000..0cc2c23 --- /dev/null +++ b/include/uapi/linux/in_route.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IN_ROUTE_H +#define _LINUX_IN_ROUTE_H + +/* IPv4 routing cache flags */ + +#define RTCF_DEAD RTNH_F_DEAD +#define RTCF_ONLINK RTNH_F_ONLINK + +/* Obsolete flag. About to be deleted */ +#define RTCF_NOPMTUDISC RTM_F_NOPMTUDISC + +#define RTCF_NOTIFY 0x00010000 +#define RTCF_DIRECTDST 0x00020000 /* unused */ +#define RTCF_REDIRECTED 0x00040000 +#define RTCF_TPROXY 0x00080000 /* unused */ + +#define RTCF_FAST 0x00200000 /* unused */ +#define RTCF_MASQ 0x00400000 /* unused */ +#define RTCF_SNAT 0x00800000 /* unused */ +#define RTCF_DOREDIRECT 0x01000000 +#define RTCF_DIRECTSRC 0x04000000 +#define RTCF_DNAT 0x08000000 +#define RTCF_BROADCAST 0x10000000 +#define RTCF_MULTICAST 0x20000000 +#define RTCF_REJECT 0x40000000 /* unused */ +#define RTCF_LOCAL 0x80000000 + +#define RTCF_NAT (RTCF_DNAT|RTCF_SNAT) + +#define RT_TOS(tos) ((tos)&IPTOS_TOS_MASK) + +#endif /* _LINUX_IN_ROUTE_H */ diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h new file mode 100644 index 0000000..cd83b4f --- /dev/null +++ b/include/uapi/linux/inet_diag.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _INET_DIAG_H_ +#define _INET_DIAG_H_ + +#include + +/* Just some random number */ +#define TCPDIAG_GETSOCK 18 +#define DCCPDIAG_GETSOCK 19 + +#define INET_DIAG_GETSOCK_MAX 24 + +/* Socket identity */ +struct inet_diag_sockid { + __be16 idiag_sport; + __be16 idiag_dport; + __be32 idiag_src[4]; + __be32 idiag_dst[4]; + __u32 idiag_if; + __u32 idiag_cookie[2]; +#define INET_DIAG_NOCOOKIE (~0U) +}; + +/* Request structure */ + +struct inet_diag_req { + __u8 idiag_family; /* Family of addresses. */ + __u8 idiag_src_len; + __u8 idiag_dst_len; + __u8 idiag_ext; /* Query extended information */ + + struct inet_diag_sockid id; + + __u32 idiag_states; /* States to dump */ + __u32 idiag_dbs; /* Tables to dump (NI) */ +}; + +struct inet_diag_req_v2 { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 pad; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +/* + * SOCK_RAW sockets require the underlied protocol to be + * additionally specified so we can use @pad member for + * this, but we can't rename it because userspace programs + * still may depend on this name. Instead lets use another + * structure definition as an alias for struct + * @inet_diag_req_v2. + */ +struct inet_diag_req_raw { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u8 idiag_ext; + __u8 sdiag_raw_protocol; + __u32 idiag_states; + struct inet_diag_sockid id; +}; + +enum { + INET_DIAG_REQ_NONE, + INET_DIAG_REQ_BYTECODE, + INET_DIAG_REQ_SK_BPF_STORAGES, + INET_DIAG_REQ_PROTOCOL, + __INET_DIAG_REQ_MAX, +}; + +#define INET_DIAG_REQ_MAX (__INET_DIAG_REQ_MAX - 1) + +/* Bytecode is sequence of 4 byte commands followed by variable arguments. + * All the commands identified by "code" are conditional jumps forward: + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be + * length of the command and its arguments. + */ + +struct inet_diag_bc_op { + unsigned char code; + unsigned char yes; + unsigned short no; +}; + +enum { + INET_DIAG_BC_NOP, + INET_DIAG_BC_JMP, + INET_DIAG_BC_S_GE, + INET_DIAG_BC_S_LE, + INET_DIAG_BC_D_GE, + INET_DIAG_BC_D_LE, + INET_DIAG_BC_AUTO, + INET_DIAG_BC_S_COND, + INET_DIAG_BC_D_COND, + INET_DIAG_BC_DEV_COND, /* u32 ifindex */ + INET_DIAG_BC_MARK_COND, + INET_DIAG_BC_S_EQ, + INET_DIAG_BC_D_EQ, + INET_DIAG_BC_CGROUP_COND, /* u64 cgroup v2 ID */ +}; + +struct inet_diag_hostcond { + __u8 family; + __u8 prefix_len; + int port; + __be32 addr[0]; +}; + +struct inet_diag_markcond { + __u32 mark; + __u32 mask; +}; + +/* Base info structure. It contains socket identity (addrs/ports/cookie) + * and, alas, the information shown by netstat. */ +struct inet_diag_msg { + __u8 idiag_family; + __u8 idiag_state; + __u8 idiag_timer; + __u8 idiag_retrans; + + struct inet_diag_sockid id; + + __u32 idiag_expires; + __u32 idiag_rqueue; + __u32 idiag_wqueue; + __u32 idiag_uid; + __u32 idiag_inode; +}; + +/* Extensions */ + +enum { + INET_DIAG_NONE, + INET_DIAG_MEMINFO, + INET_DIAG_INFO, + INET_DIAG_VEGASINFO, + INET_DIAG_CONG, + INET_DIAG_TOS, + INET_DIAG_TCLASS, + INET_DIAG_SKMEMINFO, + INET_DIAG_SHUTDOWN, + + /* + * Next extenstions cannot be requested in struct inet_diag_req_v2: + * its field idiag_ext has only 8 bits. + */ + + INET_DIAG_DCTCPINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_PROTOCOL, /* response attribute only */ + INET_DIAG_SKV6ONLY, + INET_DIAG_LOCALS, + INET_DIAG_PEERS, + INET_DIAG_PAD, + INET_DIAG_MARK, /* only with CAP_NET_ADMIN */ + INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */ + INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */ + INET_DIAG_MD5SIG, + INET_DIAG_ULP_INFO, + INET_DIAG_SK_BPF_STORAGES, + INET_DIAG_CGROUP_ID, + __INET_DIAG_MAX, +}; + +#define INET_DIAG_MAX (__INET_DIAG_MAX - 1) + +enum { + INET_ULP_INFO_UNSPEC, + INET_ULP_INFO_NAME, + INET_ULP_INFO_TLS, + INET_ULP_INFO_MPTCP, + __INET_ULP_INFO_MAX, +}; +#define INET_ULP_INFO_MAX (__INET_ULP_INFO_MAX - 1) + +/* INET_DIAG_MEM */ + +struct inet_diag_meminfo { + __u32 idiag_rmem; + __u32 idiag_wmem; + __u32 idiag_fmem; + __u32 idiag_tmem; +}; + +/* INET_DIAG_VEGASINFO */ + +struct tcpvegas_info { + __u32 tcpv_enabled; + __u32 tcpv_rttcnt; + __u32 tcpv_rtt; + __u32 tcpv_minrtt; +}; + +/* INET_DIAG_DCTCPINFO */ + +struct tcp_dctcp_info { + __u16 dctcp_enabled; + __u16 dctcp_ce_state; + __u32 dctcp_alpha; + __u32 dctcp_ab_ecn; + __u32 dctcp_ab_tot; +}; + +/* INET_DIAG_BBRINFO */ + +struct tcp_bbr_info { + /* u64 bw: max-filtered BW (app throughput) estimate in Byte per sec: */ + __u32 bbr_bw_lo; /* lower 32 bits of bw */ + __u32 bbr_bw_hi; /* upper 32 bits of bw */ + __u32 bbr_min_rtt; /* min-filtered RTT in uSec */ + __u32 bbr_pacing_gain; /* pacing gain shifted left 8 bits */ + __u32 bbr_cwnd_gain; /* cwnd gain shifted left 8 bits */ +}; + +union tcp_cc_info { + struct tcpvegas_info vegas; + struct tcp_dctcp_info dctcp; + struct tcp_bbr_info bbr; +}; +#endif /* _INET_DIAG_H_ */ diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h new file mode 100644 index 0000000..f4ecd2f --- /dev/null +++ b/include/uapi/linux/ip.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions for the IP protocol. + * + * Version: @(#)ip.h 1.0.2 04/28/93 + * + * Authors: 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_IP_H +#define _LINUX_IP_H +#include +#include + +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_MINCOST 0x02 + +#define IPTOS_PREC_MASK 0xE0 +#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* IP options */ +#define IPOPT_COPY 0x80 +#define IPOPT_CLASS_MASK 0x60 +#define IPOPT_NUMBER_MASK 0x1f + +#define IPOPT_COPIED(o) ((o)&IPOPT_COPY) +#define IPOPT_CLASS(o) ((o)&IPOPT_CLASS_MASK) +#define IPOPT_NUMBER(o) ((o)&IPOPT_NUMBER_MASK) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_MEASUREMENT 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_END (0 |IPOPT_CONTROL) +#define IPOPT_NOOP (1 |IPOPT_CONTROL) +#define IPOPT_SEC (2 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_LSRR (3 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_TIMESTAMP (4 |IPOPT_MEASUREMENT) +#define IPOPT_CIPSO (6 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RR (7 |IPOPT_CONTROL) +#define IPOPT_SID (8 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_SSRR (9 |IPOPT_CONTROL|IPOPT_COPY) +#define IPOPT_RA (20|IPOPT_CONTROL|IPOPT_COPY) + +#define IPVERSION 4 +#define MAXTTL 255 +#define IPDEFTTL 64 + +#define IPOPT_OPTVAL 0 +#define IPOPT_OLEN 1 +#define IPOPT_OFFSET 2 +#define IPOPT_MINOFF 4 +#define MAX_IPOPTLEN 40 +#define IPOPT_NOP IPOPT_NOOP +#define IPOPT_EOL IPOPT_END +#define IPOPT_TS IPOPT_TIMESTAMP + +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +#define IPV4_BEET_PHMAXLEN 8 + +struct iphdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ihl:4, + version:4; +#elif defined (__BIG_ENDIAN_BITFIELD) + __u8 version:4, + ihl:4; +#else +#error "Please fix " +#endif + __u8 tos; + __be16 tot_len; + __be16 id; + __be16 frag_off; + __u8 ttl; + __u8 protocol; + __sum16 check; + __be32 saddr; + __be32 daddr; + /*The options start here. */ +}; + + +struct ip_auth_hdr { + __u8 nexthdr; + __u8 hdrlen; /* This one is measured in 32 bit units! */ + __be16 reserved; + __be32 spi; + __be32 seq_no; /* Sequence number */ + __u8 auth_data[0]; /* Variable len but >=4. Mind the 64 bit alignment! */ +}; + +struct ip_esp_hdr { + __be32 spi; + __be32 seq_no; /* Sequence number */ + __u8 enc_data[0]; /* Variable len but >=8. Mind the 64 bit alignment! */ +}; + +struct ip_comp_hdr { + __u8 nexthdr; + __u8 flags; + __be16 cpi; +}; + +struct ip_beet_phdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 padlen; + __u8 reserved; +}; + +/* index values for the variables in ipv4_devconf */ +enum +{ + IPV4_DEVCONF_FORWARDING=1, + IPV4_DEVCONF_MC_FORWARDING, + IPV4_DEVCONF_PROXY_ARP, + IPV4_DEVCONF_ACCEPT_REDIRECTS, + IPV4_DEVCONF_SECURE_REDIRECTS, + IPV4_DEVCONF_SEND_REDIRECTS, + IPV4_DEVCONF_SHARED_MEDIA, + IPV4_DEVCONF_RP_FILTER, + IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, + IPV4_DEVCONF_BOOTP_RELAY, + IPV4_DEVCONF_LOG_MARTIANS, + IPV4_DEVCONF_TAG, + IPV4_DEVCONF_ARPFILTER, + IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_NOXFRM, + IPV4_DEVCONF_NOPOLICY, + IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_ARP_ANNOUNCE, + IPV4_DEVCONF_ARP_IGNORE, + IPV4_DEVCONF_PROMOTE_SECONDARIES, + IPV4_DEVCONF_ARP_ACCEPT, + IPV4_DEVCONF_ARP_NOTIFY, + IPV4_DEVCONF_ACCEPT_LOCAL, + IPV4_DEVCONF_SRC_VMARK, + IPV4_DEVCONF_PROXY_ARP_PVLAN, + IPV4_DEVCONF_ROUTE_LOCALNET, + IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, + IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, + IPV4_DEVCONF_DROP_GRATUITOUS_ARP, + IPV4_DEVCONF_BC_FORWARDING, + __IPV4_DEVCONF_MAX +}; + +#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) + +#endif /* _LINUX_IP_H */ diff --git a/include/uapi/linux/ip6_tunnel.h b/include/uapi/linux/ip6_tunnel.h new file mode 100644 index 0000000..0245269 --- /dev/null +++ b/include/uapi/linux/ip6_tunnel.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _IP6_TUNNEL_H +#define _IP6_TUNNEL_H + +#include +#include /* For IFNAMSIZ. */ +#include /* For struct in6_addr. */ + +#define IPV6_TLV_TNL_ENCAP_LIMIT 4 +#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 + +/* don't add encapsulation limit if one isn't present in inner packet */ +#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 +/* copy the traffic class field from the inner packet */ +#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 +/* copy the flowlabel from the inner packet */ +#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 +/* being used for Mobile IPv6 */ +#define IP6_TNL_F_MIP6_DEV 0x8 +/* copy DSCP from the outer packet */ +#define IP6_TNL_F_RCV_DSCP_COPY 0x10 +/* copy fwmark from inner packet */ +#define IP6_TNL_F_USE_ORIG_FWMARK 0x20 +/* allow remote endpoint on the local node */ +#define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40 + +struct ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __be32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ +}; + +struct ip6_tnl_parm2 { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __be32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ + + __be16 i_flags; + __be16 o_flags; + __be32 i_key; + __be32 o_key; +}; + +#endif diff --git a/include/uapi/linux/ipsec.h b/include/uapi/linux/ipsec.h new file mode 100644 index 0000000..50d8ee1 --- /dev/null +++ b/include/uapi/linux/ipsec.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IPSEC_H +#define _LINUX_IPSEC_H + +/* The definitions, required to talk to KAME racoon IKE. */ + +#include + +#define IPSEC_PORT_ANY 0 +#define IPSEC_ULPROTO_ANY 255 +#define IPSEC_PROTO_ANY 255 + +enum { + IPSEC_MODE_ANY = 0, /* We do not support this for SA */ + IPSEC_MODE_TRANSPORT = 1, + IPSEC_MODE_TUNNEL = 2, + IPSEC_MODE_BEET = 3 +}; + +enum { + IPSEC_DIR_ANY = 0, + IPSEC_DIR_INBOUND = 1, + IPSEC_DIR_OUTBOUND = 2, + IPSEC_DIR_FWD = 3, /* It is our own */ + IPSEC_DIR_MAX = 4, + IPSEC_DIR_INVALID = 5 +}; + +enum { + IPSEC_POLICY_DISCARD = 0, + IPSEC_POLICY_NONE = 1, + IPSEC_POLICY_IPSEC = 2, + IPSEC_POLICY_ENTRUST = 3, + IPSEC_POLICY_BYPASS = 4 +}; + +enum { + IPSEC_LEVEL_DEFAULT = 0, + IPSEC_LEVEL_USE = 1, + IPSEC_LEVEL_REQUIRE = 2, + IPSEC_LEVEL_UNIQUE = 3 +}; + +#define IPSEC_MANUAL_REQID_MAX 0x3fff + +#define IPSEC_REPLAYWSIZE 32 + +#endif /* _LINUX_IPSEC_H */ diff --git a/include/uapi/linux/kernel.h b/include/uapi/linux/kernel.h new file mode 100644 index 0000000..d99ffa1 --- /dev/null +++ b/include/uapi/linux/kernel.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_KERNEL_H +#define _LINUX_KERNEL_H + +#include + +/* + * 'kernel.h' contains some often-used function prototypes etc + */ +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#endif /* _LINUX_KERNEL_H */ diff --git a/include/uapi/linux/l2tp.h b/include/uapi/linux/l2tp.h new file mode 100644 index 0000000..131c3a2 --- /dev/null +++ b/include/uapi/linux/l2tp.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * L2TP-over-IP socket for L2TPv3. + * + * Author: James Chapman + */ + +#ifndef _LINUX_L2TP_H_ +#define _LINUX_L2TP_H_ + +#include +#include +#include +#include + +#define IPPROTO_L2TP 115 + +/** + * struct sockaddr_l2tpip - the sockaddr structure for L2TP-over-IP sockets + * @l2tp_family: address family number AF_L2TPIP. + * @l2tp_addr: protocol specific address information + * @l2tp_conn_id: connection id of tunnel + */ +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ +struct sockaddr_l2tpip { + /* The first fields must match struct sockaddr_in */ + __kernel_sa_family_t l2tp_family; /* AF_INET */ + __be16 l2tp_unused; /* INET port number (unused) */ + struct in_addr l2tp_addr; /* Internet address */ + + __u32 l2tp_conn_id; /* Connection ID of tunnel */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char __pad[__SOCK_SIZE__ - + sizeof(__kernel_sa_family_t) - + sizeof(__be16) - sizeof(struct in_addr) - + sizeof(__u32)]; +}; + +/** + * struct sockaddr_l2tpip6 - the sockaddr structure for L2TP-over-IPv6 sockets + * @l2tp_family: address family number AF_L2TPIP. + * @l2tp_addr: protocol specific address information + * @l2tp_conn_id: connection id of tunnel + */ +struct sockaddr_l2tpip6 { + /* The first fields must match struct sockaddr_in6 */ + __kernel_sa_family_t l2tp_family; /* AF_INET6 */ + __be16 l2tp_unused; /* INET port number (unused) */ + __be32 l2tp_flowinfo; /* IPv6 flow information */ + struct in6_addr l2tp_addr; /* IPv6 address */ + __u32 l2tp_scope_id; /* scope id (new in RFC2553) */ + __u32 l2tp_conn_id; /* Connection ID of tunnel */ +}; + +/***************************************************************************** + * NETLINK_GENERIC netlink family. + *****************************************************************************/ + +/* + * Commands. + * Valid TLVs of each command are:- + * TUNNEL_CREATE - CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum + * TUNNEL_DELETE - CONN_ID + * TUNNEL_MODIFY - CONN_ID, udpcsum + * TUNNEL_GETSTATS - CONN_ID, (stats) + * TUNNEL_GET - CONN_ID, (...) + * SESSION_CREATE - SESSION_ID, PW_TYPE, cookie, peer_cookie, l2spec + * SESSION_DELETE - SESSION_ID + * SESSION_MODIFY - SESSION_ID + * SESSION_GET - SESSION_ID, (...) + * SESSION_GETSTATS - SESSION_ID, (stats) + * + */ +enum { + L2TP_CMD_NOOP, + L2TP_CMD_TUNNEL_CREATE, + L2TP_CMD_TUNNEL_DELETE, + L2TP_CMD_TUNNEL_MODIFY, + L2TP_CMD_TUNNEL_GET, + L2TP_CMD_SESSION_CREATE, + L2TP_CMD_SESSION_DELETE, + L2TP_CMD_SESSION_MODIFY, + L2TP_CMD_SESSION_GET, + __L2TP_CMD_MAX, +}; + +#define L2TP_CMD_MAX (__L2TP_CMD_MAX - 1) + +/* + * ATTR types defined for L2TP + */ +enum { + L2TP_ATTR_NONE, /* no data */ + L2TP_ATTR_PW_TYPE, /* u16, enum l2tp_pwtype */ + L2TP_ATTR_ENCAP_TYPE, /* u16, enum l2tp_encap_type */ + L2TP_ATTR_OFFSET, /* u16 (not used) */ + L2TP_ATTR_DATA_SEQ, /* u16 (not used) */ + L2TP_ATTR_L2SPEC_TYPE, /* u8, enum l2tp_l2spec_type */ + L2TP_ATTR_L2SPEC_LEN, /* u8 (not used) */ + L2TP_ATTR_PROTO_VERSION, /* u8 */ + L2TP_ATTR_IFNAME, /* string */ + L2TP_ATTR_CONN_ID, /* u32 */ + L2TP_ATTR_PEER_CONN_ID, /* u32 */ + L2TP_ATTR_SESSION_ID, /* u32 */ + L2TP_ATTR_PEER_SESSION_ID, /* u32 */ + L2TP_ATTR_UDP_CSUM, /* u8 */ + L2TP_ATTR_VLAN_ID, /* u16 (not used) */ + L2TP_ATTR_COOKIE, /* 0, 4 or 8 bytes */ + L2TP_ATTR_PEER_COOKIE, /* 0, 4 or 8 bytes */ + L2TP_ATTR_DEBUG, /* u32, enum l2tp_debug_flags */ + L2TP_ATTR_RECV_SEQ, /* u8 */ + L2TP_ATTR_SEND_SEQ, /* u8 */ + L2TP_ATTR_LNS_MODE, /* u8 */ + L2TP_ATTR_USING_IPSEC, /* u8 */ + L2TP_ATTR_RECV_TIMEOUT, /* msec */ + L2TP_ATTR_FD, /* int */ + L2TP_ATTR_IP_SADDR, /* u32 */ + L2TP_ATTR_IP_DADDR, /* u32 */ + L2TP_ATTR_UDP_SPORT, /* u16 */ + L2TP_ATTR_UDP_DPORT, /* u16 */ + L2TP_ATTR_MTU, /* u16 (not used) */ + L2TP_ATTR_MRU, /* u16 (not used) */ + L2TP_ATTR_STATS, /* nested */ + L2TP_ATTR_IP6_SADDR, /* struct in6_addr */ + L2TP_ATTR_IP6_DADDR, /* struct in6_addr */ + L2TP_ATTR_UDP_ZERO_CSUM6_TX, /* flag */ + L2TP_ATTR_UDP_ZERO_CSUM6_RX, /* flag */ + L2TP_ATTR_PAD, + __L2TP_ATTR_MAX, +}; + +#define L2TP_ATTR_MAX (__L2TP_ATTR_MAX - 1) + +/* Nested in L2TP_ATTR_STATS */ +enum { + L2TP_ATTR_STATS_NONE, /* no data */ + L2TP_ATTR_TX_PACKETS, /* u64 */ + L2TP_ATTR_TX_BYTES, /* u64 */ + L2TP_ATTR_TX_ERRORS, /* u64 */ + L2TP_ATTR_RX_PACKETS, /* u64 */ + L2TP_ATTR_RX_BYTES, /* u64 */ + L2TP_ATTR_RX_SEQ_DISCARDS, /* u64 */ + L2TP_ATTR_RX_OOS_PACKETS, /* u64 */ + L2TP_ATTR_RX_ERRORS, /* u64 */ + L2TP_ATTR_STATS_PAD, + __L2TP_ATTR_STATS_MAX, +}; + +#define L2TP_ATTR_STATS_MAX (__L2TP_ATTR_STATS_MAX - 1) + +enum l2tp_pwtype { + L2TP_PWTYPE_NONE = 0x0000, + L2TP_PWTYPE_ETH_VLAN = 0x0004, + L2TP_PWTYPE_ETH = 0x0005, + L2TP_PWTYPE_PPP = 0x0007, + L2TP_PWTYPE_PPP_AC = 0x0008, + L2TP_PWTYPE_IP = 0x000b, + __L2TP_PWTYPE_MAX +}; + +enum l2tp_l2spec_type { + L2TP_L2SPECTYPE_NONE, + L2TP_L2SPECTYPE_DEFAULT, +}; + +enum l2tp_encap_type { + L2TP_ENCAPTYPE_UDP, + L2TP_ENCAPTYPE_IP, +}; + +/* For L2TP_ATTR_DATA_SEQ. Unused. */ +enum l2tp_seqmode { + L2TP_SEQ_NONE = 0, + L2TP_SEQ_IP = 1, + L2TP_SEQ_ALL = 2, +}; + +/** + * enum l2tp_debug_flags - debug message categories for L2TP tunnels/sessions + * + * @L2TP_MSG_DEBUG: verbose debug (if compiled in) + * @L2TP_MSG_CONTROL: userspace - kernel interface + * @L2TP_MSG_SEQ: sequence numbers + * @L2TP_MSG_DATA: data packets + */ +enum l2tp_debug_flags { + L2TP_MSG_DEBUG = (1 << 0), + L2TP_MSG_CONTROL = (1 << 1), + L2TP_MSG_SEQ = (1 << 2), + L2TP_MSG_DATA = (1 << 3), +}; + +/* + * NETLINK_GENERIC related info + */ +#define L2TP_GENL_NAME "l2tp" +#define L2TP_GENL_VERSION 0x1 +#define L2TP_GENL_MCGROUP "l2tp" + +#endif /* _LINUX_L2TP_H_ */ diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h new file mode 100644 index 0000000..a159991 --- /dev/null +++ b/include/uapi/linux/libc-compat.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Compatibility interface for userspace libc header coordination: + * + * Define compatibility macros that are used to control the inclusion or + * exclusion of UAPI structures and definitions in coordination with another + * userspace C library. + * + * This header is intended to solve the problem of UAPI definitions that + * conflict with userspace definitions. If a UAPI header has such conflicting + * definitions then the solution is as follows: + * + * * Synchronize the UAPI header and the libc headers so either one can be + * used and such that the ABI is preserved. If this is not possible then + * no simple compatibility interface exists (you need to write translating + * wrappers and rename things) and you can't use this interface. + * + * Then follow this process: + * + * (a) Include libc-compat.h in the UAPI header. + * e.g. #include + * This include must be as early as possible. + * + * (b) In libc-compat.h add enough code to detect that the comflicting + * userspace libc header has been included first. + * + * (c) If the userspace libc header has been included first define a set of + * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else + * set their values to 0. + * + * (d) Back in the UAPI header with the conflicting definitions, guard the + * definitions with: + * #if __UAPI_DEF_FOO + * ... + * #endif + * + * This fixes the situation where the linux headers are included *after* the + * libc headers. To fix the problem with the inclusion in the other order the + * userspace libc headers must be fixed like this: + * + * * For all definitions that conflict with kernel definitions wrap those + * defines in the following: + * #if !__UAPI_DEF_FOO + * ... + * #endif + * + * This prevents the redefinition of a construct already defined by the kernel. + */ +#ifndef _LIBC_COMPAT_H +#define _LIBC_COMPAT_H + +/* We have included glibc headers... */ +#if defined(__GLIBC__) + +/* Coordinate with glibc net/if.h header. */ +#if defined(_NET_IF_H) && defined(__USE_MISC) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ + +#define __UAPI_DEF_IF_IFCONF 0 +#define __UAPI_DEF_IF_IFMAP 0 +#define __UAPI_DEF_IF_IFNAMSIZ 0 +#define __UAPI_DEF_IF_IFREQ 0 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */ + +#else /* _NET_IF_H */ + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ + +#define __UAPI_DEF_IF_IFCONF 1 +#define __UAPI_DEF_IF_IFMAP 1 +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#define __UAPI_DEF_IF_IFREQ 1 +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 + +#endif /* _NET_IF_H */ + +/* Coordinate with glibc netinet/in.h header. */ +#if defined(_NETINET_IN_H) + +/* GLIBC headers included first so don't define anything + * that would already be defined. */ +#define __UAPI_DEF_IN_ADDR 0 +#define __UAPI_DEF_IN_IPPROTO 0 +#define __UAPI_DEF_IN_PKTINFO 0 +#define __UAPI_DEF_IP_MREQ 0 +#define __UAPI_DEF_SOCKADDR_IN 0 +#define __UAPI_DEF_IN_CLASS 0 + +#define __UAPI_DEF_IN6_ADDR 0 +/* The exception is the in6_addr macros which must be defined + * if the glibc code didn't define them. This guard matches + * the guard in glibc/inet/netinet/in.h which defines the + * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */ +#if defined(__USE_MISC) || defined (__USE_GNU) +#define __UAPI_DEF_IN6_ADDR_ALT 0 +#else +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#define __UAPI_DEF_SOCKADDR_IN6 0 +#define __UAPI_DEF_IPV6_MREQ 0 +#define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 +#define __UAPI_DEF_IN6_PKTINFO 0 +#define __UAPI_DEF_IP6_MTUINFO 0 + +#else + +/* Linux headers included first, and we must define everything + * we need. The expectation is that glibc will check the + * __UAPI_DEF_* defines and adjust appropriately. */ +#define __UAPI_DEF_IN_ADDR 1 +#define __UAPI_DEF_IN_IPPROTO 1 +#define __UAPI_DEF_IN_PKTINFO 1 +#define __UAPI_DEF_IP_MREQ 1 +#define __UAPI_DEF_SOCKADDR_IN 1 +#define __UAPI_DEF_IN_CLASS 1 + +#define __UAPI_DEF_IN6_ADDR 1 +/* We unconditionally define the in6_addr macros and glibc must + * coordinate. */ +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#define __UAPI_DEF_IPV6_MREQ 1 +#define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 +#define __UAPI_DEF_IN6_PKTINFO 1 +#define __UAPI_DEF_IP6_MTUINFO 1 + +#endif /* _NETINET_IN_H */ + +/* Coordinate with glibc netipx/ipx.h header. */ +#if defined(__NETIPX_IPX_H) + +#define __UAPI_DEF_SOCKADDR_IPX 0 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0 +#define __UAPI_DEF_IPX_CONFIG_DATA 0 +#define __UAPI_DEF_IPX_ROUTE_DEF 0 + +#else /* defined(__NETIPX_IPX_H) */ + +#define __UAPI_DEF_SOCKADDR_IPX 1 +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#define __UAPI_DEF_IPX_ROUTE_DEF 1 + +#endif /* defined(__NETIPX_IPX_H) */ + +/* Definitions for xattr.h */ +#if defined(_SYS_XATTR_H) +#define __UAPI_DEF_XATTR 0 +#else +#define __UAPI_DEF_XATTR 1 +#endif + +/* If we did not see any headers from any supported C libraries, + * or we are being included in the kernel, then define everything + * that we need. Check for previous __UAPI_* definitions to give + * unsupported C libraries a way to opt out of any kernel definition. */ +#else /* !defined(__GLIBC__) */ + +/* Definitions for if.h */ +#ifndef __UAPI_DEF_IF_IFCONF +#define __UAPI_DEF_IF_IFCONF 1 +#endif +#ifndef __UAPI_DEF_IF_IFMAP +#define __UAPI_DEF_IF_IFMAP 1 +#endif +#ifndef __UAPI_DEF_IF_IFNAMSIZ +#define __UAPI_DEF_IF_IFNAMSIZ 1 +#endif +#ifndef __UAPI_DEF_IF_IFREQ +#define __UAPI_DEF_IF_IFREQ 1 +#endif +/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1 +#endif +/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */ +#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO +#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1 +#endif + +/* Definitions for in.h */ +#ifndef __UAPI_DEF_IN_ADDR +#define __UAPI_DEF_IN_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN_IPPROTO +#define __UAPI_DEF_IN_IPPROTO 1 +#endif +#ifndef __UAPI_DEF_IN_PKTINFO +#define __UAPI_DEF_IN_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP_MREQ +#define __UAPI_DEF_IP_MREQ 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN +#define __UAPI_DEF_SOCKADDR_IN 1 +#endif +#ifndef __UAPI_DEF_IN_CLASS +#define __UAPI_DEF_IN_CLASS 1 +#endif + +/* Definitions for in6.h */ +#ifndef __UAPI_DEF_IN6_ADDR +#define __UAPI_DEF_IN6_ADDR 1 +#endif +#ifndef __UAPI_DEF_IN6_ADDR_ALT +#define __UAPI_DEF_IN6_ADDR_ALT 1 +#endif +#ifndef __UAPI_DEF_SOCKADDR_IN6 +#define __UAPI_DEF_SOCKADDR_IN6 1 +#endif +#ifndef __UAPI_DEF_IPV6_MREQ +#define __UAPI_DEF_IPV6_MREQ 1 +#endif +#ifndef __UAPI_DEF_IPPROTO_V6 +#define __UAPI_DEF_IPPROTO_V6 1 +#endif +#ifndef __UAPI_DEF_IPV6_OPTIONS +#define __UAPI_DEF_IPV6_OPTIONS 1 +#endif +#ifndef __UAPI_DEF_IN6_PKTINFO +#define __UAPI_DEF_IN6_PKTINFO 1 +#endif +#ifndef __UAPI_DEF_IP6_MTUINFO +#define __UAPI_DEF_IP6_MTUINFO 1 +#endif + +/* Definitions for ipx.h */ +#ifndef __UAPI_DEF_SOCKADDR_IPX +#define __UAPI_DEF_SOCKADDR_IPX 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION +#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION +#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1 +#endif +#ifndef __UAPI_DEF_IPX_CONFIG_DATA +#define __UAPI_DEF_IPX_CONFIG_DATA 1 +#endif +#ifndef __UAPI_DEF_IPX_ROUTE_DEF +#define __UAPI_DEF_IPX_ROUTE_DEF 1 +#endif + +/* Definitions for xattr.h */ +#ifndef __UAPI_DEF_XATTR +#define __UAPI_DEF_XATTR 1 +#endif + +#endif /* __GLIBC__ */ + +#endif /* _LIBC_COMPAT_H */ diff --git a/include/uapi/linux/limits.h b/include/uapi/linux/limits.h new file mode 100644 index 0000000..c3547f0 --- /dev/null +++ b/include/uapi/linux/limits.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_LIMITS_H +#define _LINUX_LIMITS_H + +#define NR_OPEN 1024 + +#define NGROUPS_MAX 65536 /* supplemental group IDs are available */ +#define ARG_MAX 131072 /* # bytes of args + environ for exec() */ +#define LINK_MAX 127 /* # links a file may have */ +#define MAX_CANON 255 /* size of the canonical input queue */ +#define MAX_INPUT 255 /* size of the type-ahead buffer */ +#define NAME_MAX 255 /* # chars in a file name */ +#define PATH_MAX 4096 /* # chars in a path name including nul */ +#define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */ +#define XATTR_NAME_MAX 255 /* # chars in an extended attribute name */ +#define XATTR_SIZE_MAX 65536 /* size of an extended attribute value (64k) */ +#define XATTR_LIST_MAX 65536 /* size of extended attribute namelist (64k) */ + +#define RTSIG_MAX 32 + +#endif diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h new file mode 100644 index 0000000..b7c0191 --- /dev/null +++ b/include/uapi/linux/lwtunnel.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LWTUNNEL_H_ +#define _LWTUNNEL_H_ + +#include + +enum lwtunnel_encap_types { + LWTUNNEL_ENCAP_NONE, + LWTUNNEL_ENCAP_MPLS, + LWTUNNEL_ENCAP_IP, + LWTUNNEL_ENCAP_ILA, + LWTUNNEL_ENCAP_IP6, + LWTUNNEL_ENCAP_SEG6, + LWTUNNEL_ENCAP_BPF, + LWTUNNEL_ENCAP_SEG6_LOCAL, + LWTUNNEL_ENCAP_RPL, + __LWTUNNEL_ENCAP_MAX, +}; + +#define LWTUNNEL_ENCAP_MAX (__LWTUNNEL_ENCAP_MAX - 1) + +enum lwtunnel_ip_t { + LWTUNNEL_IP_UNSPEC, + LWTUNNEL_IP_ID, + LWTUNNEL_IP_DST, + LWTUNNEL_IP_SRC, + LWTUNNEL_IP_TTL, + LWTUNNEL_IP_TOS, + LWTUNNEL_IP_FLAGS, + LWTUNNEL_IP_PAD, + LWTUNNEL_IP_OPTS, + __LWTUNNEL_IP_MAX, +}; + +#define LWTUNNEL_IP_MAX (__LWTUNNEL_IP_MAX - 1) + +enum lwtunnel_ip6_t { + LWTUNNEL_IP6_UNSPEC, + LWTUNNEL_IP6_ID, + LWTUNNEL_IP6_DST, + LWTUNNEL_IP6_SRC, + LWTUNNEL_IP6_HOPLIMIT, + LWTUNNEL_IP6_TC, + LWTUNNEL_IP6_FLAGS, + LWTUNNEL_IP6_PAD, + LWTUNNEL_IP6_OPTS, + __LWTUNNEL_IP6_MAX, +}; + +#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) + +enum { + LWTUNNEL_IP_OPTS_UNSPEC, + LWTUNNEL_IP_OPTS_GENEVE, + LWTUNNEL_IP_OPTS_VXLAN, + LWTUNNEL_IP_OPTS_ERSPAN, + __LWTUNNEL_IP_OPTS_MAX, +}; + +#define LWTUNNEL_IP_OPTS_MAX (__LWTUNNEL_IP_OPTS_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_GENEVE_UNSPEC, + LWTUNNEL_IP_OPT_GENEVE_CLASS, + LWTUNNEL_IP_OPT_GENEVE_TYPE, + LWTUNNEL_IP_OPT_GENEVE_DATA, + __LWTUNNEL_IP_OPT_GENEVE_MAX, +}; + +#define LWTUNNEL_IP_OPT_GENEVE_MAX (__LWTUNNEL_IP_OPT_GENEVE_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_VXLAN_UNSPEC, + LWTUNNEL_IP_OPT_VXLAN_GBP, + __LWTUNNEL_IP_OPT_VXLAN_MAX, +}; + +#define LWTUNNEL_IP_OPT_VXLAN_MAX (__LWTUNNEL_IP_OPT_VXLAN_MAX - 1) + +enum { + LWTUNNEL_IP_OPT_ERSPAN_UNSPEC, + LWTUNNEL_IP_OPT_ERSPAN_VER, + LWTUNNEL_IP_OPT_ERSPAN_INDEX, + LWTUNNEL_IP_OPT_ERSPAN_DIR, + LWTUNNEL_IP_OPT_ERSPAN_HWID, + __LWTUNNEL_IP_OPT_ERSPAN_MAX, +}; + +#define LWTUNNEL_IP_OPT_ERSPAN_MAX (__LWTUNNEL_IP_OPT_ERSPAN_MAX - 1) + +enum { + LWT_BPF_PROG_UNSPEC, + LWT_BPF_PROG_FD, + LWT_BPF_PROG_NAME, + __LWT_BPF_PROG_MAX, +}; + +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) + +enum { + LWT_BPF_UNSPEC, + LWT_BPF_IN, + LWT_BPF_OUT, + LWT_BPF_XMIT, + LWT_BPF_XMIT_HEADROOM, + __LWT_BPF_MAX, +}; + +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1) + +#define LWT_BPF_MAX_HEADROOM 256 + +#endif /* _LWTUNNEL_H_ */ diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h new file mode 100644 index 0000000..f3956fc --- /dev/null +++ b/include/uapi/linux/magic.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_MAGIC_H__ +#define __LINUX_MAGIC_H__ + +#define ADFS_SUPER_MAGIC 0xadf5 +#define AFFS_SUPER_MAGIC 0xadff +#define AFS_SUPER_MAGIC 0x5346414F +#define AUTOFS_SUPER_MAGIC 0x0187 +#define CODA_SUPER_MAGIC 0x73757245 +#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ +#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */ +#define DEBUGFS_MAGIC 0x64626720 +#define SECURITYFS_MAGIC 0x73636673 +#define SELINUX_MAGIC 0xf97cff8c +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ +#define RAMFS_MAGIC 0x858458f6 /* some random number */ +#define TMPFS_MAGIC 0x01021994 +#define HUGETLBFS_MAGIC 0x958458f6 /* some random number */ +#define SQUASHFS_MAGIC 0x73717368 +#define ECRYPTFS_SUPER_MAGIC 0xf15f +#define EFS_SUPER_MAGIC 0x414A53 +#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2 +#define EXT2_SUPER_MAGIC 0xEF53 +#define EXT3_SUPER_MAGIC 0xEF53 +#define XENFS_SUPER_MAGIC 0xabba1974 +#define EXT4_SUPER_MAGIC 0xEF53 +#define BTRFS_SUPER_MAGIC 0x9123683E +#define NILFS_SUPER_MAGIC 0x3434 +#define F2FS_SUPER_MAGIC 0xF2F52010 +#define HPFS_SUPER_MAGIC 0xf995e849 +#define ISOFS_SUPER_MAGIC 0x9660 +#define JFFS2_SUPER_MAGIC 0x72b6 +#define XFS_SUPER_MAGIC 0x58465342 /* "XFSB" */ +#define PSTOREFS_MAGIC 0x6165676C +#define EFIVARFS_MAGIC 0xde5e81e4 +#define HOSTFS_SUPER_MAGIC 0x00c0ffee +#define OVERLAYFS_SUPER_MAGIC 0x794c7630 + +#define MINIX_SUPER_MAGIC 0x137F /* minix v1 fs, 14 char names */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix v1 fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix v2 fs, 14 char names */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix v2 fs, 30 char names */ +#define MINIX3_SUPER_MAGIC 0x4d5a /* minix v3 fs, 60 char names */ + +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ +#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */ +#define NFS_SUPER_MAGIC 0x6969 +#define OCFS2_SUPER_MAGIC 0x7461636f +#define OPENPROM_SUPER_MAGIC 0x9fa1 +#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ +#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */ +#define AFS_FS_MAGIC 0x6B414653 + +#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */ + /* used by file system utilities that + look at the superblock, etc. */ +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + +#define SMB_SUPER_MAGIC 0x517B +#define CGROUP_SUPER_MAGIC 0x27e0eb +#define CGROUP2_SUPER_MAGIC 0x63677270 + +#define RDTGROUP_SUPER_MAGIC 0x7655821 + +#define STACK_END_MAGIC 0x57AC6E9D + +#define TRACEFS_MAGIC 0x74726163 + +#define V9FS_MAGIC 0x01021997 + +#define BDEVFS_MAGIC 0x62646576 +#define DAXFS_MAGIC 0x64646178 +#define BINFMTFS_MAGIC 0x42494e4d +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#define BINDERFS_SUPER_MAGIC 0x6c6f6f70 +#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA +#define PIPEFS_MAGIC 0x50495045 +#define PROC_SUPER_MAGIC 0x9fa0 +#define SOCKFS_MAGIC 0x534F434B +#define SYSFS_MAGIC 0x62656572 +#define USBDEVICE_SUPER_MAGIC 0x9fa2 +#define MTD_INODE_FS_MAGIC 0x11307854 +#define ANON_INODE_FS_MAGIC 0x09041934 +#define BTRFS_TEST_MAGIC 0x73727279 +#define NSFS_MAGIC 0x6e736673 +#define BPF_FS_MAGIC 0xcafe4a11 +#define AAFS_MAGIC 0x5a3c69f0 +#define ZONEFS_MAGIC 0x5a4f4653 + +/* Since UDF 2.01 is ISO 13346 based... */ +#define UDF_SUPER_MAGIC 0x15013346 +#define BALLOON_KVM_MAGIC 0x13661366 +#define ZSMALLOC_MAGIC 0x58295829 +#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */ +#define DEVMEM_MAGIC 0x454d444d /* "DMEM" */ +#define Z3FOLD_MAGIC 0x33 +#define PPC_CMM_MAGIC 0xc7571590 + +#endif /* __LINUX_MAGIC_H__ */ diff --git a/include/uapi/linux/mpls.h b/include/uapi/linux/mpls.h new file mode 100644 index 0000000..9effbf9 --- /dev/null +++ b/include/uapi/linux/mpls.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _MPLS_H +#define _MPLS_H + +#include +#include + +/* Reference: RFC 5462, RFC 3032 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Label | TC |S| TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Label: Label Value, 20 bits + * TC: Traffic Class field, 3 bits + * S: Bottom of Stack, 1 bit + * TTL: Time to Live, 8 bits + */ + +struct mpls_label { + __be32 entry; +}; + +#define MPLS_LS_LABEL_MASK 0xFFFFF000 +#define MPLS_LS_LABEL_SHIFT 12 +#define MPLS_LS_TC_MASK 0x00000E00 +#define MPLS_LS_TC_SHIFT 9 +#define MPLS_LS_S_MASK 0x00000100 +#define MPLS_LS_S_SHIFT 8 +#define MPLS_LS_TTL_MASK 0x000000FF +#define MPLS_LS_TTL_SHIFT 0 + +/* Reserved labels */ +#define MPLS_LABEL_IPV4NULL 0 /* RFC3032 */ +#define MPLS_LABEL_RTALERT 1 /* RFC3032 */ +#define MPLS_LABEL_IPV6NULL 2 /* RFC3032 */ +#define MPLS_LABEL_IMPLNULL 3 /* RFC3032 */ +#define MPLS_LABEL_ENTROPY 7 /* RFC6790 */ +#define MPLS_LABEL_GAL 13 /* RFC5586 */ +#define MPLS_LABEL_OAMALERT 14 /* RFC3429 */ +#define MPLS_LABEL_EXTENSION 15 /* RFC7274 */ + +#define MPLS_LABEL_FIRST_UNRESERVED 16 /* RFC3032 */ + +/* These are embedded into IFLA_STATS_AF_SPEC: + * [IFLA_STATS_AF_SPEC] + * -> [AF_MPLS] + * -> [MPLS_STATS_xxx] + * + * Attributes: + * [MPLS_STATS_LINK] = { + * struct mpls_link_stats + * } + */ +enum { + MPLS_STATS_UNSPEC, /* also used as 64bit pad attribute */ + MPLS_STATS_LINK, + __MPLS_STATS_MAX, +}; + +#define MPLS_STATS_MAX (__MPLS_STATS_MAX - 1) + +struct mpls_link_stats { + __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; /* packet dropped on receive */ + __u64 tx_dropped; /* packet dropped on transmit */ + __u64 rx_noroute; /* no route for packet dest */ +}; + +#endif /* _MPLS_H */ diff --git a/include/uapi/linux/mpls_iptunnel.h b/include/uapi/linux/mpls_iptunnel.h new file mode 100644 index 0000000..2c69b7d --- /dev/null +++ b/include/uapi/linux/mpls_iptunnel.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * mpls tunnel api + * + * Authors: + * Roopa Prabhu + * + * 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_MPLS_IPTUNNEL_H +#define _LINUX_MPLS_IPTUNNEL_H + +/* MPLS tunnel attributes + * [RTA_ENCAP] = { + * [MPLS_IPTUNNEL_DST] + * [MPLS_IPTUNNEL_TTL] + * } + */ +enum { + MPLS_IPTUNNEL_UNSPEC, + MPLS_IPTUNNEL_DST, + MPLS_IPTUNNEL_TTL, + __MPLS_IPTUNNEL_MAX, +}; +#define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) + +#endif /* _LINUX_MPLS_IPTUNNEL_H */ diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h new file mode 100644 index 0000000..3218123 --- /dev/null +++ b/include/uapi/linux/mptcp.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +#ifndef _MPTCP_H +#define _MPTCP_H + +#include +#include + +#define MPTCP_SUBFLOW_FLAG_MCAP_REM _BITUL(0) +#define MPTCP_SUBFLOW_FLAG_MCAP_LOC _BITUL(1) +#define MPTCP_SUBFLOW_FLAG_JOIN_REM _BITUL(2) +#define MPTCP_SUBFLOW_FLAG_JOIN_LOC _BITUL(3) +#define MPTCP_SUBFLOW_FLAG_BKUP_REM _BITUL(4) +#define MPTCP_SUBFLOW_FLAG_BKUP_LOC _BITUL(5) +#define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED _BITUL(6) +#define MPTCP_SUBFLOW_FLAG_CONNECTED _BITUL(7) +#define MPTCP_SUBFLOW_FLAG_MAPVALID _BITUL(8) + +enum { + MPTCP_SUBFLOW_ATTR_UNSPEC, + MPTCP_SUBFLOW_ATTR_TOKEN_REM, + MPTCP_SUBFLOW_ATTR_TOKEN_LOC, + MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ, + MPTCP_SUBFLOW_ATTR_MAP_SEQ, + MPTCP_SUBFLOW_ATTR_MAP_SFSEQ, + MPTCP_SUBFLOW_ATTR_SSN_OFFSET, + MPTCP_SUBFLOW_ATTR_MAP_DATALEN, + MPTCP_SUBFLOW_ATTR_FLAGS, + MPTCP_SUBFLOW_ATTR_ID_REM, + MPTCP_SUBFLOW_ATTR_ID_LOC, + MPTCP_SUBFLOW_ATTR_PAD, + __MPTCP_SUBFLOW_ATTR_MAX +}; + +#define MPTCP_SUBFLOW_ATTR_MAX (__MPTCP_SUBFLOW_ATTR_MAX - 1) + +/* netlink interface */ +#define MPTCP_PM_NAME "mptcp_pm" +#define MPTCP_PM_CMD_GRP_NAME "mptcp_pm_cmds" +#define MPTCP_PM_VER 0x1 + +/* + * ATTR types defined for MPTCP + */ +enum { + MPTCP_PM_ATTR_UNSPEC, + + MPTCP_PM_ATTR_ADDR, /* nested address */ + MPTCP_PM_ATTR_RCV_ADD_ADDRS, /* u32 */ + MPTCP_PM_ATTR_SUBFLOWS, /* u32 */ + + __MPTCP_PM_ATTR_MAX +}; + +#define MPTCP_PM_ATTR_MAX (__MPTCP_PM_ATTR_MAX - 1) + +enum { + MPTCP_PM_ADDR_ATTR_UNSPEC, + + MPTCP_PM_ADDR_ATTR_FAMILY, /* u16 */ + MPTCP_PM_ADDR_ATTR_ID, /* u8 */ + MPTCP_PM_ADDR_ATTR_ADDR4, /* struct in_addr */ + MPTCP_PM_ADDR_ATTR_ADDR6, /* struct in6_addr */ + MPTCP_PM_ADDR_ATTR_PORT, /* u16 */ + MPTCP_PM_ADDR_ATTR_FLAGS, /* u32 */ + MPTCP_PM_ADDR_ATTR_IF_IDX, /* s32 */ + + __MPTCP_PM_ADDR_ATTR_MAX +}; + +#define MPTCP_PM_ADDR_ATTR_MAX (__MPTCP_PM_ADDR_ATTR_MAX - 1) + +#define MPTCP_PM_ADDR_FLAG_SIGNAL (1 << 0) +#define MPTCP_PM_ADDR_FLAG_SUBFLOW (1 << 1) +#define MPTCP_PM_ADDR_FLAG_BACKUP (1 << 2) + +enum { + MPTCP_PM_CMD_UNSPEC, + + MPTCP_PM_CMD_ADD_ADDR, + MPTCP_PM_CMD_DEL_ADDR, + MPTCP_PM_CMD_GET_ADDR, + MPTCP_PM_CMD_FLUSH_ADDRS, + MPTCP_PM_CMD_SET_LIMITS, + MPTCP_PM_CMD_GET_LIMITS, + + __MPTCP_PM_CMD_AFTER_LAST +}; + +#define MPTCP_INFO_FLAG_FALLBACK _BITUL(0) +#define MPTCP_INFO_FLAG_REMOTE_KEY_RECEIVED _BITUL(1) + +struct mptcp_info { + __u8 mptcpi_subflows; + __u8 mptcpi_add_addr_signal; + __u8 mptcpi_add_addr_accepted; + __u8 mptcpi_subflows_max; + __u8 mptcpi_add_addr_signal_max; + __u8 mptcpi_add_addr_accepted_max; + __u32 mptcpi_flags; + __u32 mptcpi_token; + __u64 mptcpi_write_seq; + __u64 mptcpi_snd_una; + __u64 mptcpi_rcv_nxt; +}; + +#endif /* _MPTCP_H */ diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h new file mode 100644 index 0000000..dc8b722 --- /dev/null +++ b/include/uapi/linux/neighbour.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include +#include + +struct ndmsg { + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum { + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + NDA_VLAN, + NDA_PORT, + NDA_VNI, + NDA_IFINDEX, + NDA_MASTER, + NDA_LINK_NETNSID, + NDA_SRC_VNI, + NDA_PROTOCOL, /* Originator of entry */ + NDA_NH_ID, + NDA_FDB_EXT_ATTRS, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_USE 0x01 +#define NTF_SELF 0x02 +#define NTF_MASTER 0x04 +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_EXT_LEARNED 0x10 +#define NTF_OFFLOADED 0x20 +#define NTF_STICKY 0x40 +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + +/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change + and make no address resolution or NUD. + NUD_PERMANENT also cannot be deleted by garbage collectors. + */ + +struct nda_cacheinfo { + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/***************************************************************** + * Neighbour tables specific messages. + * + * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the + * NLM_F_DUMP flag set. Every neighbour table configuration is + * spread over multiple messages to avoid running into message + * size limits on systems with many interfaces. The first message + * in the sequence transports all not device specific data such as + * statistics, configuration, and the default parameter set. + * This message is followed by 0..n messages carrying device + * specific parameter sets. + * Although the ordering should be sufficient, NDTA_NAME can be + * used to identify sequences. The initial message can be identified + * by checking for NDTA_CONFIG. The device specific messages do + * not contain this TLV but have NDTPA_IFINDEX set to the + * corresponding interface index. + * + * To change neighbour table attributes, send RTM_SETNEIGHTBL + * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3], + * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked + * otherwise. Device specific parameter sets can be changed by + * setting NDTPA_IFINDEX to the interface index of the corresponding + * device. + ****/ + +struct ndt_stats { + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; + __u64 ndts_table_fulls; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, /* u32, unchangeable */ + NDTPA_REFCNT, /* u32, read-only */ + NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */ + NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */ + NDTPA_RETRANS_TIME, /* u64, msecs */ + NDTPA_GC_STALETIME, /* u64, msecs */ + NDTPA_DELAY_PROBE_TIME, /* u64, msecs */ + NDTPA_QUEUE_LEN, /* u32 */ + NDTPA_APP_PROBES, /* u32 */ + NDTPA_UCAST_PROBES, /* u32 */ + NDTPA_MCAST_PROBES, /* u32 */ + NDTPA_ANYCAST_DELAY, /* u64, msecs */ + NDTPA_PROXY_DELAY, /* u64, msecs */ + NDTPA_PROXY_QLEN, /* u32 */ + NDTPA_LOCKTIME, /* u64, msecs */ + NDTPA_QUEUE_LENBYTES, /* u32 */ + NDTPA_MCAST_REPROBES, /* u32 */ + NDTPA_PAD, + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg { + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config { + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + NDTA_PAD, + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + + /* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY: + * - FDB_NOTIFY_BIT - notify on activity/expire for any entry + * - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications + */ +enum { + FDB_NOTIFY_BIT = (1 << 0), + FDB_NOTIFY_INACTIVE_BIT = (1 << 1) +}; + +/* embedded into NDA_FDB_EXT_ATTRS: + * [NDA_FDB_EXT_ATTRS] = { + * [NFEA_ACTIVITY_NOTIFY] + * ... + * } + */ +enum { + NFEA_UNSPEC, + NFEA_ACTIVITY_NOTIFY, + NFEA_DONT_REFRESH, + __NFEA_MAX +}; +#define NFEA_MAX (__NFEA_MAX - 1) + +#endif diff --git a/include/uapi/linux/net.h b/include/uapi/linux/net.h new file mode 100644 index 0000000..4754f70 --- /dev/null +++ b/include/uapi/linux/net.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * NET An implementation of the SOCKET network access protocol. + * This is the master header file for the Linux NET layer, + * or, in plain English: the networking handling part of the + * kernel. + * + * Version: @(#)net.h 1.0.3 05/25/93 + * + * Authors: Orest Zborowski, + * 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_NET_H +#define _LINUX_NET_H + +#include +#include + +#define NPROTO AF_MAX + +#define SYS_SOCKET 1 /* sys_socket(2) */ +#define SYS_BIND 2 /* sys_bind(2) */ +#define SYS_CONNECT 3 /* sys_connect(2) */ +#define SYS_LISTEN 4 /* sys_listen(2) */ +#define SYS_ACCEPT 5 /* sys_accept(2) */ +#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */ +#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */ +#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */ +#define SYS_SEND 9 /* sys_send(2) */ +#define SYS_RECV 10 /* sys_recv(2) */ +#define SYS_SENDTO 11 /* sys_sendto(2) */ +#define SYS_RECVFROM 12 /* sys_recvfrom(2) */ +#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */ +#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */ +#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */ +#define SYS_SENDMSG 16 /* sys_sendmsg(2) */ +#define SYS_RECVMSG 17 /* sys_recvmsg(2) */ +#define SYS_ACCEPT4 18 /* sys_accept4(2) */ +#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */ +#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ + +typedef enum { + SS_FREE = 0, /* not allocated */ + SS_UNCONNECTED, /* unconnected to any socket */ + SS_CONNECTING, /* in process of connecting */ + SS_CONNECTED, /* connected to socket */ + SS_DISCONNECTING /* in process of disconnecting */ +} socket_state; + +#define __SO_ACCEPTCON (1 << 16) /* performed a listen */ + +#endif /* _LINUX_NET_H */ diff --git a/include/uapi/linux/net_namespace.h b/include/uapi/linux/net_namespace.h new file mode 100644 index 0000000..fa81f1e --- /dev/null +++ b/include/uapi/linux/net_namespace.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * 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. + */ +#ifndef _LINUX_NET_NAMESPACE_H_ +#define _LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + NETNSA_TARGET_NSID, + NETNSA_CURRENT_NSID, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _LINUX_NET_NAMESPACE_H_ */ diff --git a/include/uapi/linux/netconf.h b/include/uapi/linux/netconf.h new file mode 100644 index 0000000..229e885 --- /dev/null +++ b/include/uapi/linux/netconf.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NETCONF_H_ +#define _LINUX_NETCONF_H_ + +#include +#include + +struct netconfmsg { + __u8 ncm_family; +}; + +enum { + NETCONFA_UNSPEC, + NETCONFA_IFINDEX, + NETCONFA_FORWARDING, + NETCONFA_RP_FILTER, + NETCONFA_MC_FORWARDING, + NETCONFA_PROXY_NEIGH, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + NETCONFA_INPUT, + NETCONFA_BC_FORWARDING, + __NETCONFA_MAX +}; +#define NETCONFA_MAX (__NETCONFA_MAX - 1) +#define NETCONFA_ALL -1 + +#define NETCONFA_IFINDEX_ALL -1 +#define NETCONFA_IFINDEX_DEFAULT -2 + +#endif /* _LINUX_NETCONF_H_ */ diff --git a/include/uapi/linux/netdevice.h b/include/uapi/linux/netdevice.h new file mode 100644 index 0000000..86d961c --- /dev/null +++ b/include/uapi/linux/netdevice.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions for the Interfaces handler. + * + * Version: @(#)dev.h 1.0.10 08/12/93 + * + * Authors: Ross Biro + * Fred N. van Kempen, + * Corey Minyard + * Donald J. Becker, + * Alan Cox, + * Bjorn Ekwall. + * Pekka Riikonen + * + * 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. + * + * Moved to /usr/include/linux for NET3 + */ +#ifndef _LINUX_NETDEVICE_H +#define _LINUX_NETDEVICE_H + +#include +#include +#include +#include + + +#define MAX_ADDR_LEN 32 /* Largest hardware address length */ + +/* Initial net device group. All devices belong to group 0 by default. */ +#define INIT_NETDEV_GROUP 0 + + +/* interface name assignment types (sysfs name_assign_type attribute) */ +#define NET_NAME_UNKNOWN 0 /* unknown origin (not exposed to userspace) */ +#define NET_NAME_ENUM 1 /* enumerated by kernel */ +#define NET_NAME_PREDICTABLE 2 /* predictably named by the kernel */ +#define NET_NAME_USER 3 /* provided by user-space */ +#define NET_NAME_RENAMED 4 /* renamed by user-space */ + +/* Media selection options. */ +enum { + IF_PORT_UNKNOWN = 0, + IF_PORT_10BASE2, + IF_PORT_10BASET, + IF_PORT_AUI, + IF_PORT_100BASET, + IF_PORT_100BASETX, + IF_PORT_100BASEFX +}; + +/* hardware address assignment types */ +#define NET_ADDR_PERM 0 /* address is permanent (default) */ +#define NET_ADDR_RANDOM 1 /* address is generated randomly */ +#define NET_ADDR_STOLEN 2 /* address is stolen from other device */ +#define NET_ADDR_SET 3 /* address is set using + * dev_set_mac_address() */ + +#endif /* _LINUX_NETDEVICE_H */ diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h new file mode 100644 index 0000000..899be98 --- /dev/null +++ b/include/uapi/linux/netfilter.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETFILTER_H +#define __LINUX_NETFILTER_H + +#include + +#include +#include + +/* Responses from hook functions. */ +#define NF_DROP 0 +#define NF_ACCEPT 1 +#define NF_STOLEN 2 +#define NF_QUEUE 3 +#define NF_REPEAT 4 +#define NF_STOP 5 /* Deprecated, for userspace nf_queue compatibility. */ +#define NF_MAX_VERDICT NF_STOP + +/* we overload the higher bits for encoding auxiliary data such as the queue + * number or errno values. Not nice, but better than additional function + * arguments. */ +#define NF_VERDICT_MASK 0x000000ff + +/* extra verdict flags have mask 0x0000ff00 */ +#define NF_VERDICT_FLAG_QUEUE_BYPASS 0x00008000 + +/* queue number (NF_QUEUE) or errno (NF_DROP) */ +#define NF_VERDICT_QMASK 0xffff0000 +#define NF_VERDICT_QBITS 16 + +#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE) + +#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP) + +/* only for userspace compatibility */ + +/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */ +#define NF_VERDICT_BITS 16 + +enum nf_inet_hooks { + NF_INET_PRE_ROUTING, + NF_INET_LOCAL_IN, + NF_INET_FORWARD, + NF_INET_LOCAL_OUT, + NF_INET_POST_ROUTING, + NF_INET_NUMHOOKS +}; + +enum nf_dev_hooks { + NF_NETDEV_INGRESS, + NF_NETDEV_NUMHOOKS +}; + +enum { + NFPROTO_UNSPEC = 0, + NFPROTO_INET = 1, + NFPROTO_IPV4 = 2, + NFPROTO_ARP = 3, + NFPROTO_NETDEV = 5, + NFPROTO_BRIDGE = 7, + NFPROTO_IPV6 = 10, + NFPROTO_DECNET = 12, + NFPROTO_NUMPROTO, +}; + +union nf_inet_addr { + __u32 all[4]; + __be32 ip; + __be32 ip6[4]; + struct in_addr in; + struct in6_addr in6; +}; + +#endif /* __LINUX_NETFILTER_H */ diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h new file mode 100644 index 0000000..4b372f4 --- /dev/null +++ b/include/uapi/linux/netfilter/ipset/ip_set.h @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson + * Copyright (C) 2003-2011 Jozsef Kadlecsik + * + * 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. + */ +#ifndef _IP_SET_H +#define _IP_SET_H + +#include + +/* The protocol versions */ +#define IPSET_PROTOCOL 7 +#define IPSET_PROTOCOL_MIN 6 + +/* The max length of strings including NUL: set and type identifiers */ +#define IPSET_MAXNAMELEN 32 + +/* The maximum permissible comment length we will accept over netlink */ +#define IPSET_MAX_COMMENT_SIZE 255 + +/* Message types and commands */ +enum ipset_cmd { + IPSET_CMD_NONE, + IPSET_CMD_PROTOCOL, /* 1: Return protocol version */ + IPSET_CMD_CREATE, /* 2: Create a new (empty) set */ + IPSET_CMD_DESTROY, /* 3: Destroy a (empty) set */ + IPSET_CMD_FLUSH, /* 4: Remove all elements from a set */ + IPSET_CMD_RENAME, /* 5: Rename a set */ + IPSET_CMD_SWAP, /* 6: Swap two sets */ + IPSET_CMD_LIST, /* 7: List sets */ + IPSET_CMD_SAVE, /* 8: Save sets */ + IPSET_CMD_ADD, /* 9: Add an element to a set */ + IPSET_CMD_DEL, /* 10: Delete an element from a set */ + IPSET_CMD_TEST, /* 11: Test an element in a set */ + IPSET_CMD_HEADER, /* 12: Get set header data only */ + IPSET_CMD_TYPE, /* 13: Get set type */ + IPSET_CMD_GET_BYNAME, /* 14: Get set index by name */ + IPSET_CMD_GET_BYINDEX, /* 15: Get set name by index */ + IPSET_MSG_MAX, /* Netlink message commands */ + + /* Commands in userspace: */ + IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 16: Enter restore mode */ + IPSET_CMD_HELP, /* 17: Get help */ + IPSET_CMD_VERSION, /* 18: Get program version */ + IPSET_CMD_QUIT, /* 19: Quit from interactive mode */ + + IPSET_CMD_MAX, + + IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 20: Commit buffered commands */ +}; + +/* Attributes at command level */ +enum { + IPSET_ATTR_UNSPEC, + IPSET_ATTR_PROTOCOL, /* 1: Protocol version */ + IPSET_ATTR_SETNAME, /* 2: Name of the set */ + IPSET_ATTR_TYPENAME, /* 3: Typename */ + IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */ + IPSET_ATTR_REVISION, /* 4: Settype revision */ + IPSET_ATTR_FAMILY, /* 5: Settype family */ + IPSET_ATTR_FLAGS, /* 6: Flags at command level */ + IPSET_ATTR_DATA, /* 7: Nested attributes */ + IPSET_ATTR_ADT, /* 8: Multiple data containers */ + IPSET_ATTR_LINENO, /* 9: Restore lineno */ + IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */ + IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */ + IPSET_ATTR_INDEX, /* 11: Kernel index of set */ + __IPSET_ATTR_CMD_MAX, +}; +#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1) + +/* CADT specific attributes */ +enum { + IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1, + IPSET_ATTR_IP_FROM = IPSET_ATTR_IP, + IPSET_ATTR_IP_TO, /* 2 */ + IPSET_ATTR_CIDR, /* 3 */ + IPSET_ATTR_PORT, /* 4 */ + IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT, + IPSET_ATTR_PORT_TO, /* 5 */ + IPSET_ATTR_TIMEOUT, /* 6 */ + IPSET_ATTR_PROTO, /* 7 */ + IPSET_ATTR_CADT_FLAGS, /* 8 */ + IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ + IPSET_ATTR_MARK, /* 10 */ + IPSET_ATTR_MARKMASK, /* 11 */ + /* Reserve empty slots */ + IPSET_ATTR_CADT_MAX = 16, + /* Create-only specific attributes */ + IPSET_ATTR_GC, + IPSET_ATTR_HASHSIZE, + IPSET_ATTR_MAXELEM, + IPSET_ATTR_NETMASK, + IPSET_ATTR_PROBES, + IPSET_ATTR_RESIZE, + IPSET_ATTR_SIZE, + /* Kernel-only */ + IPSET_ATTR_ELEMENTS, + IPSET_ATTR_REFERENCES, + IPSET_ATTR_MEMSIZE, + + __IPSET_ATTR_CREATE_MAX, +}; +#define IPSET_ATTR_CREATE_MAX (__IPSET_ATTR_CREATE_MAX - 1) + +/* ADT specific attributes */ +enum { + IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1, + IPSET_ATTR_NAME, + IPSET_ATTR_NAMEREF, + IPSET_ATTR_IP2, + IPSET_ATTR_CIDR2, + IPSET_ATTR_IP2_TO, + IPSET_ATTR_IFACE, + IPSET_ATTR_BYTES, + IPSET_ATTR_PACKETS, + IPSET_ATTR_COMMENT, + IPSET_ATTR_SKBMARK, + IPSET_ATTR_SKBPRIO, + IPSET_ATTR_SKBQUEUE, + IPSET_ATTR_PAD, + __IPSET_ATTR_ADT_MAX, +}; +#define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) + +/* IP specific attributes */ +enum { + IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1, + IPSET_ATTR_IPADDR_IPV6, + __IPSET_ATTR_IPADDR_MAX, +}; +#define IPSET_ATTR_IPADDR_MAX (__IPSET_ATTR_IPADDR_MAX - 1) + +/* Error codes */ +enum ipset_errno { + IPSET_ERR_PRIVATE = 4096, + IPSET_ERR_PROTOCOL, + IPSET_ERR_FIND_TYPE, + IPSET_ERR_MAX_SETS, + IPSET_ERR_BUSY, + IPSET_ERR_EXIST_SETNAME2, + IPSET_ERR_TYPE_MISMATCH, + IPSET_ERR_EXIST, + IPSET_ERR_INVALID_CIDR, + IPSET_ERR_INVALID_NETMASK, + IPSET_ERR_INVALID_FAMILY, + IPSET_ERR_TIMEOUT, + IPSET_ERR_REFERENCED, + IPSET_ERR_IPADDR_IPV4, + IPSET_ERR_IPADDR_IPV6, + IPSET_ERR_COUNTER, + IPSET_ERR_COMMENT, + IPSET_ERR_INVALID_MARKMASK, + IPSET_ERR_SKBINFO, + + /* Type specific error codes */ + IPSET_ERR_TYPE_SPECIFIC = 4352, +}; + +/* Flags at command level or match/target flags, lower half of cmdattrs*/ +enum ipset_cmd_flags { + IPSET_FLAG_BIT_EXIST = 0, + IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), + IPSET_FLAG_BIT_LIST_SETNAME = 1, + IPSET_FLAG_LIST_SETNAME = (1 << IPSET_FLAG_BIT_LIST_SETNAME), + IPSET_FLAG_BIT_LIST_HEADER = 2, + IPSET_FLAG_LIST_HEADER = (1 << IPSET_FLAG_BIT_LIST_HEADER), + IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE = 3, + IPSET_FLAG_SKIP_COUNTER_UPDATE = + (1 << IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE), + IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE = 4, + IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE = + (1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE), + IPSET_FLAG_BIT_MATCH_COUNTERS = 5, + IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS), + IPSET_FLAG_BIT_RETURN_NOMATCH = 7, + IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH), + IPSET_FLAG_BIT_MAP_SKBMARK = 8, + IPSET_FLAG_MAP_SKBMARK = (1 << IPSET_FLAG_BIT_MAP_SKBMARK), + IPSET_FLAG_BIT_MAP_SKBPRIO = 9, + IPSET_FLAG_MAP_SKBPRIO = (1 << IPSET_FLAG_BIT_MAP_SKBPRIO), + IPSET_FLAG_BIT_MAP_SKBQUEUE = 10, + IPSET_FLAG_MAP_SKBQUEUE = (1 << IPSET_FLAG_BIT_MAP_SKBQUEUE), + IPSET_FLAG_CMD_MAX = 15, +}; + +/* Flags at CADT attribute level, upper half of cmdattrs */ +enum ipset_cadt_flags { + IPSET_FLAG_BIT_BEFORE = 0, + IPSET_FLAG_BEFORE = (1 << IPSET_FLAG_BIT_BEFORE), + IPSET_FLAG_BIT_PHYSDEV = 1, + IPSET_FLAG_PHYSDEV = (1 << IPSET_FLAG_BIT_PHYSDEV), + IPSET_FLAG_BIT_NOMATCH = 2, + IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH), + IPSET_FLAG_BIT_WITH_COUNTERS = 3, + IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS), + IPSET_FLAG_BIT_WITH_COMMENT = 4, + IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT), + IPSET_FLAG_BIT_WITH_FORCEADD = 5, + IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD), + IPSET_FLAG_BIT_WITH_SKBINFO = 6, + IPSET_FLAG_WITH_SKBINFO = (1 << IPSET_FLAG_BIT_WITH_SKBINFO), + IPSET_FLAG_BIT_IFACE_WILDCARD = 7, + IPSET_FLAG_IFACE_WILDCARD = (1 << IPSET_FLAG_BIT_IFACE_WILDCARD), + IPSET_FLAG_CADT_MAX = 15, +}; + +/* The flag bits which correspond to the non-extension create flags */ +enum ipset_create_flags { + IPSET_CREATE_FLAG_BIT_FORCEADD = 0, + IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD), + IPSET_CREATE_FLAG_BIT_MAX = 7, +}; + +/* Commands with settype-specific attributes */ +enum ipset_adt { + IPSET_ADD, + IPSET_DEL, + IPSET_TEST, + IPSET_ADT_MAX, + IPSET_CREATE = IPSET_ADT_MAX, + IPSET_CADT_MAX, +}; + +/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t + * and IPSET_INVALID_ID if you want to increase the max number of sets. + * Also, IPSET_ATTR_INDEX must be changed. + */ +typedef __u16 ip_set_id_t; + +#define IPSET_INVALID_ID 65535 + +enum ip_set_dim { + IPSET_DIM_ZERO = 0, + IPSET_DIM_ONE, + IPSET_DIM_TWO, + IPSET_DIM_THREE, + /* Max dimension in elements. + * If changed, new revision of iptables match/target is required. + */ + IPSET_DIM_MAX = 6, + /* Backward compatibility: set match revision 2 */ + IPSET_BIT_RETURN_NOMATCH = 7, +}; + +/* Option flags for kernel operations */ +enum ip_set_kopt { + IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO), + IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE), + IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO), + IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE), + IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH), +}; + +enum { + IPSET_COUNTER_NONE = 0, + IPSET_COUNTER_EQ, + IPSET_COUNTER_NE, + IPSET_COUNTER_LT, + IPSET_COUNTER_GT, +}; + +/* Backward compatibility for set match v3 */ +struct ip_set_counter_match0 { + __u8 op; + __u64 value; +}; + +struct ip_set_counter_match { + __aligned_u64 value; + __u8 op; +}; + +/* Interface to iptables/ip6tables */ + +#define SO_IP_SET 83 + +union ip_set_name_index { + char name[IPSET_MAXNAMELEN]; + ip_set_id_t index; +}; + +#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ +struct ip_set_req_get_set { + unsigned int op; + unsigned int version; + union ip_set_name_index set; +}; + +#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ +/* Uses ip_set_req_get_set */ + +#define IP_SET_OP_GET_FNAME 0x00000008 /* Get set index and family */ +struct ip_set_req_get_set_family { + unsigned int op; + unsigned int version; + unsigned int family; + union ip_set_name_index set; +}; + +#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ +struct ip_set_req_version { + unsigned int op; + unsigned int version; +}; + +#endif /* _IP_SET_H */ diff --git a/include/uapi/linux/netfilter/x_tables.h b/include/uapi/linux/netfilter/x_tables.h new file mode 100644 index 0000000..ae2fd12 --- /dev/null +++ b/include/uapi/linux/netfilter/x_tables.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _X_TABLES_H +#define _X_TABLES_H +#include +#include + +#define XT_FUNCTION_MAXNAMELEN 30 +#define XT_EXTENSION_MAXNAMELEN 29 +#define XT_TABLE_MAXNAMELEN 32 + +struct xt_entry_match { + union { + struct { + __u16 match_size; + + /* Used by userspace */ + char name[XT_EXTENSION_MAXNAMELEN]; + __u8 revision; + } user; + struct { + __u16 match_size; + + /* Used inside the kernel */ + struct xt_match *match; + } kernel; + + /* Total length */ + __u16 match_size; + } u; + + unsigned char data[0]; +}; + +struct xt_entry_target { + union { + struct { + __u16 target_size; + + /* Used by userspace */ + char name[XT_EXTENSION_MAXNAMELEN]; + __u8 revision; + } user; + struct { + __u16 target_size; + + /* Used inside the kernel */ + struct xt_target *target; + } kernel; + + /* Total length */ + __u16 target_size; + } u; + + unsigned char data[0]; +}; + +#define XT_TARGET_INIT(__name, __size) \ +{ \ + .target.u.user = { \ + .target_size = XT_ALIGN(__size), \ + .name = __name, \ + }, \ +} + +struct xt_standard_target { + struct xt_entry_target target; + int verdict; +}; + +struct xt_error_target { + struct xt_entry_target target; + char errorname[XT_FUNCTION_MAXNAMELEN]; +}; + +/* The argument to IPT_SO_GET_REVISION_*. Returns highest revision + * kernel supports, if >= revision. */ +struct xt_get_revision { + char name[XT_EXTENSION_MAXNAMELEN]; + __u8 revision; +}; + +/* CONTINUE verdict for targets */ +#define XT_CONTINUE 0xFFFFFFFF + +/* For standard target */ +#define XT_RETURN (-NF_REPEAT - 1) + +/* this is a dummy structure to find out the alignment requirement for a struct + * containing all the fundamental data types that are used in ipt_entry, + * ip6t_entry and arpt_entry. This sucks, and it is a hack. It will be my + * personal pleasure to remove it -HW + */ +struct _xt_align { + __u8 u8; + __u16 u16; + __u32 u32; + __u64 u64; +}; + +#define XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _xt_align)) + +/* Standard return verdict, or do jump. */ +#define XT_STANDARD_TARGET "" +/* Error verdict. */ +#define XT_ERROR_TARGET "ERROR" + +#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0) +#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0) + +struct xt_counters { + __u64 pcnt, bcnt; /* Packet and byte counters */ +}; + +/* The argument to IPT_SO_ADD_COUNTERS. */ +struct xt_counters_info { + /* Which table. */ + char name[XT_TABLE_MAXNAMELEN]; + + unsigned int num_counters; + + /* The counters (actually `number' of these). */ + struct xt_counters counters[0]; +}; + +#define XT_INV_PROTO 0x40 /* Invert the sense of PROTO. */ + +/* fn returns 0 to continue iteration */ +#define XT_MATCH_ITERATE(type, e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct xt_entry_match *__m; \ + \ + for (__i = sizeof(type); \ + __i < (e)->target_offset; \ + __i += __m->u.match_size) { \ + __m = (void *)e + __i; \ + \ + __ret = fn(__m , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* fn returns 0 to continue iteration */ +#define XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \ +({ \ + unsigned int __i, __n; \ + int __ret = 0; \ + type *__entry; \ + \ + for (__i = 0, __n = 0; __i < (size); \ + __i += __entry->next_offset, __n++) { \ + __entry = (void *)(entries) + __i; \ + if (__n < n) \ + continue; \ + \ + __ret = fn(__entry , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* fn returns 0 to continue iteration */ +#define XT_ENTRY_ITERATE(type, entries, size, fn, args...) \ + XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args) + + +/* pos is normally a struct ipt_entry/ip6t_entry/etc. */ +#define xt_entry_foreach(pos, ehead, esize) \ + for ((pos) = (typeof(pos))(ehead); \ + (pos) < (typeof(pos))((char *)(ehead) + (esize)); \ + (pos) = (typeof(pos))((char *)(pos) + (pos)->next_offset)) + +/* can only be xt_entry_match, so no use of typeof here */ +#define xt_ematch_foreach(pos, entry) \ + for ((pos) = (struct xt_entry_match *)entry->elems; \ + (pos) < (struct xt_entry_match *)((char *)(entry) + \ + (entry)->target_offset); \ + (pos) = (struct xt_entry_match *)((char *)(pos) + \ + (pos)->u.match_size)) + + +#endif /* _X_TABLES_H */ diff --git a/include/uapi/linux/netfilter/xt_set.h b/include/uapi/linux/netfilter/xt_set.h new file mode 100644 index 0000000..8c1ca66 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_set.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _XT_SET_H +#define _XT_SET_H + +#include +#include + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + +/* + * Option flags for kernel operations (xt_set_info_v0) + */ +#define IPSET_SRC 0x01 /* Source match/add */ +#define IPSET_DST 0x02 /* Destination match/add */ +#define IPSET_MATCH_INV 0x04 /* Inverse matching */ + +struct xt_set_info_v0 { + ip_set_id_t index; + union { + __u32 flags[IPSET_DIM_MAX + 1]; + struct { + __u32 __flags[IPSET_DIM_MAX]; + __u8 dim; + __u8 flags; + } compat; + } u; +}; + +/* match and target infos */ +struct xt_set_info_match_v0 { + struct xt_set_info_v0 match_set; +}; + +struct xt_set_info_target_v0 { + struct xt_set_info_v0 add_set; + struct xt_set_info_v0 del_set; +}; + +/* Revision 1 match and target */ + +struct xt_set_info { + ip_set_id_t index; + __u8 dim; + __u8 flags; +}; + +/* match and target infos */ +struct xt_set_info_match_v1 { + struct xt_set_info match_set; +}; + +struct xt_set_info_target_v1 { + struct xt_set_info add_set; + struct xt_set_info del_set; +}; + +/* Revision 2 target */ + +struct xt_set_info_target_v2 { + struct xt_set_info add_set; + struct xt_set_info del_set; + __u32 flags; + __u32 timeout; +}; + +/* Revision 3 match */ + +struct xt_set_info_match_v3 { + struct xt_set_info match_set; + struct ip_set_counter_match0 packets; + struct ip_set_counter_match0 bytes; + __u32 flags; +}; + +/* Revision 3 target */ + +struct xt_set_info_target_v3 { + struct xt_set_info add_set; + struct xt_set_info del_set; + struct xt_set_info map_set; + __u32 flags; + __u32 timeout; +}; + +/* Revision 4 match */ + +struct xt_set_info_match_v4 { + struct xt_set_info match_set; + struct ip_set_counter_match packets; + struct ip_set_counter_match bytes; + __u32 flags; +}; + +#endif /*_XT_SET_H*/ diff --git a/include/uapi/linux/netfilter/xt_tcpudp.h b/include/uapi/linux/netfilter/xt_tcpudp.h new file mode 100644 index 0000000..658c169 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_tcpudp.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _XT_TCPUDP_H +#define _XT_TCPUDP_H + +#include + +/* TCP matching stuff */ +struct xt_tcp { + __u16 spts[2]; /* Source port range. */ + __u16 dpts[2]; /* Destination port range. */ + __u8 option; /* TCP Option iff non-zero*/ + __u8 flg_mask; /* TCP flags mask byte */ + __u8 flg_cmp; /* TCP flags compare byte */ + __u8 invflags; /* Inverse flags */ +}; + +/* Values for "inv" field in struct ipt_tcp. */ +#define XT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define XT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define XT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */ +#define XT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */ +#define XT_TCP_INV_MASK 0x0F /* All possible flags. */ + +/* UDP matching stuff */ +struct xt_udp { + __u16 spts[2]; /* Source port range. */ + __u16 dpts[2]; /* Destination port range. */ + __u8 invflags; /* Inverse flags */ +}; + +/* Values for "invflags" field in struct ipt_udp. */ +#define XT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define XT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define XT_UDP_INV_MASK 0x03 /* All possible flags. */ + + +#endif diff --git a/include/uapi/linux/netfilter_ipv4.h b/include/uapi/linux/netfilter_ipv4.h new file mode 100644 index 0000000..96979e3 --- /dev/null +++ b/include/uapi/linux/netfilter_ipv4.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* IPv4-specific defines for netfilter. + * (C)1998 Rusty Russell -- This code is GPL. + */ +#ifndef __LINUX_IP_NETFILTER_H +#define __LINUX_IP_NETFILTER_H + + +#include + +/* only for userspace compatibility */ + +#include /* for INT_MIN, INT_MAX */ + +/* IP Hooks */ +/* After promisc drops, checksum checks. */ +#define NF_IP_PRE_ROUTING 0 +/* If the packet is destined for this box. */ +#define NF_IP_LOCAL_IN 1 +/* If the packet is destined for another interface. */ +#define NF_IP_FORWARD 2 +/* Packets coming from a local process. */ +#define NF_IP_LOCAL_OUT 3 +/* Packets about to hit the wire. */ +#define NF_IP_POST_ROUTING 4 +#define NF_IP_NUMHOOKS 5 + +enum nf_ip_hook_priorities { + NF_IP_PRI_FIRST = INT_MIN, + NF_IP_PRI_RAW_BEFORE_DEFRAG = -450, + NF_IP_PRI_CONNTRACK_DEFRAG = -400, + NF_IP_PRI_RAW = -300, + NF_IP_PRI_SELINUX_FIRST = -225, + NF_IP_PRI_CONNTRACK = -200, + NF_IP_PRI_MANGLE = -150, + NF_IP_PRI_NAT_DST = -100, + NF_IP_PRI_FILTER = 0, + NF_IP_PRI_SECURITY = 50, + NF_IP_PRI_NAT_SRC = 100, + NF_IP_PRI_SELINUX_LAST = 225, + NF_IP_PRI_CONNTRACK_HELPER = 300, + NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, + NF_IP_PRI_LAST = INT_MAX, +}; + +/* Arguments for setsockopt SOL_IP: */ +/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */ +/* 2.2 firewalling (+ masq) went from 64 through 76 */ +/* 2.4 firewalling went 64 through 67. */ +#define SO_ORIGINAL_DST 80 + + +#endif /* __LINUX_IP_NETFILTER_H */ diff --git a/include/uapi/linux/netfilter_ipv4/ip_tables.h b/include/uapi/linux/netfilter_ipv4/ip_tables.h new file mode 100644 index 0000000..e1ce5b3 --- /dev/null +++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * 25-Jul-1998 Major changes to allow for ip chain table + * + * 3-Jan-2000 Named tables to allow packet selection for different uses. + */ + +/* + * Format of an IP firewall descriptor + * + * src, dst, src_mask, dst_mask are always stored in network byte order. + * flags are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + */ + +#ifndef _IPTABLES_H +#define _IPTABLES_H + +#include + +#include +#include + +#include + +#define IPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN +#define IPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN +#define ipt_match xt_match +#define ipt_target xt_target +#define ipt_table xt_table +#define ipt_get_revision xt_get_revision +#define ipt_entry_match xt_entry_match +#define ipt_entry_target xt_entry_target +#define ipt_standard_target xt_standard_target +#define ipt_error_target xt_error_target +#define ipt_counters xt_counters +#define IPT_CONTINUE XT_CONTINUE +#define IPT_RETURN XT_RETURN + +/* This group is older than old (iptables < v1.4.0-rc1~89) */ +#include +#define ipt_udp xt_udp +#define ipt_tcp xt_tcp +#define IPT_TCP_INV_SRCPT XT_TCP_INV_SRCPT +#define IPT_TCP_INV_DSTPT XT_TCP_INV_DSTPT +#define IPT_TCP_INV_FLAGS XT_TCP_INV_FLAGS +#define IPT_TCP_INV_OPTION XT_TCP_INV_OPTION +#define IPT_TCP_INV_MASK XT_TCP_INV_MASK +#define IPT_UDP_INV_SRCPT XT_UDP_INV_SRCPT +#define IPT_UDP_INV_DSTPT XT_UDP_INV_DSTPT +#define IPT_UDP_INV_MASK XT_UDP_INV_MASK + +/* The argument to IPT_SO_ADD_COUNTERS. */ +#define ipt_counters_info xt_counters_info +/* Standard return verdict, or do jump. */ +#define IPT_STANDARD_TARGET XT_STANDARD_TARGET +/* Error verdict. */ +#define IPT_ERROR_TARGET XT_ERROR_TARGET + +/* fn returns 0 to continue iteration */ +#define IPT_MATCH_ITERATE(e, fn, args...) \ + XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args) + +/* fn returns 0 to continue iteration */ +#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \ + XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args) + +/* Yes, Virginia, you have to zero the padding. */ +struct ipt_ip { + /* Source and destination IP addr */ + struct in_addr src, dst; + /* Mask for src and dest IP addr */ + struct in_addr smsk, dmsk; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* Protocol, 0 = ANY */ + __u16 proto; + + /* Flags word */ + __u8 flags; + /* Inverse flags */ + __u8 invflags; +}; + +/* Values for "flag" field in struct ipt_ip (general ip structure). */ +#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ +#define IPT_F_GOTO 0x02 /* Set if jump is a goto */ +#define IPT_F_MASK 0x03 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct ipt_ip. */ +#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +#define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ +#define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */ +#define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ +#define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ +#define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */ +#define IPT_INV_PROTO XT_INV_PROTO +#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general IP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct ipt_entry { + struct ipt_ip ip; + + /* Mark with fields that we care about. */ + unsigned int nfcache; + + /* Size of ipt_entry + matches */ + __u16 target_offset; + /* Size of ipt_entry + matches + target */ + __u16 next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct xt_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use a raw + * socket for this. Instead we check rights in the calls. + * + * ATTENTION: check linux/in.h before adding new number here. + */ +#define IPT_BASE_CTL 64 + +#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) +#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1) +#define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS + +#define IPT_SO_GET_INFO (IPT_BASE_CTL) +#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) +#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2) +#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3) +#define IPT_SO_GET_MAX IPT_SO_GET_REVISION_TARGET + +/* ICMP matching stuff */ +struct ipt_icmp { + __u8 type; /* type to match */ + __u8 code[2]; /* range of code */ + __u8 invflags; /* Inverse flags */ +}; + +/* Values for "inv" field for struct ipt_icmp. */ +#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */ + +/* The argument to IPT_SO_GET_INFO */ +struct ipt_getinfo { + /* Which table: caller fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_INET_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_INET_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to IPT_SO_SET_REPLACE. */ +struct ipt_replace { + /* Which table. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_INET_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_INET_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + /* The old entries' counters. */ + struct xt_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct ipt_entry entries[0]; +}; + +/* The argument to IPT_SO_GET_ENTRIES. */ +struct ipt_get_entries { + /* Which table: user fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + struct ipt_entry entrytable[0]; +}; + +/* Helper functions */ +static __inline__ struct xt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (struct xt_entry_target *)((char *)e + e->target_offset); +} + +/* + * Main firewall chains definitions and global var's definitions. + */ +#endif /* _IPTABLES_H */ diff --git a/include/uapi/linux/netfilter_ipv6.h b/include/uapi/linux/netfilter_ipv6.h new file mode 100644 index 0000000..eedf7a2 --- /dev/null +++ b/include/uapi/linux/netfilter_ipv6.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* IPv6-specific defines for netfilter. + * (C)1998 Rusty Russell -- This code is GPL. + * (C)1999 David Jeffery + * this header was blatantly ripped from netfilter_ipv4.h + * it's amazing what adding a bunch of 6s can do =8^) + */ +#ifndef __LINUX_IP6_NETFILTER_H +#define __LINUX_IP6_NETFILTER_H + + +#include + +/* only for userspace compatibility */ + +#include /* for INT_MIN, INT_MAX */ + +/* IP6 Hooks */ +/* After promisc drops, checksum checks. */ +#define NF_IP6_PRE_ROUTING 0 +/* If the packet is destined for this box. */ +#define NF_IP6_LOCAL_IN 1 +/* If the packet is destined for another interface. */ +#define NF_IP6_FORWARD 2 +/* Packets coming from a local process. */ +#define NF_IP6_LOCAL_OUT 3 +/* Packets about to hit the wire. */ +#define NF_IP6_POST_ROUTING 4 +#define NF_IP6_NUMHOOKS 5 + + +enum nf_ip6_hook_priorities { + NF_IP6_PRI_FIRST = INT_MIN, + NF_IP6_PRI_RAW_BEFORE_DEFRAG = -450, + NF_IP6_PRI_CONNTRACK_DEFRAG = -400, + NF_IP6_PRI_RAW = -300, + NF_IP6_PRI_SELINUX_FIRST = -225, + NF_IP6_PRI_CONNTRACK = -200, + NF_IP6_PRI_MANGLE = -150, + NF_IP6_PRI_NAT_DST = -100, + NF_IP6_PRI_FILTER = 0, + NF_IP6_PRI_SECURITY = 50, + NF_IP6_PRI_NAT_SRC = 100, + NF_IP6_PRI_SELINUX_LAST = 225, + NF_IP6_PRI_CONNTRACK_HELPER = 300, + NF_IP6_PRI_LAST = INT_MAX, +}; + + +#endif /* __LINUX_IP6_NETFILTER_H */ diff --git a/include/uapi/linux/netfilter_ipv6/ip6_tables.h b/include/uapi/linux/netfilter_ipv6/ip6_tables.h new file mode 100644 index 0000000..7bbead8 --- /dev/null +++ b/include/uapi/linux/netfilter_ipv6/ip6_tables.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * 25-Jul-1998 Major changes to allow for ip chain table + * + * 3-Jan-2000 Named tables to allow packet selection for different uses. + */ + +/* + * Format of an IP6 firewall descriptor + * + * src, dst, src_mask, dst_mask are always stored in network byte order. + * flags are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + */ + +#ifndef _IP6_TABLES_H +#define _IP6_TABLES_H + +#include + +#include +#include + +#include + +#define IP6T_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN +#define IP6T_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN +#define ip6t_match xt_match +#define ip6t_target xt_target +#define ip6t_table xt_table +#define ip6t_get_revision xt_get_revision +#define ip6t_entry_match xt_entry_match +#define ip6t_entry_target xt_entry_target +#define ip6t_standard_target xt_standard_target +#define ip6t_error_target xt_error_target +#define ip6t_counters xt_counters +#define IP6T_CONTINUE XT_CONTINUE +#define IP6T_RETURN XT_RETURN + +/* Pre-iptables-1.4.0 */ +#include +#define ip6t_tcp xt_tcp +#define ip6t_udp xt_udp +#define IP6T_TCP_INV_SRCPT XT_TCP_INV_SRCPT +#define IP6T_TCP_INV_DSTPT XT_TCP_INV_DSTPT +#define IP6T_TCP_INV_FLAGS XT_TCP_INV_FLAGS +#define IP6T_TCP_INV_OPTION XT_TCP_INV_OPTION +#define IP6T_TCP_INV_MASK XT_TCP_INV_MASK +#define IP6T_UDP_INV_SRCPT XT_UDP_INV_SRCPT +#define IP6T_UDP_INV_DSTPT XT_UDP_INV_DSTPT +#define IP6T_UDP_INV_MASK XT_UDP_INV_MASK + +#define ip6t_counters_info xt_counters_info +#define IP6T_STANDARD_TARGET XT_STANDARD_TARGET +#define IP6T_ERROR_TARGET XT_ERROR_TARGET +#define IP6T_MATCH_ITERATE(e, fn, args...) \ + XT_MATCH_ITERATE(struct ip6t_entry, e, fn, ## args) +#define IP6T_ENTRY_ITERATE(entries, size, fn, args...) \ + XT_ENTRY_ITERATE(struct ip6t_entry, entries, size, fn, ## args) + +/* Yes, Virginia, you have to zero the padding. */ +struct ip6t_ip6 { + /* Source and destination IP6 addr */ + struct in6_addr src, dst; + /* Mask for src and dest IP6 addr */ + struct in6_addr smsk, dmsk; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* Upper protocol number + * - The allowed value is 0 (any) or protocol number of last parsable + * header, which is 50 (ESP), 59 (No Next Header), 135 (MH), or + * the non IPv6 extension headers. + * - The protocol numbers of IPv6 extension headers except of ESP and + * MH do not match any packets. + * - You also need to set IP6T_FLAGS_PROTO to "flags" to check protocol. + */ + __u16 proto; + /* TOS to match iff flags & IP6T_F_TOS */ + __u8 tos; + + /* Flags word */ + __u8 flags; + /* Inverse flags */ + __u8 invflags; +}; + +/* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */ +#define IP6T_F_PROTO 0x01 /* Set if rule cares about upper + protocols */ +#define IP6T_F_TOS 0x02 /* Match the TOS. */ +#define IP6T_F_GOTO 0x04 /* Set if jump is a goto */ +#define IP6T_F_MASK 0x07 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct ip6t_ip6. */ +#define IP6T_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +#define IP6T_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ +#define IP6T_INV_TOS 0x04 /* Invert the sense of TOS. */ +#define IP6T_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ +#define IP6T_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ +#define IP6T_INV_FRAG 0x20 /* Invert the sense of FRAG. */ +#define IP6T_INV_PROTO XT_INV_PROTO +#define IP6T_INV_MASK 0x7F /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general IP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct ip6t_entry { + struct ip6t_ip6 ipv6; + + /* Mark with fields that we care about. */ + unsigned int nfcache; + + /* Size of ipt_entry + matches */ + __u16 target_offset; + /* Size of ipt_entry + matches + target */ + __u16 next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct xt_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* Standard entry */ +struct ip6t_standard { + struct ip6t_entry entry; + struct xt_standard_target target; +}; + +struct ip6t_error { + struct ip6t_entry entry; + struct xt_error_target target; +}; + +#define IP6T_ENTRY_INIT(__size) \ +{ \ + .target_offset = sizeof(struct ip6t_entry), \ + .next_offset = (__size), \ +} + +#define IP6T_STANDARD_INIT(__verdict) \ +{ \ + .entry = IP6T_ENTRY_INIT(sizeof(struct ip6t_standard)), \ + .target = XT_TARGET_INIT(XT_STANDARD_TARGET, \ + sizeof(struct xt_standard_target)), \ + .target.verdict = -(__verdict) - 1, \ +} + +#define IP6T_ERROR_INIT \ +{ \ + .entry = IP6T_ENTRY_INIT(sizeof(struct ip6t_error)), \ + .target = XT_TARGET_INIT(XT_ERROR_TARGET, \ + sizeof(struct xt_error_target)), \ + .target.errorname = "ERROR", \ +} + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use + * a raw socket for this. Instead we check rights in the calls. + * + * ATTENTION: check linux/in6.h before adding new number here. + */ +#define IP6T_BASE_CTL 64 + +#define IP6T_SO_SET_REPLACE (IP6T_BASE_CTL) +#define IP6T_SO_SET_ADD_COUNTERS (IP6T_BASE_CTL + 1) +#define IP6T_SO_SET_MAX IP6T_SO_SET_ADD_COUNTERS + +#define IP6T_SO_GET_INFO (IP6T_BASE_CTL) +#define IP6T_SO_GET_ENTRIES (IP6T_BASE_CTL + 1) +#define IP6T_SO_GET_REVISION_MATCH (IP6T_BASE_CTL + 4) +#define IP6T_SO_GET_REVISION_TARGET (IP6T_BASE_CTL + 5) +#define IP6T_SO_GET_MAX IP6T_SO_GET_REVISION_TARGET + +/* obtain original address if REDIRECT'd connection */ +#define IP6T_SO_ORIGINAL_DST 80 + +/* ICMP matching stuff */ +struct ip6t_icmp { + __u8 type; /* type to match */ + __u8 code[2]; /* range of code */ + __u8 invflags; /* Inverse flags */ +}; + +/* Values for "inv" field for struct ipt_icmp. */ +#define IP6T_ICMP_INV 0x01 /* Invert the sense of type/code test */ + +/* The argument to IP6T_SO_GET_INFO */ +struct ip6t_getinfo { + /* Which table: caller fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_INET_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_INET_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to IP6T_SO_SET_REPLACE. */ +struct ip6t_replace { + /* Which table. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_INET_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_INET_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + /* The old entries' counters. */ + struct xt_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct ip6t_entry entries[0]; +}; + +/* The argument to IP6T_SO_GET_ENTRIES. */ +struct ip6t_get_entries { + /* Which table: user fills this in. */ + char name[XT_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + struct ip6t_entry entrytable[0]; +}; + +/* Helper functions */ +static __inline__ struct xt_entry_target * +ip6t_get_target(struct ip6t_entry *e) +{ + return (struct xt_entry_target *)((char *)e + e->target_offset); +} + +/* + * Main firewall chains definitions and global var's definitions. + */ + +#endif /* _IP6_TABLES_H */ diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h new file mode 100644 index 0000000..695c88e --- /dev/null +++ b/include/uapi/linux/netlink.h @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include +#include /* for __kernel_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 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* 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 NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ +#define NETLINK_SMC 22 /* SMC monitoring */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_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 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Echo this request */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ + +/* 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 */ + +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + +/* + 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 4U +#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_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; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 +#define NETLINK_GET_STRICT_CHK 12 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) + +#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))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; + +/* + * policy descriptions - it's specific to each family how this is used + * Normally, it should be retrieved via a dump inside another attribute + * specifying where it applies. + */ + +/** + * enum netlink_attribute_type - type of an attribute + * @NL_ATTR_TYPE_INVALID: unused + * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present) + * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute + * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute + * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute + * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute + * @NL_ATTR_TYPE_S8: 8-bit signed attribute + * @NL_ATTR_TYPE_S16: 16-bit signed attribute + * @NL_ATTR_TYPE_S32: 32-bit signed attribute + * @NL_ATTR_TYPE_S64: 64-bit signed attribute + * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified + * @NL_ATTR_TYPE_STRING: string, min/max length may be specified + * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string, + * min/max length may be specified + * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute + * consists of sub-attributes. The nested policy and maxtype + * inside may be specified. + * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this + * attribute contains sub-attributes whose type is irrelevant + * (just used to separate the array entries) and each such array + * entry has attributes again, the policy for those inner ones + * and the corresponding maxtype may be specified. + * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute + */ +enum netlink_attribute_type { + NL_ATTR_TYPE_INVALID, + + NL_ATTR_TYPE_FLAG, + + NL_ATTR_TYPE_U8, + NL_ATTR_TYPE_U16, + NL_ATTR_TYPE_U32, + NL_ATTR_TYPE_U64, + + NL_ATTR_TYPE_S8, + NL_ATTR_TYPE_S16, + NL_ATTR_TYPE_S32, + NL_ATTR_TYPE_S64, + + NL_ATTR_TYPE_BINARY, + NL_ATTR_TYPE_STRING, + NL_ATTR_TYPE_NUL_STRING, + + NL_ATTR_TYPE_NESTED, + NL_ATTR_TYPE_NESTED_ARRAY, + + NL_ATTR_TYPE_BITFIELD32, +}; + +/** + * enum netlink_policy_type_attr - policy type attributes + * @NL_POLICY_TYPE_ATTR_UNSPEC: unused + * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute, + * &enum netlink_attribute_type (U32) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed + * integers (S64) + * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned + * integers (U64) + * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary + * attributes, no minimum if not given (U32) + * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary + * attributes, no maximum if not given (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and + * nested array types (U32) + * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy + * attribute for nested and nested array types, this can + * in theory be < the size of the policy pointed to by + * the index, if limited inside the nesting (U32) + * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the + * bitfield32 type (U32) + * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment + */ +enum netlink_policy_type_attr { + NL_POLICY_TYPE_ATTR_UNSPEC, + NL_POLICY_TYPE_ATTR_TYPE, + NL_POLICY_TYPE_ATTR_MIN_VALUE_S, + NL_POLICY_TYPE_ATTR_MAX_VALUE_S, + NL_POLICY_TYPE_ATTR_MIN_VALUE_U, + NL_POLICY_TYPE_ATTR_MAX_VALUE_U, + NL_POLICY_TYPE_ATTR_MIN_LENGTH, + NL_POLICY_TYPE_ATTR_MAX_LENGTH, + NL_POLICY_TYPE_ATTR_POLICY_IDX, + NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, + NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, + NL_POLICY_TYPE_ATTR_PAD, + + /* keep last */ + __NL_POLICY_TYPE_ATTR_MAX, + NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1 +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h new file mode 100644 index 0000000..4cd0657 --- /dev/null +++ b/include/uapi/linux/netlink_diag.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __NETLINK_DIAG_H__ +#define __NETLINK_DIAG_H__ + +#include + +struct netlink_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 ndiag_ino; + __u32 ndiag_show; + __u32 ndiag_cookie[2]; +}; + +struct netlink_diag_msg { + __u8 ndiag_family; + __u8 ndiag_type; + __u8 ndiag_protocol; + __u8 ndiag_state; + + __u32 ndiag_portid; + __u32 ndiag_dst_portid; + __u32 ndiag_dst_group; + __u32 ndiag_ino; + __u32 ndiag_cookie[2]; +}; + +struct netlink_diag_ring { + __u32 ndr_block_size; + __u32 ndr_block_nr; + __u32 ndr_frame_size; + __u32 ndr_frame_nr; +}; + +enum { + /* NETLINK_DIAG_NONE, standard nl API requires this attribute! */ + NETLINK_DIAG_MEMINFO, + NETLINK_DIAG_GROUPS, + NETLINK_DIAG_RX_RING, + NETLINK_DIAG_TX_RING, + NETLINK_DIAG_FLAGS, + + __NETLINK_DIAG_MAX, +}; + +#define NETLINK_DIAG_MAX (__NETLINK_DIAG_MAX - 1) + +#define NDIAG_PROTO_ALL ((__u8) ~0) + +#define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */ +#define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */ +/* deprecated since 4.6 */ +#define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */ +#define NDIAG_SHOW_FLAGS 0x00000008 /* show flags of a netlink socket */ + +/* flags */ +#define NDIAG_FLAG_CB_RUNNING 0x00000001 +#define NDIAG_FLAG_PKTINFO 0x00000002 +#define NDIAG_FLAG_BROADCAST_ERROR 0x00000004 +#define NDIAG_FLAG_NO_ENOBUFS 0x00000008 +#define NDIAG_FLAG_LISTEN_ALL_NSID 0x00000010 +#define NDIAG_FLAG_CAP_ACK 0x00000020 + +#endif diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h new file mode 100644 index 0000000..b0a5613 --- /dev/null +++ b/include/uapi/linux/nexthop.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NEXTHOP_H +#define _LINUX_NEXTHOP_H + +#include + +struct nhmsg { + unsigned char nh_family; + unsigned char nh_scope; /* return only */ + unsigned char nh_protocol; /* Routing protocol that installed nh */ + unsigned char resvd; + unsigned int nh_flags; /* RTNH_F flags */ +}; + +/* entry in a nexthop group */ +struct nexthop_grp { + __u32 id; /* nexthop id - must exist */ + __u8 weight; /* weight of this nexthop */ + __u8 resvd1; + __u16 resvd2; +}; + +enum { + NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */ + __NEXTHOP_GRP_TYPE_MAX, +}; + +#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) + +enum { + NHA_UNSPEC, + NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ + + NHA_GROUP, /* array of nexthop_grp */ + NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */ + /* if NHA_GROUP attribute is added, no other attributes can be set */ + + NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */ + /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */ + + NHA_OIF, /* u32; nexthop device */ + NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */ + NHA_ENCAP_TYPE, /* u16; lwt encap type */ + NHA_ENCAP, /* lwt encap data */ + + /* NHA_OIF can be appended to dump request to return only + * nexthops using given device + */ + NHA_GROUPS, /* flag; only return nexthop groups in dump */ + NHA_MASTER, /* u32; only return nexthops with given master dev */ + + NHA_FDB, /* flag; nexthop belongs to a bridge fdb */ + /* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */ + + __NHA_MAX, +}; + +#define NHA_MAX (__NHA_MAX - 1) +#endif diff --git a/include/uapi/linux/packet_diag.h b/include/uapi/linux/packet_diag.h new file mode 100644 index 0000000..349ddf0 --- /dev/null +++ b/include/uapi/linux/packet_diag.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __PACKET_DIAG_H__ +#define __PACKET_DIAG_H__ + +#include + +struct packet_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 pdiag_ino; + __u32 pdiag_show; + __u32 pdiag_cookie[2]; +}; + +#define PACKET_SHOW_INFO 0x00000001 /* Basic packet_sk information */ +#define PACKET_SHOW_MCLIST 0x00000002 /* A set of packet_diag_mclist-s */ +#define PACKET_SHOW_RING_CFG 0x00000004 /* Rings configuration parameters */ +#define PACKET_SHOW_FANOUT 0x00000008 +#define PACKET_SHOW_MEMINFO 0x00000010 +#define PACKET_SHOW_FILTER 0x00000020 + +struct packet_diag_msg { + __u8 pdiag_family; + __u8 pdiag_type; + __u16 pdiag_num; + + __u32 pdiag_ino; + __u32 pdiag_cookie[2]; +}; + +enum { + /* PACKET_DIAG_NONE, standard nl API requires this attribute! */ + PACKET_DIAG_INFO, + PACKET_DIAG_MCLIST, + PACKET_DIAG_RX_RING, + PACKET_DIAG_TX_RING, + PACKET_DIAG_FANOUT, + PACKET_DIAG_UID, + PACKET_DIAG_MEMINFO, + PACKET_DIAG_FILTER, + + __PACKET_DIAG_MAX, +}; + +#define PACKET_DIAG_MAX (__PACKET_DIAG_MAX - 1) + +struct packet_diag_info { + __u32 pdi_index; + __u32 pdi_version; + __u32 pdi_reserve; + __u32 pdi_copy_thresh; + __u32 pdi_tstamp; + __u32 pdi_flags; + +#define PDI_RUNNING 0x1 +#define PDI_AUXDATA 0x2 +#define PDI_ORIGDEV 0x4 +#define PDI_VNETHDR 0x8 +#define PDI_LOSS 0x10 +}; + +struct packet_diag_mclist { + __u32 pdmc_index; + __u32 pdmc_count; + __u16 pdmc_type; + __u16 pdmc_alen; + __u8 pdmc_addr[32]; /* MAX_ADDR_LEN */ +}; + +struct packet_diag_ring { + __u32 pdr_block_size; + __u32 pdr_block_nr; + __u32 pdr_frame_size; + __u32 pdr_frame_nr; + __u32 pdr_retire_tmo; + __u32 pdr_sizeof_priv; + __u32 pdr_features; +}; + +#endif diff --git a/include/uapi/linux/param.h b/include/uapi/linux/param.h new file mode 100644 index 0000000..94e0c57 --- /dev/null +++ b/include/uapi/linux/param.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_PARAM_H +#define _LINUX_PARAM_H + +#include + +#endif diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h new file mode 100644 index 0000000..d65b117 --- /dev/null +++ b/include/uapi/linux/pfkeyv2.h @@ -0,0 +1,384 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* PF_KEY user interface, this is defined by rfc2367 so + * do not make arbitrary modifications or else this header + * file will not be compliant. + */ + +#ifndef _LINUX_PFKEY2_H +#define _LINUX_PFKEY2_H + +#include + +#define PF_KEY_V2 2 +#define PFKEYV2_REVISION 199806L + +struct sadb_msg { + __u8 sadb_msg_version; + __u8 sadb_msg_type; + __u8 sadb_msg_errno; + __u8 sadb_msg_satype; + __u16 sadb_msg_len; + __u16 sadb_msg_reserved; + __u32 sadb_msg_seq; + __u32 sadb_msg_pid; +} __attribute__((packed)); +/* sizeof(struct sadb_msg) == 16 */ + +struct sadb_ext { + __u16 sadb_ext_len; + __u16 sadb_ext_type; +} __attribute__((packed)); +/* sizeof(struct sadb_ext) == 4 */ + +struct sadb_sa { + __u16 sadb_sa_len; + __u16 sadb_sa_exttype; + __be32 sadb_sa_spi; + __u8 sadb_sa_replay; + __u8 sadb_sa_state; + __u8 sadb_sa_auth; + __u8 sadb_sa_encrypt; + __u32 sadb_sa_flags; +} __attribute__((packed)); +/* sizeof(struct sadb_sa) == 16 */ + +struct sadb_lifetime { + __u16 sadb_lifetime_len; + __u16 sadb_lifetime_exttype; + __u32 sadb_lifetime_allocations; + __u64 sadb_lifetime_bytes; + __u64 sadb_lifetime_addtime; + __u64 sadb_lifetime_usetime; +} __attribute__((packed)); +/* sizeof(struct sadb_lifetime) == 32 */ + +struct sadb_address { + __u16 sadb_address_len; + __u16 sadb_address_exttype; + __u8 sadb_address_proto; + __u8 sadb_address_prefixlen; + __u16 sadb_address_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_address) == 8 */ + +struct sadb_key { + __u16 sadb_key_len; + __u16 sadb_key_exttype; + __u16 sadb_key_bits; + __u16 sadb_key_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_key) == 8 */ + +struct sadb_ident { + __u16 sadb_ident_len; + __u16 sadb_ident_exttype; + __u16 sadb_ident_type; + __u16 sadb_ident_reserved; + __u64 sadb_ident_id; +} __attribute__((packed)); +/* sizeof(struct sadb_ident) == 16 */ + +struct sadb_sens { + __u16 sadb_sens_len; + __u16 sadb_sens_exttype; + __u32 sadb_sens_dpd; + __u8 sadb_sens_sens_level; + __u8 sadb_sens_sens_len; + __u8 sadb_sens_integ_level; + __u8 sadb_sens_integ_len; + __u32 sadb_sens_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_sens) == 16 */ + +/* followed by: + __u64 sadb_sens_bitmap[sens_len]; + __u64 sadb_integ_bitmap[integ_len]; */ + +struct sadb_prop { + __u16 sadb_prop_len; + __u16 sadb_prop_exttype; + __u8 sadb_prop_replay; + __u8 sadb_prop_reserved[3]; +} __attribute__((packed)); +/* sizeof(struct sadb_prop) == 8 */ + +/* followed by: + struct sadb_comb sadb_combs[(sadb_prop_len + + sizeof(__u64) - sizeof(struct sadb_prop)) / + sizeof(struct sadb_comb)]; */ + +struct sadb_comb { + __u8 sadb_comb_auth; + __u8 sadb_comb_encrypt; + __u16 sadb_comb_flags; + __u16 sadb_comb_auth_minbits; + __u16 sadb_comb_auth_maxbits; + __u16 sadb_comb_encrypt_minbits; + __u16 sadb_comb_encrypt_maxbits; + __u32 sadb_comb_reserved; + __u32 sadb_comb_soft_allocations; + __u32 sadb_comb_hard_allocations; + __u64 sadb_comb_soft_bytes; + __u64 sadb_comb_hard_bytes; + __u64 sadb_comb_soft_addtime; + __u64 sadb_comb_hard_addtime; + __u64 sadb_comb_soft_usetime; + __u64 sadb_comb_hard_usetime; +} __attribute__((packed)); +/* sizeof(struct sadb_comb) == 72 */ + +struct sadb_supported { + __u16 sadb_supported_len; + __u16 sadb_supported_exttype; + __u32 sadb_supported_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_supported) == 8 */ + +/* followed by: + struct sadb_alg sadb_algs[(sadb_supported_len + + sizeof(__u64) - sizeof(struct sadb_supported)) / + sizeof(struct sadb_alg)]; */ + +struct sadb_alg { + __u8 sadb_alg_id; + __u8 sadb_alg_ivlen; + __u16 sadb_alg_minbits; + __u16 sadb_alg_maxbits; + __u16 sadb_alg_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_alg) == 8 */ + +struct sadb_spirange { + __u16 sadb_spirange_len; + __u16 sadb_spirange_exttype; + __u32 sadb_spirange_min; + __u32 sadb_spirange_max; + __u32 sadb_spirange_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_spirange) == 16 */ + +struct sadb_x_kmprivate { + __u16 sadb_x_kmprivate_len; + __u16 sadb_x_kmprivate_exttype; + __u32 sadb_x_kmprivate_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_kmprivate) == 8 */ + +struct sadb_x_sa2 { + __u16 sadb_x_sa2_len; + __u16 sadb_x_sa2_exttype; + __u8 sadb_x_sa2_mode; + __u8 sadb_x_sa2_reserved1; + __u16 sadb_x_sa2_reserved2; + __u32 sadb_x_sa2_sequence; + __u32 sadb_x_sa2_reqid; +} __attribute__((packed)); +/* sizeof(struct sadb_x_sa2) == 16 */ + +struct sadb_x_policy { + __u16 sadb_x_policy_len; + __u16 sadb_x_policy_exttype; + __u16 sadb_x_policy_type; + __u8 sadb_x_policy_dir; + __u8 sadb_x_policy_reserved; + __u32 sadb_x_policy_id; + __u32 sadb_x_policy_priority; +} __attribute__((packed)); +/* sizeof(struct sadb_x_policy) == 16 */ + +struct sadb_x_ipsecrequest { + __u16 sadb_x_ipsecrequest_len; + __u16 sadb_x_ipsecrequest_proto; + __u8 sadb_x_ipsecrequest_mode; + __u8 sadb_x_ipsecrequest_level; + __u16 sadb_x_ipsecrequest_reserved1; + __u32 sadb_x_ipsecrequest_reqid; + __u32 sadb_x_ipsecrequest_reserved2; +} __attribute__((packed)); +/* sizeof(struct sadb_x_ipsecrequest) == 16 */ + +/* This defines the TYPE of Nat Traversal in use. Currently only one + * type of NAT-T is supported, draft-ietf-ipsec-udp-encaps-06 + */ +struct sadb_x_nat_t_type { + __u16 sadb_x_nat_t_type_len; + __u16 sadb_x_nat_t_type_exttype; + __u8 sadb_x_nat_t_type_type; + __u8 sadb_x_nat_t_type_reserved[3]; +} __attribute__((packed)); +/* sizeof(struct sadb_x_nat_t_type) == 8 */ + +/* Pass a NAT Traversal port (Source or Dest port) */ +struct sadb_x_nat_t_port { + __u16 sadb_x_nat_t_port_len; + __u16 sadb_x_nat_t_port_exttype; + __be16 sadb_x_nat_t_port_port; + __u16 sadb_x_nat_t_port_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_nat_t_port) == 8 */ + +/* Generic LSM security context */ +struct sadb_x_sec_ctx { + __u16 sadb_x_sec_len; + __u16 sadb_x_sec_exttype; + __u8 sadb_x_ctx_alg; /* LSMs: e.g., selinux == 1 */ + __u8 sadb_x_ctx_doi; + __u16 sadb_x_ctx_len; +} __attribute__((packed)); +/* sizeof(struct sadb_sec_ctx) = 8 */ + +/* Used by MIGRATE to pass addresses IKE will use to perform + * negotiation with the peer */ +struct sadb_x_kmaddress { + __u16 sadb_x_kmaddress_len; + __u16 sadb_x_kmaddress_exttype; + __u32 sadb_x_kmaddress_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_kmaddress) == 8 */ + +/* To specify the SA dump filter */ +struct sadb_x_filter { + __u16 sadb_x_filter_len; + __u16 sadb_x_filter_exttype; + __u32 sadb_x_filter_saddr[4]; + __u32 sadb_x_filter_daddr[4]; + __u16 sadb_x_filter_family; + __u8 sadb_x_filter_splen; + __u8 sadb_x_filter_dplen; +} __attribute__((packed)); +/* sizeof(struct sadb_x_filter) == 40 */ + +/* Message types */ +#define SADB_RESERVED 0 +#define SADB_GETSPI 1 +#define SADB_UPDATE 2 +#define SADB_ADD 3 +#define SADB_DELETE 4 +#define SADB_GET 5 +#define SADB_ACQUIRE 6 +#define SADB_REGISTER 7 +#define SADB_EXPIRE 8 +#define SADB_FLUSH 9 +#define SADB_DUMP 10 +#define SADB_X_PROMISC 11 +#define SADB_X_PCHANGE 12 +#define SADB_X_SPDUPDATE 13 +#define SADB_X_SPDADD 14 +#define SADB_X_SPDDELETE 15 +#define SADB_X_SPDGET 16 +#define SADB_X_SPDACQUIRE 17 +#define SADB_X_SPDDUMP 18 +#define SADB_X_SPDFLUSH 19 +#define SADB_X_SPDSETIDX 20 +#define SADB_X_SPDEXPIRE 21 +#define SADB_X_SPDDELETE2 22 +#define SADB_X_NAT_T_NEW_MAPPING 23 +#define SADB_X_MIGRATE 24 +#define SADB_MAX 24 + +/* Security Association flags */ +#define SADB_SAFLAGS_PFS 1 +#define SADB_SAFLAGS_NOPMTUDISC 0x20000000 +#define SADB_SAFLAGS_DECAP_DSCP 0x40000000 +#define SADB_SAFLAGS_NOECN 0x80000000 + +/* Security Association states */ +#define SADB_SASTATE_LARVAL 0 +#define SADB_SASTATE_MATURE 1 +#define SADB_SASTATE_DYING 2 +#define SADB_SASTATE_DEAD 3 +#define SADB_SASTATE_MAX 3 + +/* Security Association types */ +#define SADB_SATYPE_UNSPEC 0 +#define SADB_SATYPE_AH 2 +#define SADB_SATYPE_ESP 3 +#define SADB_SATYPE_RSVP 5 +#define SADB_SATYPE_OSPFV2 6 +#define SADB_SATYPE_RIPV2 7 +#define SADB_SATYPE_MIP 8 +#define SADB_X_SATYPE_IPCOMP 9 +#define SADB_SATYPE_MAX 9 + +/* Authentication algorithms */ +#define SADB_AALG_NONE 0 +#define SADB_AALG_MD5HMAC 2 +#define SADB_AALG_SHA1HMAC 3 +#define SADB_X_AALG_SHA2_256HMAC 5 +#define SADB_X_AALG_SHA2_384HMAC 6 +#define SADB_X_AALG_SHA2_512HMAC 7 +#define SADB_X_AALG_RIPEMD160HMAC 8 +#define SADB_X_AALG_AES_XCBC_MAC 9 +#define SADB_X_AALG_NULL 251 /* kame */ +#define SADB_AALG_MAX 251 + +/* Encryption algorithms */ +#define SADB_EALG_NONE 0 +#define SADB_EALG_DESCBC 2 +#define SADB_EALG_3DESCBC 3 +#define SADB_X_EALG_CASTCBC 6 +#define SADB_X_EALG_BLOWFISHCBC 7 +#define SADB_EALG_NULL 11 +#define SADB_X_EALG_AESCBC 12 +#define SADB_X_EALG_AESCTR 13 +#define SADB_X_EALG_AES_CCM_ICV8 14 +#define SADB_X_EALG_AES_CCM_ICV12 15 +#define SADB_X_EALG_AES_CCM_ICV16 16 +#define SADB_X_EALG_AES_GCM_ICV8 18 +#define SADB_X_EALG_AES_GCM_ICV12 19 +#define SADB_X_EALG_AES_GCM_ICV16 20 +#define SADB_X_EALG_CAMELLIACBC 22 +#define SADB_X_EALG_NULL_AES_GMAC 23 +#define SADB_EALG_MAX 253 /* last EALG */ +/* private allocations should use 249-255 (RFC2407) */ +#define SADB_X_EALG_SERPENTCBC 252 /* draft-ietf-ipsec-ciph-aes-cbc-00 */ +#define SADB_X_EALG_TWOFISHCBC 253 /* draft-ietf-ipsec-ciph-aes-cbc-00 */ + +/* Compression algorithms */ +#define SADB_X_CALG_NONE 0 +#define SADB_X_CALG_OUI 1 +#define SADB_X_CALG_DEFLATE 2 +#define SADB_X_CALG_LZS 3 +#define SADB_X_CALG_LZJH 4 +#define SADB_X_CALG_MAX 4 + +/* Extension Header values */ +#define SADB_EXT_RESERVED 0 +#define SADB_EXT_SA 1 +#define SADB_EXT_LIFETIME_CURRENT 2 +#define SADB_EXT_LIFETIME_HARD 3 +#define SADB_EXT_LIFETIME_SOFT 4 +#define SADB_EXT_ADDRESS_SRC 5 +#define SADB_EXT_ADDRESS_DST 6 +#define SADB_EXT_ADDRESS_PROXY 7 +#define SADB_EXT_KEY_AUTH 8 +#define SADB_EXT_KEY_ENCRYPT 9 +#define SADB_EXT_IDENTITY_SRC 10 +#define SADB_EXT_IDENTITY_DST 11 +#define SADB_EXT_SENSITIVITY 12 +#define SADB_EXT_PROPOSAL 13 +#define SADB_EXT_SUPPORTED_AUTH 14 +#define SADB_EXT_SUPPORTED_ENCRYPT 15 +#define SADB_EXT_SPIRANGE 16 +#define SADB_X_EXT_KMPRIVATE 17 +#define SADB_X_EXT_POLICY 18 +#define SADB_X_EXT_SA2 19 +/* The next four entries are for setting up NAT Traversal */ +#define SADB_X_EXT_NAT_T_TYPE 20 +#define SADB_X_EXT_NAT_T_SPORT 21 +#define SADB_X_EXT_NAT_T_DPORT 22 +#define SADB_X_EXT_NAT_T_OA 23 +#define SADB_X_EXT_SEC_CTX 24 +/* Used with MIGRATE to pass @ to IKE for negotiation */ +#define SADB_X_EXT_KMADDRESS 25 +#define SADB_X_EXT_FILTER 26 +#define SADB_EXT_MAX 26 + +/* Identity Extension values */ +#define SADB_IDENTTYPE_RESERVED 0 +#define SADB_IDENTTYPE_PREFIX 1 +#define SADB_IDENTTYPE_FQDN 2 +#define SADB_IDENTTYPE_USERFQDN 3 +#define SADB_IDENTTYPE_MAX 3 + +#endif /* !(_LINUX_PFKEY2_H) */ diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h new file mode 100644 index 0000000..ee95f42 --- /dev/null +++ b/include/uapi/linux/pkt_cls.h @@ -0,0 +1,773 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include +#include + +#define TC_COOKIE_MAX_SIZE 16 + +/* Action attributes */ +enum { + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + TCA_ACT_STATS, + TCA_ACT_PAD, + TCA_ACT_COOKIE, + TCA_ACT_FLAGS, + TCA_ACT_HW_STATS, + TCA_ACT_USED_HW_STATS, + __TCA_ACT_MAX +}; + +#define TCA_ACT_FLAGS_NO_PERCPU_STATS 1 /* Don't use percpu allocator for + * actions stats. + */ + +/* tca HW stats type + * When user does not pass the attribute, he does not care. + * It is the same as if he would pass the attribute with + * all supported bits set. + * In case no bits are set, user is not interested in getting any HW statistics. + */ +#define TCA_ACT_HW_STATS_IMMEDIATE (1 << 0) /* Means that in dump, user + * gets the current HW stats + * state from the device + * queried at the dump time. + */ +#define TCA_ACT_HW_STATS_DELAYED (1 << 1) /* Means that in dump, user gets + * HW stats that might be out of date + * for some time, maybe couple of + * seconds. This is the case when + * driver polls stats updates + * periodically or when it gets async + * stats update from the device. + */ + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_REDIRECT 7 +#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu" + * and don't further process the frame + * in hardware. For sw path, this is + * equivalent of TC_ACT_STOLEN - drop + * the skb and act like everything + * is alright. + */ +#define TC_ACT_VALUE_MAX TC_ACT_TRAP + +/* There is a special kind of actions called "extended actions", + * which need a value parameter. These have a local opcode located in + * the highest nibble, starting from 1. The rest of the bits + * are used to carry the value. These two parts together make + * a combined opcode. + */ +#define __TC_ACT_EXT_SHIFT 28 +#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT) +#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1) +#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK)) +#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode) + +#define TC_ACT_JUMP __TC_ACT_EXT(1) +#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2) +#define TC_ACT_EXT_OPCODE_MAX TC_ACT_GOTO_CHAIN + +/* These macros are put here for binary compatibility with userspace apps that + * make use of them. For kernel code and new userspace apps, use the TCA_ID_* + * versions. + */ +#define TCA_ACT_GACT 5 +#define TCA_ACT_IPT 6 +#define TCA_ACT_PEDIT 7 +#define TCA_ACT_MIRRED 8 +#define TCA_ACT_NAT 9 +#define TCA_ACT_XT 10 +#define TCA_ACT_SKBEDIT 11 +#define TCA_ACT_VLAN 12 +#define TCA_ACT_BPF 13 +#define TCA_ACT_CONNMARK 14 +#define TCA_ACT_SKBMOD 15 +#define TCA_ACT_CSUM 16 +#define TCA_ACT_TUNNEL_KEY 17 +#define TCA_ACT_SIMP 22 +#define TCA_ACT_IFE 25 +#define TCA_ACT_SAMPLE 26 + +/* Action type identifiers*/ +enum tca_id { + TCA_ID_UNSPEC = 0, + TCA_ID_POLICE = 1, + TCA_ID_GACT = TCA_ACT_GACT, + TCA_ID_IPT = TCA_ACT_IPT, + TCA_ID_PEDIT = TCA_ACT_PEDIT, + TCA_ID_MIRRED = TCA_ACT_MIRRED, + TCA_ID_NAT = TCA_ACT_NAT, + TCA_ID_XT = TCA_ACT_XT, + TCA_ID_SKBEDIT = TCA_ACT_SKBEDIT, + TCA_ID_VLAN = TCA_ACT_VLAN, + TCA_ID_BPF = TCA_ACT_BPF, + TCA_ID_CONNMARK = TCA_ACT_CONNMARK, + TCA_ID_SKBMOD = TCA_ACT_SKBMOD, + TCA_ID_CSUM = TCA_ACT_CSUM, + TCA_ID_TUNNEL_KEY = TCA_ACT_TUNNEL_KEY, + TCA_ID_SIMP = TCA_ACT_SIMP, + TCA_ID_IFE = TCA_ACT_IFE, + TCA_ID_SAMPLE = TCA_ACT_SAMPLE, + TCA_ID_CTINFO, + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, + /* other actions go here */ + __TCA_ID_MAX = 255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police { + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t { + __u64 install; + __u64 lastuse; + __u64 expires; + __u64 firstuse; +}; + +struct tc_cnt { + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum { + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + TCA_POLICE_TM, + TCA_POLICE_PAD, + TCA_POLICE_RATE64, + TCA_POLICE_PEAKRATE64, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* tca flags definitions */ +#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) /* don't offload filter to HW */ +#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) /* don't use filter in SW */ +#define TCA_CLS_FLAGS_IN_HW (1 << 2) /* filter is offloaded to HW */ +#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */ +#define TCA_CLS_FLAGS_VERBOSE (1 << 4) /* verbose logging */ + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum { + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + TCA_U32_FLAGS, + TCA_U32_PAD, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key { + __be32 mask; + __be32 val; + int off; + int offmask; +}; + +struct tc_u32_sel { + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __be16 offmask; + __u16 off; + short offoff; + + short hoff; + __be32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark { + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt { + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum { + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi { + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo { + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; + __u8 pad; +}; + +/* ROUTE filter */ + +enum { + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum { + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + TCA_FW_MASK, + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum { + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Flow filter */ + +enum { + FLOW_KEY_SRC, + FLOW_KEY_DST, + FLOW_KEY_PROTO, + FLOW_KEY_PROTO_SRC, + FLOW_KEY_PROTO_DST, + FLOW_KEY_IIF, + FLOW_KEY_PRIORITY, + FLOW_KEY_MARK, + FLOW_KEY_NFCT, + FLOW_KEY_NFCT_SRC, + FLOW_KEY_NFCT_DST, + FLOW_KEY_NFCT_PROTO_SRC, + FLOW_KEY_NFCT_PROTO_DST, + FLOW_KEY_RTCLASSID, + FLOW_KEY_SKUID, + FLOW_KEY_SKGID, + FLOW_KEY_VLAN_TAG, + FLOW_KEY_RXHASH, + __FLOW_KEY_MAX, +}; + +#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1) + +enum { + FLOW_MODE_MAP, + FLOW_MODE_HASH, +}; + +enum { + TCA_FLOW_UNSPEC, + TCA_FLOW_KEYS, + TCA_FLOW_MODE, + TCA_FLOW_BASECLASS, + TCA_FLOW_RSHIFT, + TCA_FLOW_ADDEND, + TCA_FLOW_MASK, + TCA_FLOW_XOR, + TCA_FLOW_DIVISOR, + TCA_FLOW_ACT, + TCA_FLOW_POLICE, + TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, + __TCA_FLOW_MAX +}; + +#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1) + +/* Basic filter */ + +struct tc_basic_pcnt { + __u64 rcnt; + __u64 rhit; +}; + +enum { + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + TCA_BASIC_PCNT, + TCA_BASIC_PAD, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + + +/* Cgroup classifier */ + +enum { + TCA_CGROUP_UNSPEC, + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, + TCA_CGROUP_EMATCHES, + __TCA_CGROUP_MAX, +}; + +#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) + +/* BPF classifier */ + +#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0) + +enum { + TCA_BPF_UNSPEC, + TCA_BPF_ACT, + TCA_BPF_POLICE, + TCA_BPF_CLASSID, + TCA_BPF_OPS_LEN, + TCA_BPF_OPS, + TCA_BPF_FD, + TCA_BPF_NAME, + TCA_BPF_FLAGS, + TCA_BPF_FLAGS_GEN, + TCA_BPF_TAG, + TCA_BPF_ID, + __TCA_BPF_MAX, +}; + +#define TCA_BPF_MAX (__TCA_BPF_MAX - 1) + +/* Flower classifier */ + +enum { + TCA_FLOWER_UNSPEC, + TCA_FLOWER_CLASSID, + TCA_FLOWER_INDEV, + TCA_FLOWER_ACT, + TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ETH_TYPE, /* be16 */ + TCA_FLOWER_KEY_IP_PROTO, /* u8 */ + TCA_FLOWER_KEY_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */ + TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */ + TCA_FLOWER_KEY_TCP_SRC, /* be16 */ + TCA_FLOWER_KEY_TCP_DST, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC, /* be16 */ + TCA_FLOWER_KEY_UDP_DST, /* be16 */ + + TCA_FLOWER_FLAGS, + TCA_FLOWER_KEY_VLAN_ID, /* be16 */ + TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */ + TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */ + TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */ + TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */ + + TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */ + + TCA_FLOWER_KEY_SCTP_SRC, /* be16 */ + TCA_FLOWER_KEY_SCTP_DST, /* be16 */ + + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */ + TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */ + + TCA_FLOWER_KEY_FLAGS, /* be32 */ + TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */ + + TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */ + TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */ + + TCA_FLOWER_KEY_ARP_SIP, /* be32 */ + TCA_FLOWER_KEY_ARP_SIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP, /* be32 */ + TCA_FLOWER_KEY_ARP_TIP_MASK, /* be32 */ + TCA_FLOWER_KEY_ARP_OP, /* u8 */ + TCA_FLOWER_KEY_ARP_OP_MASK, /* u8 */ + TCA_FLOWER_KEY_ARP_SHA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_SHA_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */ + TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */ + + TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */ + TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */ + TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */ + TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */ + + TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */ + TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */ + + TCA_FLOWER_KEY_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_CVLAN_ID, /* be16 */ + TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */ + TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */ + + TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */ + TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */ + + TCA_FLOWER_KEY_ENC_OPTS, + TCA_FLOWER_KEY_ENC_OPTS_MASK, + + TCA_FLOWER_IN_HW_COUNT, + + TCA_FLOWER_KEY_PORT_SRC_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_SRC_MAX, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MIN, /* be16 */ + TCA_FLOWER_KEY_PORT_DST_MAX, /* be16 */ + + TCA_FLOWER_KEY_CT_STATE, /* u16 */ + TCA_FLOWER_KEY_CT_STATE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE, /* u16 */ + TCA_FLOWER_KEY_CT_ZONE_MASK, /* u16 */ + TCA_FLOWER_KEY_CT_MARK, /* u32 */ + TCA_FLOWER_KEY_CT_MARK_MASK, /* u32 */ + TCA_FLOWER_KEY_CT_LABELS, /* u128 */ + TCA_FLOWER_KEY_CT_LABELS_MASK, /* u128 */ + + TCA_FLOWER_KEY_MPLS_OPTS, + + TCA_FLOWER_KEY_HASH, /* u32 */ + TCA_FLOWER_KEY_HASH_MASK, /* u32 */ + + __TCA_FLOWER_MAX, +}; + +#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1) + +enum { + TCA_FLOWER_KEY_CT_FLAGS_NEW = 1 << 0, /* Beginning of a new connection. */ + TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 1 << 1, /* Part of an existing connection. */ + TCA_FLOWER_KEY_CT_FLAGS_RELATED = 1 << 2, /* Related to an established connection. */ + TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 1 << 3, /* Conntrack has occurred. */ +}; + +enum { + TCA_FLOWER_KEY_ENC_OPTS_UNSPEC, + TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_GENEVE_ + * attributes + */ + TCA_FLOWER_KEY_ENC_OPTS_VXLAN, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_VXLAN_ + * attributes + */ + TCA_FLOWER_KEY_ENC_OPTS_ERSPAN, /* Nested + * TCA_FLOWER_KEY_ENC_OPT_ERSPAN_ + * attributes + */ + __TCA_FLOWER_KEY_ENC_OPTS_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */ + + __TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_VXLAN_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, /* u32 */ + __TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX - 1) + +enum { + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_UNSPEC, + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX, /* be32 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR, /* u8 */ + TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID, /* u8 */ + __TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX, +}; + +#define TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX \ + (__TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX - 1) + +enum { + TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC, + TCA_FLOWER_KEY_MPLS_OPTS_LSE, + __TCA_FLOWER_KEY_MPLS_OPTS_MAX, +}; + +#define TCA_FLOWER_KEY_MPLS_OPTS_MAX (__TCA_FLOWER_KEY_MPLS_OPTS_MAX - 1) + +enum { + TCA_FLOWER_KEY_MPLS_OPT_LSE_UNSPEC, + TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH, + TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL, + TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS, + TCA_FLOWER_KEY_MPLS_OPT_LSE_TC, + TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL, + __TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX, +}; + +#define TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX \ + (__TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX - 1) + +enum { + TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0), + TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1), +}; + +#define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */ + +/* Match-all classifier */ + +struct tc_matchall_pcnt { + __u64 rhit; +}; + +enum { + TCA_MATCHALL_UNSPEC, + TCA_MATCHALL_CLASSID, + TCA_MATCHALL_ACT, + TCA_MATCHALL_FLAGS, + TCA_MATCHALL_PCNT, + TCA_MATCHALL_PAD, + __TCA_MATCHALL_MAX, +}; + +#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr { + __u16 nmatches; + __u16 progid; +}; + +enum { + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr { + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum { + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_VLAN 6 +#define TCF_EM_CANID 7 +#define TCF_EM_IPSET 8 +#define TCF_EM_IPT 9 +#define TCF_EM_MAX 9 + +enum { + TCF_EM_PROG_TC +}; + +enum { + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h new file mode 100644 index 0000000..9e7c2c6 --- /dev/null +++ b/include/uapi/linux/pkt_sched.h @@ -0,0 +1,1265 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +#include +#include + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats { + __u64 bytes; /* Number of enqueued bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator { + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) +#define TC_H_CLSACT TC_H_INGRESS + +#define TC_H_MIN_PRIORITY 0xFFE0U +#define TC_H_MIN_INGRESS 0xFFF2U +#define TC_H_MIN_EGRESS 0xFFF3U + +/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ +enum tc_link_layer { + TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ + TC_LINKLAYER_ETHERNET, + TC_LINKLAYER_ATM, +}; +#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ + +struct tc_ratespec { + unsigned char cell_log; + __u8 linklayer; /* lower 4 bits */ + unsigned short overhead; + short cell_align; + unsigned short mpu; + __u32 rate; +}; + +#define TC_RTAB_SIZE 1024 + +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + +/* FIFO section */ + +struct tc_fifo_qopt { + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* SKBPRIO section */ + +/* + * Priorities go from zero to (SKBPRIO_MAX_PRIORITY - 1). + * SKBPRIO_MAX_PRIORITY should be at least 64 in order for skbprio to be able + * to map one to one the DS field of IPV4 and IPV6 headers. + * Memory allocation grows linearly with SKBPRIO_MAX_PRIORITY. + */ + +#define SKBPRIO_MAX_PRIORITY 64 + +struct tc_skbprio_qopt { + __u32 limit; /* Queue length in packets. */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 +#define TCQ_MIN_PRIO_BANDS 2 + +struct tc_prio_qopt { + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* MULTIQ section */ + +struct tc_multiq_qopt { + __u16 bands; /* Number of bands */ + __u16 max_bands; /* Maximum number of queues */ +}; + +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + +/* TBF section */ + +struct tc_tbf_qopt { + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum { + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + TCA_TBF_RATE64, + TCA_TBF_PRATE64, + TCA_TBF_BURST, + TCA_TBF_PBURST, + TCA_TBF_PAD, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt { + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +struct tc_sfqred_stats { + __u32 prob_drop; /* Early drops, below max threshold */ + __u32 forced_drop; /* Early drops, after max threshold */ + __u32 prob_mark; /* Marked packets, below max threshold */ + __u32 forced_mark; /* Marked packets, after max threshold */ + __u32 prob_mark_head; /* Marked packets, below max threshold */ + __u32 forced_mark_head;/* Marked packets, after max threshold */ +}; + +struct tc_sfq_qopt_v1 { + struct tc_sfq_qopt v0; + unsigned int depth; /* max number of packets per flow */ + unsigned int headdrop; +/* SFQRED parameters */ + __u32 limit; /* HARD maximal flow queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; + __u32 max_P; /* probability, high resolution */ +/* SFQRED stats */ + struct tc_sfqred_stats stats; +}; + + +struct tc_sfq_xstats { + __s32 allot; +}; + +/* RED section */ + +enum { + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + TCA_RED_MAX_P, + TCA_RED_FLAGS, /* bitfield32 */ + TCA_RED_EARLY_DROP_BLOCK, /* u32 */ + TCA_RED_MARK_BLOCK, /* u32 */ + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + + /* This field can be used for flags that a RED-like qdisc has + * historically supported. E.g. when configuring RED, it can be used for + * ECN, HARDDROP and ADAPTATIVE. For SFQ it can be used for ECN, + * HARDDROP. Etc. Because this field has not been validated, and is + * copied back on dump, any bits besides those to which a given qdisc + * has assigned a historical meaning need to be considered for free use + * by userspace tools. + * + * Any further flags need to be passed differently, e.g. through an + * attribute (such as TCA_RED_FLAGS above). Such attribute should allow + * passing both recent and historic flags in one value. + */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +#define TC_RED_ADAPTATIVE 4 +#define TC_RED_NODROP 8 +}; + +#define TC_RED_HISTORIC_FLAGS (TC_RED_ECN | TC_RED_HARDDROP | TC_RED_ADAPTATIVE) + +struct tc_red_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum { + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + TCA_GRED_MAX_P, + TCA_GRED_LIMIT, + TCA_GRED_VQ_LIST, /* nested TCA_GRED_VQ_ENTRY */ + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +enum { + TCA_GRED_VQ_ENTRY_UNSPEC, + TCA_GRED_VQ_ENTRY, /* nested TCA_GRED_VQ_* */ + __TCA_GRED_VQ_ENTRY_MAX, +}; +#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1) + +enum { + TCA_GRED_VQ_UNSPEC, + TCA_GRED_VQ_PAD, + TCA_GRED_VQ_DP, /* u32 */ + TCA_GRED_VQ_STAT_BYTES, /* u64 */ + TCA_GRED_VQ_STAT_PACKETS, /* u32 */ + TCA_GRED_VQ_STAT_BACKLOG, /* u32 */ + TCA_GRED_VQ_STAT_PROB_DROP, /* u32 */ + TCA_GRED_VQ_STAT_PROB_MARK, /* u32 */ + TCA_GRED_VQ_STAT_FORCED_DROP, /* u32 */ + TCA_GRED_VQ_STAT_FORCED_MARK, /* u32 */ + TCA_GRED_VQ_STAT_PDROP, /* u32 */ + TCA_GRED_VQ_STAT_OTHER, /* u32 */ + TCA_GRED_VQ_FLAGS, /* u32 */ + __TCA_GRED_VQ_MAX +}; + +#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1) + +struct tc_gred_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + __u32 DP; /* up to 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + __u8 Wlog; /* log(W) */ + __u8 Plog; /* log(P_max/(qth_max-qth_min)) */ + __u8 Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; + +/* gred setup */ +struct tc_gred_sopt { + __u32 DPs; + __u32 def_DP; + __u8 grio; + __u8 flags; + __u16 pad1; +}; + +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + TCA_CHOKE_MAX_P, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt { + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob { + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shaped packets */ +}; +enum { + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + TCA_HTB_DIRECT_QLEN, + TCA_HTB_RATE64, + TCA_HTB_CEIL64, + TCA_HTB_PAD, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats { + __u32 lends; + __u32 borrows; + __u32 giants; /* unused since 'Make HTB scheduler work with TSO.' */ + __s32 tokens; + __s32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt { + __u16 defcls; /* default class */ +}; + +struct tc_service_curve { + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats { + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum { + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt { + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt { + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl { + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u16 pad; + __u32 penalty; +}; + +struct tc_cbq_police { + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt { + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats { + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum { + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum { + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + TCA_NETEM_REORDER, + TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, + TCA_NETEM_RATE, + TCA_NETEM_ECN, + TCA_NETEM_RATE64, + TCA_NETEM_PAD, + TCA_NETEM_LATENCY64, + TCA_NETEM_JITTER64, + TCA_NETEM_SLOT, + TCA_NETEM_SLOT_DIST, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt { + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for none) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr { + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +struct tc_netem_reorder { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_corrupt { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_rate { + __u32 rate; /* byte/s */ + __s32 packet_overhead; + __u32 cell_size; + __s32 cell_overhead; +}; + +struct tc_netem_slot { + __s64 min_delay; /* nsec */ + __s64 max_delay; + __s32 max_packets; + __s32 max_bytes; + __s64 dist_delay; /* nsec */ + __s64 dist_jitter; /* nsec */ +}; + +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probabilities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + +#define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 + +/* DRR */ + +enum { + TCA_DRR_UNSPEC, + TCA_DRR_QUANTUM, + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats { + __u32 deficit; +}; + +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +enum { + TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */ + TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */ + __TC_MQPRIO_HW_OFFLOAD_MAX +}; + +#define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1) + +enum { + TC_MQPRIO_MODE_DCB, + TC_MQPRIO_MODE_CHANNEL, + __TC_MQPRIO_MODE_MAX +}; + +#define __TC_MQPRIO_MODE_MAX (__TC_MQPRIO_MODE_MAX - 1) + +enum { + TC_MQPRIO_SHAPER_DCB, + TC_MQPRIO_SHAPER_BW_RATE, /* Add new shapers below */ + __TC_MQPRIO_SHAPER_MAX +}; + +#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1) + +struct tc_mqprio_qopt { + __u8 num_tc; + __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; + __u8 hw; + __u16 count[TC_QOPT_MAX_QUEUE]; + __u16 offset[TC_QOPT_MAX_QUEUE]; +}; + +#define TC_MQPRIO_F_MODE 0x1 +#define TC_MQPRIO_F_SHAPER 0x2 +#define TC_MQPRIO_F_MIN_RATE 0x4 +#define TC_MQPRIO_F_MAX_RATE 0x8 + +enum { + TCA_MQPRIO_UNSPEC, + TCA_MQPRIO_MODE, + TCA_MQPRIO_SHAPER, + TCA_MQPRIO_MIN_RATE64, + TCA_MQPRIO_MAX_RATE64, + __TCA_MQPRIO_MAX, +}; + +#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1) + +/* SFB */ + +enum { + TCA_SFB_UNSPEC, + TCA_SFB_PARMS, + __TCA_SFB_MAX, +}; + +#define TCA_SFB_MAX (__TCA_SFB_MAX - 1) + +/* + * Note: increment, decrement are Q0.16 fixed-point values. + */ +struct tc_sfb_qopt { + __u32 rehash_interval; /* delay between hash move, in ms */ + __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */ + __u32 max; /* max len of qlen_min */ + __u32 bin_size; /* maximum queue length per bin */ + __u32 increment; /* probability increment, (d1 in Blue) */ + __u32 decrement; /* probability decrement, (d2 in Blue) */ + __u32 limit; /* max SFB queue length */ + __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */ + __u32 penalty_burst; +}; + +struct tc_sfb_xstats { + __u32 earlydrop; + __u32 penaltydrop; + __u32 bucketdrop; + __u32 queuedrop; + __u32 childdrop; /* drops in child qdisc */ + __u32 marked; + __u32 maxqlen; + __u32 maxprob; + __u32 avgprob; +}; + +#define SFB_MAX_PROB 0xFFFF + +/* QFQ */ +enum { + TCA_QFQ_UNSPEC, + TCA_QFQ_WEIGHT, + TCA_QFQ_LMAX, + __TCA_QFQ_MAX +}; + +#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1) + +struct tc_qfq_stats { + __u32 weight; + __u32 lmax; +}; + +/* CODEL */ + +enum { + TCA_CODEL_UNSPEC, + TCA_CODEL_TARGET, + TCA_CODEL_LIMIT, + TCA_CODEL_INTERVAL, + TCA_CODEL_ECN, + TCA_CODEL_CE_THRESHOLD, + __TCA_CODEL_MAX +}; + +#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1) + +struct tc_codel_xstats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 count; /* how many drops we've done since the last time we + * entered dropping state + */ + __u32 lastcount; /* count at entry to dropping state */ + __u32 ldelay; /* in-queue delay seen by most recently dequeued packet */ + __s32 drop_next; /* time to drop next packet */ + __u32 drop_overlimit; /* number of time max qdisc packet limit was hit */ + __u32 ecn_mark; /* number of packets we ECN marked instead of dropped */ + __u32 dropping; /* are we in dropping state ? */ + __u32 ce_mark; /* number of CE marked packets because of ce_threshold */ +}; + +/* FQ_CODEL */ + +enum { + TCA_FQ_CODEL_UNSPEC, + TCA_FQ_CODEL_TARGET, + TCA_FQ_CODEL_LIMIT, + TCA_FQ_CODEL_INTERVAL, + TCA_FQ_CODEL_ECN, + TCA_FQ_CODEL_FLOWS, + TCA_FQ_CODEL_QUANTUM, + TCA_FQ_CODEL_CE_THRESHOLD, + TCA_FQ_CODEL_DROP_BATCH_SIZE, + TCA_FQ_CODEL_MEMORY_LIMIT, + __TCA_FQ_CODEL_MAX +}; + +#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1) + +enum { + TCA_FQ_CODEL_XSTATS_QDISC, + TCA_FQ_CODEL_XSTATS_CLASS, +}; + +struct tc_fq_codel_qd_stats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 drop_overlimit; /* number of time max qdisc + * packet limit was hit + */ + __u32 ecn_mark; /* number of packets we ECN marked + * instead of being dropped + */ + __u32 new_flow_count; /* number of time packets + * created a 'new flow' + */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 ce_mark; /* packets above ce_threshold */ + __u32 memory_usage; /* in bytes */ + __u32 drop_overmemory; +}; + +struct tc_fq_codel_cl_stats { + __s32 deficit; + __u32 ldelay; /* in-queue delay seen by most recently + * dequeued packet + */ + __u32 count; + __u32 lastcount; + __u32 dropping; + __s32 drop_next; +}; + +struct tc_fq_codel_xstats { + __u32 type; + union { + struct tc_fq_codel_qd_stats qdisc_stats; + struct tc_fq_codel_cl_stats class_stats; + }; +}; + +/* FQ */ + +enum { + TCA_FQ_UNSPEC, + + TCA_FQ_PLIMIT, /* limit of total number of packets in queue */ + + TCA_FQ_FLOW_PLIMIT, /* limit of packets per flow */ + + TCA_FQ_QUANTUM, /* RR quantum */ + + TCA_FQ_INITIAL_QUANTUM, /* RR quantum for new flow */ + + TCA_FQ_RATE_ENABLE, /* enable/disable rate limiting */ + + TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */ + + TCA_FQ_FLOW_MAX_RATE, /* per flow max rate */ + + TCA_FQ_BUCKETS_LOG, /* log2(number of buckets) */ + + TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */ + + TCA_FQ_ORPHAN_MASK, /* mask applied to orphaned skb hashes */ + + TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */ + + TCA_FQ_CE_THRESHOLD, /* DCTCP-like CE-marking threshold */ + + TCA_FQ_TIMER_SLACK, /* timer slack */ + + TCA_FQ_HORIZON, /* time horizon in us */ + + TCA_FQ_HORIZON_DROP, /* drop packets beyond horizon, or cap their EDT */ + + __TCA_FQ_MAX +}; + +#define TCA_FQ_MAX (__TCA_FQ_MAX - 1) + +struct tc_fq_qd_stats { + __u64 gc_flows; + __u64 highprio_packets; + __u64 tcp_retrans; + __u64 throttled; + __u64 flows_plimit; + __u64 pkts_too_long; + __u64 allocation_errors; + __s64 time_next_delayed_flow; + __u32 flows; + __u32 inactive_flows; + __u32 throttled_flows; + __u32 unthrottle_latency_ns; + __u64 ce_mark; /* packets above ce_threshold */ + __u64 horizon_drops; + __u64 horizon_caps; +}; + +/* Heavy-Hitter Filter */ + +enum { + TCA_HHF_UNSPEC, + TCA_HHF_BACKLOG_LIMIT, + TCA_HHF_QUANTUM, + TCA_HHF_HH_FLOWS_LIMIT, + TCA_HHF_RESET_TIMEOUT, + TCA_HHF_ADMIT_BYTES, + TCA_HHF_EVICT_TIMEOUT, + TCA_HHF_NON_HH_WEIGHT, + __TCA_HHF_MAX +}; + +#define TCA_HHF_MAX (__TCA_HHF_MAX - 1) + +struct tc_hhf_xstats { + __u32 drop_overlimit; /* number of times max qdisc packet limit + * was hit + */ + __u32 hh_overlimit; /* number of times max heavy-hitters was hit */ + __u32 hh_tot_count; /* number of captured heavy-hitters so far */ + __u32 hh_cur_count; /* number of current heavy-hitters */ +}; + +/* PIE */ +enum { + TCA_PIE_UNSPEC, + TCA_PIE_TARGET, + TCA_PIE_LIMIT, + TCA_PIE_TUPDATE, + TCA_PIE_ALPHA, + TCA_PIE_BETA, + TCA_PIE_ECN, + TCA_PIE_BYTEMODE, + TCA_PIE_DQ_RATE_ESTIMATOR, + __TCA_PIE_MAX +}; +#define TCA_PIE_MAX (__TCA_PIE_MAX - 1) + +struct tc_pie_xstats { + __u64 prob; /* current probability */ + __u32 delay; /* current delay in ms */ + __u32 avg_dq_rate; /* current average dq_rate in + * bits/pie_time + */ + __u32 dq_rate_estimating; /* is avg_dq_rate being calculated? */ + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to pie_action */ + __u32 overlimit; /* dropped due to lack of space + * in queue + */ + __u32 maxq; /* maximum queue size */ + __u32 ecn_mark; /* packets marked with ecn*/ +}; + +/* FQ PIE */ +enum { + TCA_FQ_PIE_UNSPEC, + TCA_FQ_PIE_LIMIT, + TCA_FQ_PIE_FLOWS, + TCA_FQ_PIE_TARGET, + TCA_FQ_PIE_TUPDATE, + TCA_FQ_PIE_ALPHA, + TCA_FQ_PIE_BETA, + TCA_FQ_PIE_QUANTUM, + TCA_FQ_PIE_MEMORY_LIMIT, + TCA_FQ_PIE_ECN_PROB, + TCA_FQ_PIE_ECN, + TCA_FQ_PIE_BYTEMODE, + TCA_FQ_PIE_DQ_RATE_ESTIMATOR, + __TCA_FQ_PIE_MAX +}; +#define TCA_FQ_PIE_MAX (__TCA_FQ_PIE_MAX - 1) + +struct tc_fq_pie_xstats { + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to fq_pie_action */ + __u32 overlimit; /* dropped due to lack of space in queue */ + __u32 overmemory; /* dropped due to lack of memory in queue */ + __u32 ecn_mark; /* packets marked with ecn */ + __u32 new_flow_count; /* count of new flows created by packets */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ + __u32 memory_usage; /* total memory across all queues */ +}; + +/* CBS */ +struct tc_cbs_qopt { + __u8 offload; + __u8 _pad[3]; + __s32 hicredit; + __s32 locredit; + __s32 idleslope; + __s32 sendslope; +}; + +enum { + TCA_CBS_UNSPEC, + TCA_CBS_PARMS, + __TCA_CBS_MAX, +}; + +#define TCA_CBS_MAX (__TCA_CBS_MAX - 1) + + +/* ETF */ +struct tc_etf_qopt { + __s32 delta; + __s32 clockid; + __u32 flags; +#define TC_ETF_DEADLINE_MODE_ON _BITUL(0) +#define TC_ETF_OFFLOAD_ON _BITUL(1) +#define TC_ETF_SKIP_SOCK_CHECK _BITUL(2) +}; + +enum { + TCA_ETF_UNSPEC, + TCA_ETF_PARMS, + __TCA_ETF_MAX, +}; + +#define TCA_ETF_MAX (__TCA_ETF_MAX - 1) + + +/* CAKE */ +enum { + TCA_CAKE_UNSPEC, + TCA_CAKE_PAD, + TCA_CAKE_BASE_RATE64, + TCA_CAKE_DIFFSERV_MODE, + TCA_CAKE_ATM, + TCA_CAKE_FLOW_MODE, + TCA_CAKE_OVERHEAD, + TCA_CAKE_RTT, + TCA_CAKE_TARGET, + TCA_CAKE_AUTORATE, + TCA_CAKE_MEMORY, + TCA_CAKE_NAT, + TCA_CAKE_RAW, + TCA_CAKE_WASH, + TCA_CAKE_MPU, + TCA_CAKE_INGRESS, + TCA_CAKE_ACK_FILTER, + TCA_CAKE_SPLIT_GSO, + TCA_CAKE_FWMARK, + __TCA_CAKE_MAX +}; +#define TCA_CAKE_MAX (__TCA_CAKE_MAX - 1) + +enum { + __TCA_CAKE_STATS_INVALID, + TCA_CAKE_STATS_PAD, + TCA_CAKE_STATS_CAPACITY_ESTIMATE64, + TCA_CAKE_STATS_MEMORY_LIMIT, + TCA_CAKE_STATS_MEMORY_USED, + TCA_CAKE_STATS_AVG_NETOFF, + TCA_CAKE_STATS_MIN_NETLEN, + TCA_CAKE_STATS_MAX_NETLEN, + TCA_CAKE_STATS_MIN_ADJLEN, + TCA_CAKE_STATS_MAX_ADJLEN, + TCA_CAKE_STATS_TIN_STATS, + TCA_CAKE_STATS_DEFICIT, + TCA_CAKE_STATS_COBALT_COUNT, + TCA_CAKE_STATS_DROPPING, + TCA_CAKE_STATS_DROP_NEXT_US, + TCA_CAKE_STATS_P_DROP, + TCA_CAKE_STATS_BLUE_TIMER_US, + __TCA_CAKE_STATS_MAX +}; +#define TCA_CAKE_STATS_MAX (__TCA_CAKE_STATS_MAX - 1) + +enum { + __TCA_CAKE_TIN_STATS_INVALID, + TCA_CAKE_TIN_STATS_PAD, + TCA_CAKE_TIN_STATS_SENT_PACKETS, + TCA_CAKE_TIN_STATS_SENT_BYTES64, + TCA_CAKE_TIN_STATS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_PACKETS, + TCA_CAKE_TIN_STATS_ACKS_DROPPED_BYTES64, + TCA_CAKE_TIN_STATS_ECN_MARKED_PACKETS, + TCA_CAKE_TIN_STATS_ECN_MARKED_BYTES64, + TCA_CAKE_TIN_STATS_BACKLOG_PACKETS, + TCA_CAKE_TIN_STATS_BACKLOG_BYTES, + TCA_CAKE_TIN_STATS_THRESHOLD_RATE64, + TCA_CAKE_TIN_STATS_TARGET_US, + TCA_CAKE_TIN_STATS_INTERVAL_US, + TCA_CAKE_TIN_STATS_WAY_INDIRECT_HITS, + TCA_CAKE_TIN_STATS_WAY_MISSES, + TCA_CAKE_TIN_STATS_WAY_COLLISIONS, + TCA_CAKE_TIN_STATS_PEAK_DELAY_US, + TCA_CAKE_TIN_STATS_AVG_DELAY_US, + TCA_CAKE_TIN_STATS_BASE_DELAY_US, + TCA_CAKE_TIN_STATS_SPARSE_FLOWS, + TCA_CAKE_TIN_STATS_BULK_FLOWS, + TCA_CAKE_TIN_STATS_UNRESPONSIVE_FLOWS, + TCA_CAKE_TIN_STATS_MAX_SKBLEN, + TCA_CAKE_TIN_STATS_FLOW_QUANTUM, + __TCA_CAKE_TIN_STATS_MAX +}; +#define TCA_CAKE_TIN_STATS_MAX (__TCA_CAKE_TIN_STATS_MAX - 1) +#define TC_CAKE_MAX_TINS (8) + +enum { + CAKE_FLOW_NONE = 0, + CAKE_FLOW_SRC_IP, + CAKE_FLOW_DST_IP, + CAKE_FLOW_HOSTS, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_DST_IP */ + CAKE_FLOW_FLOWS, + CAKE_FLOW_DUAL_SRC, /* = CAKE_FLOW_SRC_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_DUAL_DST, /* = CAKE_FLOW_DST_IP | CAKE_FLOW_FLOWS */ + CAKE_FLOW_TRIPLE, /* = CAKE_FLOW_HOSTS | CAKE_FLOW_FLOWS */ + CAKE_FLOW_MAX, +}; + +enum { + CAKE_DIFFSERV_DIFFSERV3 = 0, + CAKE_DIFFSERV_DIFFSERV4, + CAKE_DIFFSERV_DIFFSERV8, + CAKE_DIFFSERV_BESTEFFORT, + CAKE_DIFFSERV_PRECEDENCE, + CAKE_DIFFSERV_MAX +}; + +enum { + CAKE_ACK_NONE = 0, + CAKE_ACK_FILTER, + CAKE_ACK_AGGRESSIVE, + CAKE_ACK_MAX +}; + +enum { + CAKE_ATM_NONE = 0, + CAKE_ATM_ATM, + CAKE_ATM_PTM, + CAKE_ATM_MAX +}; + + +/* TAPRIO */ +enum { + TC_TAPRIO_CMD_SET_GATES = 0x00, + TC_TAPRIO_CMD_SET_AND_HOLD = 0x01, + TC_TAPRIO_CMD_SET_AND_RELEASE = 0x02, +}; + +enum { + TCA_TAPRIO_SCHED_ENTRY_UNSPEC, + TCA_TAPRIO_SCHED_ENTRY_INDEX, /* u32 */ + TCA_TAPRIO_SCHED_ENTRY_CMD, /* u8 */ + TCA_TAPRIO_SCHED_ENTRY_GATE_MASK, /* u32 */ + TCA_TAPRIO_SCHED_ENTRY_INTERVAL, /* u32 */ + __TCA_TAPRIO_SCHED_ENTRY_MAX, +}; +#define TCA_TAPRIO_SCHED_ENTRY_MAX (__TCA_TAPRIO_SCHED_ENTRY_MAX - 1) + +/* The format for schedule entry list is: + * [TCA_TAPRIO_SCHED_ENTRY_LIST] + * [TCA_TAPRIO_SCHED_ENTRY] + * [TCA_TAPRIO_SCHED_ENTRY_CMD] + * [TCA_TAPRIO_SCHED_ENTRY_GATES] + * [TCA_TAPRIO_SCHED_ENTRY_INTERVAL] + */ +enum { + TCA_TAPRIO_SCHED_UNSPEC, + TCA_TAPRIO_SCHED_ENTRY, + __TCA_TAPRIO_SCHED_MAX, +}; + +#define TCA_TAPRIO_SCHED_MAX (__TCA_TAPRIO_SCHED_MAX - 1) + +/* The format for the admin sched (dump only): + * [TCA_TAPRIO_SCHED_ADMIN_SCHED] + * [TCA_TAPRIO_ATTR_SCHED_BASE_TIME] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_CMD] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_GATES] + * [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL] + */ + +#define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST _BITUL(0) +#define TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD _BITUL(1) + +enum { + TCA_TAPRIO_ATTR_UNSPEC, + TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */ + TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST, /* nested of entry */ + TCA_TAPRIO_ATTR_SCHED_BASE_TIME, /* s64 */ + TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY, /* single entry */ + TCA_TAPRIO_ATTR_SCHED_CLOCKID, /* s32 */ + TCA_TAPRIO_PAD, + TCA_TAPRIO_ATTR_ADMIN_SCHED, /* The admin sched, only used in dump */ + TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */ + TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */ + TCA_TAPRIO_ATTR_FLAGS, /* u32 */ + TCA_TAPRIO_ATTR_TXTIME_DELAY, /* u32 */ + __TCA_TAPRIO_ATTR_MAX, +}; + +#define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1) + +/* ETS */ + +#define TCQ_ETS_MAX_BANDS 16 + +enum { + TCA_ETS_UNSPEC, + TCA_ETS_NBANDS, /* u8 */ + TCA_ETS_NSTRICT, /* u8 */ + TCA_ETS_QUANTA, /* nested TCA_ETS_QUANTA_BAND */ + TCA_ETS_QUANTA_BAND, /* u32 */ + TCA_ETS_PRIOMAP, /* nested TCA_ETS_PRIOMAP_BAND */ + TCA_ETS_PRIOMAP_BAND, /* u8 */ + __TCA_ETS_MAX, +}; + +#define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + +#endif diff --git a/include/uapi/linux/posix_types.h b/include/uapi/linux/posix_types.h new file mode 100644 index 0000000..9a7a740 --- /dev/null +++ b/include/uapi/linux/posix_types.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_POSIX_TYPES_H +#define _LINUX_POSIX_TYPES_H + +#include + +/* + * This allows for 1024 file descriptors: if NR_OPEN is ever grown + * beyond that you'll have to change this too. But 1024 fd's seem to be + * enough even for such "real" unices like OSF/1, so hopefully this is + * one limit that doesn't have to be changed [again]. + * + * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in + * (and thus ) - but this is a more logical + * place for them. Solved by having dummy defines in . + */ + +/* + * This macro may have been defined in . But we always + * use the one here. + */ +#undef __FD_SETSIZE +#define __FD_SETSIZE 1024 + +typedef struct { + unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))]; +} __kernel_fd_set; + +/* Type of a signal handler. */ +typedef void (*__kernel_sighandler_t)(int); + +/* Type of a SYSV IPC key. */ +typedef int __kernel_key_t; +typedef int __kernel_mqd_t; + +#include + +#endif /* _LINUX_POSIX_TYPES_H */ diff --git a/include/uapi/linux/rpl.h b/include/uapi/linux/rpl.h new file mode 100644 index 0000000..c24b64c --- /dev/null +++ b/include/uapi/linux/rpl.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * IPv6 RPL-SR implementation + * + * Author: + * (C) 2020 Alexander Aring + */ + +#ifndef _LINUX_RPL_H +#define _LINUX_RPL_H + +#include +#include +#include + +/* + * RPL SR Header + */ +struct ipv6_rpl_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 cmpre:4, + cmpri:4, + reserved:4, + pad:4, + reserved1:16; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 reserved:20, + pad:4, + cmpri:4, + cmpre:4; +#else +#error "Please fix " +#endif + + union { + struct in6_addr addr[0]; + __u8 data[0]; + } segments; +} __attribute__((packed)); + +#define rpl_segaddr segments.addr +#define rpl_segdata segments.data + +#endif diff --git a/include/uapi/linux/rpl_iptunnel.h b/include/uapi/linux/rpl_iptunnel.h new file mode 100644 index 0000000..c255b92 --- /dev/null +++ b/include/uapi/linux/rpl_iptunnel.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * IPv6 RPL-SR implementation + * + * Author: + * (C) 2020 Alexander Aring + */ + +#ifndef _LINUX_RPL_IPTUNNEL_H +#define _LINUX_RPL_IPTUNNEL_H + +enum { + RPL_IPTUNNEL_UNSPEC, + RPL_IPTUNNEL_SRH, + __RPL_IPTUNNEL_MAX, +}; +#define RPL_IPTUNNEL_MAX (__RPL_IPTUNNEL_MAX - 1) + +#define RPL_IPTUNNEL_SRH_SIZE(srh) (((srh)->hdrlen + 1) << 3) + +#endif diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h new file mode 100644 index 0000000..5ad84e6 --- /dev/null +++ b/include/uapi/linux/rtnetlink.h @@ -0,0 +1,785 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#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_NEWNETCONF = 80, +#define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF + RTM_GETNETCONF = 82, +#define RTM_GETNETCONF RTM_GETNETCONF + + RTM_NEWMDB = 84, +#define RTM_NEWMDB RTM_NEWMDB + RTM_DELMDB = 85, +#define RTM_DELMDB RTM_DELMDB + RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB + + RTM_NEWNSID = 88, +#define RTM_NEWNSID RTM_NEWNSID + RTM_DELNSID = 89, +#define RTM_DELNSID RTM_DELNSID + RTM_GETNSID = 90, +#define RTM_GETNSID RTM_GETNSID + + RTM_NEWSTATS = 92, +#define RTM_NEWSTATS RTM_NEWSTATS + RTM_GETSTATS = 94, +#define RTM_GETSTATS RTM_GETSTATS + + RTM_NEWCACHEREPORT = 96, +#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT + + RTM_NEWCHAIN = 100, +#define RTM_NEWCHAIN RTM_NEWCHAIN + RTM_DELCHAIN, +#define RTM_DELCHAIN RTM_DELCHAIN + RTM_GETCHAIN, +#define RTM_GETCHAIN RTM_GETCHAIN + + RTM_NEWNEXTHOP = 104, +#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP + RTM_DELNEXTHOP, +#define RTM_DELNEXTHOP RTM_DELNEXTHOP + RTM_GETNEXTHOP, +#define RTM_GETNEXTHOP RTM_GETNEXTHOP + + RTM_NEWLINKPROP = 108, +#define RTM_NEWLINKPROP RTM_NEWLINKPROP + RTM_DELLINKPROP, +#define RTM_DELLINKPROP RTM_DELLINKPROP + RTM_GETLINKPROP, +#define RTM_GETLINKPROP RTM_GETLINKPROP + + RTM_NEWVLAN = 112, +#define RTM_NEWNVLAN RTM_NEWVLAN + RTM_DELVLAN, +#define RTM_DELVLAN RTM_DELVLAN + RTM_GETVLAN, +#define RTM_GETVLAN RTM_GETVLAN + + __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 4U +#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 */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ +#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ +#define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ + +/* 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 */ +#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */ +#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */ +#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */ +#define RTM_F_TRAP 0x8000 /* route is trapping packets */ + +/* 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_MFC_STATS, + RTA_VIA, + RTA_NEWDST, + RTA_PREF, + RTA_ENCAP_TYPE, + RTA_ENCAP, + RTA_EXPIRES, + RTA_PAD, + RTA_UID, + RTA_TTL_PROPAGATE, + RTA_IP_PROTO, + RTA_SPORT, + RTA_DPORT, + RTA_NH_ID, + __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 */ +#define RTNH_F_OFFLOAD 8 /* offloaded route */ +#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ +#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ + +#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD) + +/* 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))) + +/* RTA_VIA */ +struct rtvia { + __kernel_sa_family_t rtvia_family; + __u8 rtvia_addr[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_QUICKACK, +#define RTAX_QUICKACK RTAX_QUICKACK + RTAX_CC_ALGO, +#define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN (1 << 0) +#define RTAX_FEATURE_SACK (1 << 1) +#define RTAX_FEATURE_TIMESTAMP (1 << 2) +#define RTAX_FEATURE_ALLFRAG (1 << 3) + +#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | \ + RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG) + +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; +}; + +struct rta_mfc_stats { + __u64 mfcs_packets; + __u64 mfcs_bytes; + __u64 mfcs_wrong_if; +}; + +/**** + * 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; +/* tcm_block_index is used instead of tcm_parent + * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK + */ +#define tcm_block_index tcm_parent + __u32 tcm_info; +}; + +/* For manipulation of filters in shared block, tcm_ifindex is set to + * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index + * which is the block index. + */ +#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + TCA_PAD, + TCA_DUMP_INVISIBLE, + TCA_CHAIN, + TCA_HW_OFFLOAD, + TCA_INGRESS_BLOCK, + TCA_EGRESS_BLOCK, + TCA_DUMP_FLAGS, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_DUMP_FLAGS_TERSE (1 << 0) /* Means that in dump user gets only basic + * data necessary to identify the objects + * (handle, cookie, etc.) and stats. + */ + +#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) + +/* 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 + +/* 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_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + RTNLGRP_IPV4_NETCONF, +#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF + RTNLGRP_IPV6_NETCONF, +#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF + RTNLGRP_MDB, +#define RTNLGRP_MDB RTNLGRP_MDB + RTNLGRP_MPLS_ROUTE, +#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE + RTNLGRP_NSID, +#define RTNLGRP_NSID RTNLGRP_NSID + RTNLGRP_MPLS_NETCONF, +#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF + RTNLGRP_IPV4_MROUTE_R, +#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R + RTNLGRP_IPV6_MROUTE_R, +#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R + RTNLGRP_NEXTHOP, +#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP + RTNLGRP_BRVLAN, +#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN + __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; +}; + +enum { + TCA_ROOT_UNSPEC, + TCA_ROOT_TAB, +#define TCA_ACT_TAB TCA_ROOT_TAB +#define TCAA_MAX TCA_ROOT_TAB + TCA_ROOT_FLAGS, + TCA_ROOT_COUNT, + TCA_ROOT_TIME_DELTA, /* in msecs */ + __TCA_ROOT_MAX, +#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) +}; + +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +/* tcamsg flags stored in attribute TCA_ROOT_FLAGS + * + * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO + * actions in a dump. All dump responses will contain the number of actions + * being dumped stored in for user app's consumption in TCA_ROOT_COUNT + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) + +/* New extended info filters for IFLA_EXT_MASK */ +#define RTEXT_FILTER_VF (1 << 0) +#define RTEXT_FILTER_BRVLAN (1 << 1) +#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2) +#define RTEXT_FILTER_SKIP_STATS (1 << 3) +#define RTEXT_FILTER_MRP (1 << 4) + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h new file mode 100644 index 0000000..63f9cf7 --- /dev/null +++ b/include/uapi/linux/sctp.h @@ -0,0 +1,1203 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* SCTP kernel implementation + * (C) Copyright IBM Corp. 2001, 2004 + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2002 Intel Corp. + * + * This file is part of the SCTP kernel implementation + * + * This header represents the structures and constants needed to support + * the SCTP Extension to the Sockets API. + * + * This SCTP implementation 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 SCTP implementation 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 GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * R. Stewart + * K. Morneau + * Q. Xie + * Karl Knutson + * Jon Grimm + * Daisy Chang + * Ryan Layer + * Ardelle Fan + * Sridhar Samudrala + * Inaky Perez-Gonzalez + * Vlad Yasevich + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef _SCTP_H +#define _SCTP_H + +#include +#include + +typedef __s32 sctp_assoc_t; + +#define SCTP_FUTURE_ASSOC 0 +#define SCTP_CURRENT_ASSOC 1 +#define SCTP_ALL_ASSOC 2 + +/* The following symbols come from the Sockets API Extensions for + * SCTP . + */ +#define SCTP_RTOINFO 0 +#define SCTP_ASSOCINFO 1 +#define SCTP_INITMSG 2 +#define SCTP_NODELAY 3 /* Get/set nodelay option. */ +#define SCTP_AUTOCLOSE 4 +#define SCTP_SET_PEER_PRIMARY_ADDR 5 +#define SCTP_PRIMARY_ADDR 6 +#define SCTP_ADAPTATION_LAYER 7 +#define SCTP_DISABLE_FRAGMENTS 8 +#define SCTP_PEER_ADDR_PARAMS 9 +#define SCTP_DEFAULT_SEND_PARAM 10 +#define SCTP_EVENTS 11 +#define SCTP_I_WANT_MAPPED_V4_ADDR 12 /* Turn on/off mapped v4 addresses */ +#define SCTP_MAXSEG 13 /* Get/set maximum fragment. */ +#define SCTP_STATUS 14 +#define SCTP_GET_PEER_ADDR_INFO 15 +#define SCTP_DELAYED_ACK_TIME 16 +#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME +#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME +#define SCTP_CONTEXT 17 +#define SCTP_FRAGMENT_INTERLEAVE 18 +#define SCTP_PARTIAL_DELIVERY_POINT 19 /* Set/Get partial delivery point */ +#define SCTP_MAX_BURST 20 /* Set/Get max burst */ +#define SCTP_AUTH_CHUNK 21 /* Set only: add a chunk type to authenticate */ +#define SCTP_HMAC_IDENT 22 +#define SCTP_AUTH_KEY 23 +#define SCTP_AUTH_ACTIVE_KEY 24 +#define SCTP_AUTH_DELETE_KEY 25 +#define SCTP_PEER_AUTH_CHUNKS 26 /* Read only */ +#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */ +#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ +#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ +#define SCTP_AUTO_ASCONF 30 +#define SCTP_PEER_ADDR_THLDS 31 +#define SCTP_RECVRCVINFO 32 +#define SCTP_RECVNXTINFO 33 +#define SCTP_DEFAULT_SNDINFO 34 +#define SCTP_AUTH_DEACTIVATE_KEY 35 +#define SCTP_REUSE_PORT 36 +#define SCTP_PEER_ADDR_THLDS_V2 37 + +/* Internal Socket Options. Some of the sctp library functions are + * implemented using these socket options. + */ +#define SCTP_SOCKOPT_BINDX_ADD 100 /* BINDX requests for adding addrs */ +#define SCTP_SOCKOPT_BINDX_REM 101 /* BINDX requests for removing addrs. */ +#define SCTP_SOCKOPT_PEELOFF 102 /* peel off association. */ +/* Options 104-106 are deprecated and removed. Do not use this space */ +#define SCTP_SOCKOPT_CONNECTX_OLD 107 /* CONNECTX old requests. */ +#define SCTP_GET_PEER_ADDRS 108 /* Get all peer address. */ +#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local address. */ +#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */ +#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */ +#define SCTP_GET_ASSOC_STATS 112 /* Read only */ +#define SCTP_PR_SUPPORTED 113 +#define SCTP_DEFAULT_PRINFO 114 +#define SCTP_PR_ASSOC_STATUS 115 +#define SCTP_PR_STREAM_STATUS 116 +#define SCTP_RECONFIG_SUPPORTED 117 +#define SCTP_ENABLE_STREAM_RESET 118 +#define SCTP_RESET_STREAMS 119 +#define SCTP_RESET_ASSOC 120 +#define SCTP_ADD_STREAMS 121 +#define SCTP_SOCKOPT_PEELOFF_FLAGS 122 +#define SCTP_STREAM_SCHEDULER 123 +#define SCTP_STREAM_SCHEDULER_VALUE 124 +#define SCTP_INTERLEAVING_SUPPORTED 125 +#define SCTP_SENDMSG_CONNECT 126 +#define SCTP_EVENT 127 +#define SCTP_ASCONF_SUPPORTED 128 +#define SCTP_AUTH_SUPPORTED 129 +#define SCTP_ECN_SUPPORTED 130 +#define SCTP_EXPOSE_POTENTIALLY_FAILED_STATE 131 +#define SCTP_EXPOSE_PF_STATE SCTP_EXPOSE_POTENTIALLY_FAILED_STATE + +/* PR-SCTP policies */ +#define SCTP_PR_SCTP_NONE 0x0000 +#define SCTP_PR_SCTP_TTL 0x0010 +#define SCTP_PR_SCTP_RTX 0x0020 +#define SCTP_PR_SCTP_PRIO 0x0030 +#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_PRIO +#define SCTP_PR_SCTP_MASK 0x0030 + +#define __SCTP_PR_INDEX(x) ((x >> 4) - 1) +#define SCTP_PR_INDEX(x) __SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x) + +#define SCTP_PR_POLICY(x) ((x) & SCTP_PR_SCTP_MASK) +#define SCTP_PR_SET_POLICY(flags, x) \ + do { \ + flags &= ~SCTP_PR_SCTP_MASK; \ + flags |= x; \ + } while (0) + +#define SCTP_PR_TTL_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL) +#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX) +#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO) + +/* For enable stream reset */ +#define SCTP_ENABLE_RESET_STREAM_REQ 0x01 +#define SCTP_ENABLE_RESET_ASSOC_REQ 0x02 +#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x04 +#define SCTP_ENABLE_STRRESET_MASK 0x07 + +#define SCTP_STREAM_RESET_INCOMING 0x01 +#define SCTP_STREAM_RESET_OUTGOING 0x02 + +/* These are bit fields for msghdr->msg_flags. See section 5.1. */ +/* On user space Linux, these live in as an enum. */ +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + +/* 5.3.1 SCTP Initiation Structure (SCTP_INIT) + * + * This cmsghdr structure provides information for initializing new + * SCTP associations with sendmsg(). The SCTP_INITMSG socket option + * uses this same data structure. This structure is not used for + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg + */ +struct sctp_initmsg { + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + __u16 sinit_max_attempts; + __u16 sinit_max_init_timeo; +}; + +/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * This cmsghdr structure specifies SCTP options for sendmsg() and + * describes SCTP header information about a received message through + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo + */ +struct sctp_sndrcvinfo { + __u16 sinfo_stream; + __u16 sinfo_ssn; + __u16 sinfo_flags; + __u32 sinfo_ppid; + __u32 sinfo_context; + __u32 sinfo_timetolive; + __u32 sinfo_tsn; + __u32 sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + +/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo + */ +struct sctp_sndinfo { + __u16 snd_sid; + __u16 snd_flags; + __u32 snd_ppid; + __u32 snd_context; + sctp_assoc_t snd_assoc_id; +}; + +/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO) + * + * This cmsghdr structure describes SCTP receive information + * about a received message through recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_RCVINFO struct sctp_rcvinfo + */ +struct sctp_rcvinfo { + __u16 rcv_sid; + __u16 rcv_ssn; + __u16 rcv_flags; + __u32 rcv_ppid; + __u32 rcv_tsn; + __u32 rcv_cumtsn; + __u32 rcv_context; + sctp_assoc_t rcv_assoc_id; +}; + +/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO) + * + * This cmsghdr structure describes SCTP receive information + * of the next message that will be delivered through recvmsg() + * if this information is already available when delivering + * the current message. + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_NXTINFO struct sctp_nxtinfo + */ +struct sctp_nxtinfo { + __u16 nxt_sid; + __u16 nxt_flags; + __u32 nxt_ppid; + __u32 nxt_length; + sctp_assoc_t nxt_assoc_id; +}; + +/* 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_PRINFO struct sctp_prinfo + */ +struct sctp_prinfo { + __u16 pr_policy; + __u32 pr_value; +}; + +/* 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_AUTHINFO struct sctp_authinfo + */ +struct sctp_authinfo { + __u16 auth_keynumber; +}; + +/* + * sinfo_flags: 16 bits (unsigned integer) + * + * This field may contain any of the following flags and is composed of + * a bitwise OR of these values. + */ +enum sctp_sinfo_flags { + SCTP_UNORDERED = (1 << 0), /* Send/receive message unordered. */ + SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */ + SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */ + SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */ + /* 2 bits here have been used by SCTP_PR_SCTP_MASK */ + SCTP_SENDALL = (1 << 6), + SCTP_PR_SCTP_ALL = (1 << 7), + SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */ + SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */ +}; + +typedef union { + __u8 raw; + struct sctp_initmsg init; + struct sctp_sndrcvinfo sndrcv; +} sctp_cmsg_data_t; + +/* These are cmsg_types. */ +typedef enum sctp_cmsg_type { + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ +#define SCTP_INIT SCTP_INIT + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ +#define SCTP_SNDRCV SCTP_SNDRCV + SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */ +#define SCTP_SNDINFO SCTP_SNDINFO + SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */ +#define SCTP_RCVINFO SCTP_RCVINFO + SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */ +#define SCTP_NXTINFO SCTP_NXTINFO + SCTP_PRINFO, /* 5.3.7 SCTP PR-SCTP Information Structure */ +#define SCTP_PRINFO SCTP_PRINFO + SCTP_AUTHINFO, /* 5.3.8 SCTP AUTH Information Structure */ +#define SCTP_AUTHINFO SCTP_AUTHINFO + SCTP_DSTADDRV4, /* 5.3.9 SCTP Destination IPv4 Address Structure */ +#define SCTP_DSTADDRV4 SCTP_DSTADDRV4 + SCTP_DSTADDRV6, /* 5.3.10 SCTP Destination IPv6 Address Structure */ +#define SCTP_DSTADDRV6 SCTP_DSTADDRV6 +} sctp_cmsg_t; + +/* + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * Communication notifications inform the ULP that an SCTP association + * has either begun or ended. The identifier for a new association is + * provided by this notificaion. The notification information has the + * following format: + * + */ +struct sctp_assoc_change { + __u16 sac_type; + __u16 sac_flags; + __u32 sac_length; + __u16 sac_state; + __u16 sac_error; + __u16 sac_outbound_streams; + __u16 sac_inbound_streams; + sctp_assoc_t sac_assoc_id; + __u8 sac_info[0]; +}; + +/* + * sac_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the association. They include: + * + * Note: The following state names deviate from the API draft as + * the names clash too easily with other kernel symbols. + */ +enum sctp_sac_state { + SCTP_COMM_UP, + SCTP_COMM_LOST, + SCTP_RESTART, + SCTP_SHUTDOWN_COMP, + SCTP_CANT_STR_ASSOC, +}; + +/* + * 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * When a destination address on a multi-homed peer encounters a change + * an interface details event is sent. The information has the + * following structure: + */ +struct sctp_paddr_change { + __u16 spc_type; + __u16 spc_flags; + __u32 spc_length; + struct sockaddr_storage spc_aaddr; + int spc_state; + int spc_error; + sctp_assoc_t spc_assoc_id; +} __attribute__((packed, aligned(4))); + +/* + * spc_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the address. They include: + */ +enum sctp_spc_state { + SCTP_ADDR_AVAILABLE, + SCTP_ADDR_UNREACHABLE, + SCTP_ADDR_REMOVED, + SCTP_ADDR_ADDED, + SCTP_ADDR_MADE_PRIM, + SCTP_ADDR_CONFIRMED, + SCTP_ADDR_POTENTIALLY_FAILED, +#define SCTP_ADDR_PF SCTP_ADDR_POTENTIALLY_FAILED +}; + + +/* + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * A remote peer may send an Operational Error message to its peer. + * This message indicates a variety of error conditions on an + * association. The entire error TLV as it appears on the wire is + * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP + * specification [SCTP] and any extensions for a list of possible + * error formats. SCTP error TLVs have the format: + */ +struct sctp_remote_error { + __u16 sre_type; + __u16 sre_flags; + __u32 sre_length; + __be16 sre_error; + sctp_assoc_t sre_assoc_id; + __u8 sre_data[0]; +}; + + +/* + * 5.3.1.4 SCTP_SEND_FAILED + * + * If SCTP cannot deliver a message it may return the message as a + * notification. + */ +struct sctp_send_failed { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndrcvinfo ssf_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +struct sctp_send_failed_event { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndinfo ssfe_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +/* + * ssf_flags: 16 bits (unsigned integer) + * + * The flag value will take one of the following values + * + * SCTP_DATA_UNSENT - Indicates that the data was never put on + * the wire. + * + * SCTP_DATA_SENT - Indicates that the data was put on the wire. + * Note that this does not necessarily mean that the + * data was (or was not) successfully delivered. + */ +enum sctp_ssf_flags { + SCTP_DATA_UNSENT, + SCTP_DATA_SENT, +}; + +/* + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * When a peer sends a SHUTDOWN, SCTP delivers this notification to + * inform the application that it should cease sending data. + */ +struct sctp_shutdown_event { + __u16 sse_type; + __u16 sse_flags; + __u32 sse_length; + sctp_assoc_t sse_assoc_id; +}; + +/* + * 5.3.1.6 SCTP_ADAPTATION_INDICATION + * + * When a peer sends a Adaptation Layer Indication parameter , SCTP + * delivers this notification to inform the application + * that of the peers requested adaptation layer. + */ +struct sctp_adaptation_event { + __u16 sai_type; + __u16 sai_flags; + __u32 sai_length; + __u32 sai_adaptation_ind; + sctp_assoc_t sai_assoc_id; +}; + +/* + * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT + * + * When a receiver is engaged in a partial delivery of a + * message this notification will be used to indicate + * various events. + */ +struct sctp_pdapi_event { + __u16 pdapi_type; + __u16 pdapi_flags; + __u32 pdapi_length; + __u32 pdapi_indication; + sctp_assoc_t pdapi_assoc_id; + __u32 pdapi_stream; + __u32 pdapi_seq; +}; + +enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, }; + +/* + * 5.3.1.8. SCTP_AUTHENTICATION_EVENT + * + * When a receiver is using authentication this message will provide + * notifications regarding new keys being made active as well as errors. + */ +struct sctp_authkey_event { + __u16 auth_type; + __u16 auth_flags; + __u32 auth_length; + __u16 auth_keynumber; + __u16 auth_altkeynumber; + __u32 auth_indication; + sctp_assoc_t auth_assoc_id; +}; + +enum { + SCTP_AUTH_NEW_KEY, +#define SCTP_AUTH_NEWKEY SCTP_AUTH_NEW_KEY /* compatible with before */ + SCTP_AUTH_FREE_KEY, + SCTP_AUTH_NO_AUTH, +}; + +/* + * 6.1.9. SCTP_SENDER_DRY_EVENT + * + * When the SCTP stack has no more user data to send or retransmit, this + * notification is given to the user. Also, at the time when a user app + * subscribes to this event, if there is no data to be sent or + * retransmit, the stack will immediately send up this notification. + */ +struct sctp_sender_dry_event { + __u16 sender_dry_type; + __u16 sender_dry_flags; + __u32 sender_dry_length; + sctp_assoc_t sender_dry_assoc_id; +}; + +#define SCTP_STREAM_RESET_INCOMING_SSN 0x0001 +#define SCTP_STREAM_RESET_OUTGOING_SSN 0x0002 +#define SCTP_STREAM_RESET_DENIED 0x0004 +#define SCTP_STREAM_RESET_FAILED 0x0008 +struct sctp_stream_reset_event { + __u16 strreset_type; + __u16 strreset_flags; + __u32 strreset_length; + sctp_assoc_t strreset_assoc_id; + __u16 strreset_stream_list[]; +}; + +#define SCTP_ASSOC_RESET_DENIED 0x0004 +#define SCTP_ASSOC_RESET_FAILED 0x0008 +struct sctp_assoc_reset_event { + __u16 assocreset_type; + __u16 assocreset_flags; + __u32 assocreset_length; + sctp_assoc_t assocreset_assoc_id; + __u32 assocreset_local_tsn; + __u32 assocreset_remote_tsn; +}; + +#define SCTP_ASSOC_CHANGE_DENIED 0x0004 +#define SCTP_ASSOC_CHANGE_FAILED 0x0008 +#define SCTP_STREAM_CHANGE_DENIED SCTP_ASSOC_CHANGE_DENIED +#define SCTP_STREAM_CHANGE_FAILED SCTP_ASSOC_CHANGE_FAILED +struct sctp_stream_change_event { + __u16 strchange_type; + __u16 strchange_flags; + __u32 strchange_length; + sctp_assoc_t strchange_assoc_id; + __u16 strchange_instrms; + __u16 strchange_outstrms; +}; + +/* + * Described in Section 7.3 + * Ancillary Data and Notification Interest Options + */ +struct sctp_event_subscribe { + __u8 sctp_data_io_event; + __u8 sctp_association_event; + __u8 sctp_address_event; + __u8 sctp_send_failure_event; + __u8 sctp_peer_error_event; + __u8 sctp_shutdown_event; + __u8 sctp_partial_delivery_event; + __u8 sctp_adaptation_layer_event; + __u8 sctp_authentication_event; + __u8 sctp_sender_dry_event; + __u8 sctp_stream_reset_event; + __u8 sctp_assoc_reset_event; + __u8 sctp_stream_change_event; + __u8 sctp_send_failure_event_event; +}; + +/* + * 5.3.1 SCTP Notification Structure + * + * The notification structure is defined as the union of all + * notification types. + * + */ +union sctp_notification { + struct { + __u16 sn_type; /* Notification type. */ + __u16 sn_flags; + __u32 sn_length; + } sn_header; + struct sctp_assoc_change sn_assoc_change; + struct sctp_paddr_change sn_paddr_change; + struct sctp_remote_error sn_remote_error; + struct sctp_send_failed sn_send_failed; + struct sctp_shutdown_event sn_shutdown_event; + struct sctp_adaptation_event sn_adaptation_event; + struct sctp_pdapi_event sn_pdapi_event; + struct sctp_authkey_event sn_authkey_event; + struct sctp_sender_dry_event sn_sender_dry_event; + struct sctp_stream_reset_event sn_strreset_event; + struct sctp_assoc_reset_event sn_assocreset_event; + struct sctp_stream_change_event sn_strchange_event; + struct sctp_send_failed_event sn_send_failed_event; +}; + +/* Section 5.3.1 + * All standard values for sn_type flags are greater than 2^15. + * Values from 2^15 and down are reserved. + */ + +enum sctp_sn_type { + SCTP_SN_TYPE_BASE = (1<<15), + SCTP_DATA_IO_EVENT = SCTP_SN_TYPE_BASE, +#define SCTP_DATA_IO_EVENT SCTP_DATA_IO_EVENT + SCTP_ASSOC_CHANGE, +#define SCTP_ASSOC_CHANGE SCTP_ASSOC_CHANGE + SCTP_PEER_ADDR_CHANGE, +#define SCTP_PEER_ADDR_CHANGE SCTP_PEER_ADDR_CHANGE + SCTP_SEND_FAILED, +#define SCTP_SEND_FAILED SCTP_SEND_FAILED + SCTP_REMOTE_ERROR, +#define SCTP_REMOTE_ERROR SCTP_REMOTE_ERROR + SCTP_SHUTDOWN_EVENT, +#define SCTP_SHUTDOWN_EVENT SCTP_SHUTDOWN_EVENT + SCTP_PARTIAL_DELIVERY_EVENT, +#define SCTP_PARTIAL_DELIVERY_EVENT SCTP_PARTIAL_DELIVERY_EVENT + SCTP_ADAPTATION_INDICATION, +#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTATION_INDICATION + SCTP_AUTHENTICATION_EVENT, +#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT + SCTP_SENDER_DRY_EVENT, +#define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT + SCTP_STREAM_RESET_EVENT, +#define SCTP_STREAM_RESET_EVENT SCTP_STREAM_RESET_EVENT + SCTP_ASSOC_RESET_EVENT, +#define SCTP_ASSOC_RESET_EVENT SCTP_ASSOC_RESET_EVENT + SCTP_STREAM_CHANGE_EVENT, +#define SCTP_STREAM_CHANGE_EVENT SCTP_STREAM_CHANGE_EVENT + SCTP_SEND_FAILED_EVENT, +#define SCTP_SEND_FAILED_EVENT SCTP_SEND_FAILED_EVENT + SCTP_SN_TYPE_MAX = SCTP_SEND_FAILED_EVENT, +#define SCTP_SN_TYPE_MAX SCTP_SN_TYPE_MAX +}; + +/* Notification error codes used to fill up the error fields in some + * notifications. + * SCTP_PEER_ADDRESS_CHAGE : spc_error + * SCTP_ASSOC_CHANGE : sac_error + * These names should be potentially included in the draft 04 of the SCTP + * sockets API specification. + */ +typedef enum sctp_sn_error { + SCTP_FAILED_THRESHOLD, + SCTP_RECEIVED_SACK, + SCTP_HEARTBEAT_SUCCESS, + SCTP_RESPONSE_TO_USER_REQ, + SCTP_INTERNAL_ERROR, + SCTP_SHUTDOWN_GUARD_EXPIRES, + SCTP_PEER_FAULTY, +} sctp_sn_error_t; + +/* + * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO) + * + * The protocol parameters used to initialize and bound retransmission + * timeout (RTO) are tunable. See [SCTP] for more information on how + * these parameters are used in RTO calculation. + */ +struct sctp_rtoinfo { + sctp_assoc_t srto_assoc_id; + __u32 srto_initial; + __u32 srto_max; + __u32 srto_min; +}; + +/* + * 7.1.2 Association Parameters (SCTP_ASSOCINFO) + * + * This option is used to both examine and set various association and + * endpoint parameters. + */ +struct sctp_assocparams { + sctp_assoc_t sasoc_assoc_id; + __u16 sasoc_asocmaxrxt; + __u16 sasoc_number_peer_destinations; + __u32 sasoc_peer_rwnd; + __u32 sasoc_local_rwnd; + __u32 sasoc_cookie_life; +}; + +/* + * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the peer mark the enclosed address as the association + * primary. The enclosed address must be one of the association's + * locally bound addresses. The following structure is used to make a + * set primary request: + */ +struct sctp_setpeerprim { + sctp_assoc_t sspp_assoc_id; + struct sockaddr_storage sspp_addr; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. The following structure is used to + * make a set peer primary request: + */ +struct sctp_prim { + sctp_assoc_t ssp_assoc_id; + struct sockaddr_storage ssp_addr; +} __attribute__((packed, aligned(4))); + +/* For backward compatibility use, define the old name too */ +#define sctp_setprim sctp_prim + +/* + * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER) + * + * Requests that the local endpoint set the specified Adaptation Layer + * Indication parameter for all future INIT and INIT-ACK exchanges. + */ +struct sctp_setadaptation { + __u32 ssb_adaptation_ind; +}; + +/* + * 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) + * + * Applications can enable or disable heartbeats for any peer address + * of an association, modify an address's heartbeat interval, force a + * heartbeat to be sent immediately, and adjust the address's maximum + * number of retransmissions sent before an address is considered + * unreachable. The following structure is used to access and modify an + * address's parameters: + */ +enum sctp_spp_flags { + SPP_HB_ENABLE = 1<<0, /*Enable heartbeats*/ + SPP_HB_DISABLE = 1<<1, /*Disable heartbeats*/ + SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE, + SPP_HB_DEMAND = 1<<2, /*Send heartbeat immediately*/ + SPP_PMTUD_ENABLE = 1<<3, /*Enable PMTU discovery*/ + SPP_PMTUD_DISABLE = 1<<4, /*Disable PMTU discovery*/ + SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE, + SPP_SACKDELAY_ENABLE = 1<<5, /*Enable SACK*/ + SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/ + SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE, + SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */ + SPP_IPV6_FLOWLABEL = 1<<8, + SPP_DSCP = 1<<9, +}; + +struct sctp_paddrparams { + sctp_assoc_t spp_assoc_id; + struct sockaddr_storage spp_address; + __u32 spp_hbinterval; + __u16 spp_pathmaxrxt; + __u32 spp_pathmtu; + __u32 spp_sackdelay; + __u32 spp_flags; + __u32 spp_ipv6_flowlabel; + __u8 spp_dscp; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK) + * + * This set option adds a chunk type that the user is requesting to be + * received only in an authenticated way. Changes to the list of chunks + * will only effect future associations on the socket. + */ +struct sctp_authchunk { + __u8 sauth_chunk; +}; + +/* + * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT) + * + * This option gets or sets the list of HMAC algorithms that the local + * endpoint requires the peer to use. + */ +/* This here is only used by user space as is. It might not be a good idea + * to export/reveal the whole structure with reserved fields etc. + */ +enum { + SCTP_AUTH_HMAC_ID_SHA1 = 1, + SCTP_AUTH_HMAC_ID_SHA256 = 3, +}; + +struct sctp_hmacalgo { + __u32 shmac_num_idents; + __u16 shmac_idents[]; +}; + +/* Sadly, user and kernel space have different names for + * this structure member, so this is to not break anything. + */ +#define shmac_number_of_idents shmac_num_idents + +/* + * 7.1.20. Set a shared key (SCTP_AUTH_KEY) + * + * This option will set a shared secret key which is used to build an + * association shared key. + */ +struct sctp_authkey { + sctp_assoc_t sca_assoc_id; + __u16 sca_keynumber; + __u16 sca_keylength; + __u8 sca_key[]; +}; + +/* + * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY) + * + * This option will get or set the active shared key to be used to build + * the association shared key. + */ + +struct sctp_authkeyid { + sctp_assoc_t scact_assoc_id; + __u16 scact_keynumber; +}; + + +/* + * 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK) + * + * This option will effect the way delayed acks are performed. This + * option allows you to get or set the delayed ack time, in + * milliseconds. It also allows changing the delayed ack frequency. + * Changing the frequency to 1 disables the delayed sack algorithm. If + * the assoc_id is 0, then this sets or gets the endpoints default + * values. If the assoc_id field is non-zero, then the set or get + * effects the specified association for the one to many model (the + * assoc_id field is ignored by the one to one model). Note that if + * sack_delay or sack_freq are 0 when setting this option, then the + * current values will remain unchanged. + */ +struct sctp_sack_info { + sctp_assoc_t sack_assoc_id; + uint32_t sack_delay; + uint32_t sack_freq; +}; + +struct sctp_assoc_value { + sctp_assoc_t assoc_id; + uint32_t assoc_value; +}; + +struct sctp_stream_value { + sctp_assoc_t assoc_id; + uint16_t stream_id; + uint16_t stream_value; +}; + +/* + * 7.2.2 Peer Address Information + * + * Applications can retrieve information about a specific peer address + * of an association, including its reachability state, congestion + * window, and retransmission timer values. This information is + * read-only. The following structure is used to access this + * information: + */ +struct sctp_paddrinfo { + sctp_assoc_t spinfo_assoc_id; + struct sockaddr_storage spinfo_address; + __s32 spinfo_state; + __u32 spinfo_cwnd; + __u32 spinfo_srtt; + __u32 spinfo_rto; + __u32 spinfo_mtu; +} __attribute__((packed, aligned(4))); + +/* Peer addresses's state. */ +/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x] + * calls. + * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters. + * Not yet confirmed by a heartbeat and not available for data + * transfers. + * ACTIVE : Peer address confirmed, active and available for data transfers. + * INACTIVE: Peer address inactive and not available for data transfers. + */ +enum sctp_spinfo_state { + SCTP_INACTIVE, + SCTP_PF, +#define SCTP_POTENTIALLY_FAILED SCTP_PF + SCTP_ACTIVE, + SCTP_UNCONFIRMED, + SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ +}; + +/* + * 7.2.1 Association Status (SCTP_STATUS) + * + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. The following structure is + * used to access this information: + */ +struct sctp_status { + sctp_assoc_t sstat_assoc_id; + __s32 sstat_state; + __u32 sstat_rwnd; + __u16 sstat_unackdata; + __u16 sstat_penddata; + __u16 sstat_instrms; + __u16 sstat_outstrms; + __u32 sstat_fragmentation_point; + struct sctp_paddrinfo sstat_primary; +}; + +/* + * 7.2.3. Get the list of chunks the peer requires to be authenticated + * (SCTP_PEER_AUTH_CHUNKS) + * + * This option gets a list of chunks for a specified association that + * the peer requires to be received authenticated only. + */ +struct sctp_authchunks { + sctp_assoc_t gauth_assoc_id; + __u32 gauth_number_of_chunks; + uint8_t gauth_chunks[]; +}; + +/* The broken spelling has been released already in lksctp-tools header, + * so don't break anyone, now that it's fixed. + */ +#define guth_number_of_chunks gauth_number_of_chunks + +/* Association states. */ +enum sctp_sstat_state { + SCTP_EMPTY = 0, + SCTP_CLOSED = 1, + SCTP_COOKIE_WAIT = 2, + SCTP_COOKIE_ECHOED = 3, + SCTP_ESTABLISHED = 4, + SCTP_SHUTDOWN_PENDING = 5, + SCTP_SHUTDOWN_SENT = 6, + SCTP_SHUTDOWN_RECEIVED = 7, + SCTP_SHUTDOWN_ACK_SENT = 8, +}; + +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +struct sctp_assoc_ids { + __u32 gaids_number_of_ids; + sctp_assoc_t gaids_assoc_id[]; +}; + +/* + * 8.3, 8.5 get all peer/local addresses in an association. + * This parameter struct is used by SCTP_GET_PEER_ADDRS and + * SCTP_GET_LOCAL_ADDRS socket options used internally to implement + * sctp_getpaddrs() and sctp_getladdrs() API. + */ +struct sctp_getaddrs_old { + sctp_assoc_t assoc_id; + int addr_num; + struct sockaddr *addrs; +}; + +struct sctp_getaddrs { + sctp_assoc_t assoc_id; /*input*/ + __u32 addr_num; /*output*/ + __u8 addrs[0]; /*output, variable size*/ +}; + +/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves + * association stats. All stats are counts except sas_maxrto and + * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since + * the last call. Will return 0 when RTO was not update since last call + */ +struct sctp_assoc_stats { + sctp_assoc_t sas_assoc_id; /* Input */ + /* Transport of observed max RTO */ + struct sockaddr_storage sas_obs_rto_ipaddr; + __u64 sas_maxrto; /* Maximum Observed RTO for period */ + __u64 sas_isacks; /* SACKs received */ + __u64 sas_osacks; /* SACKs sent */ + __u64 sas_opackets; /* Packets sent */ + __u64 sas_ipackets; /* Packets received */ + __u64 sas_rtxchunks; /* Retransmitted Chunks */ + __u64 sas_outofseqtsns;/* TSN received > next expected */ + __u64 sas_idupchunks; /* Dups received (ordered+unordered) */ + __u64 sas_gapcnt; /* Gap Acknowledgements Received */ + __u64 sas_ouodchunks; /* Unordered data chunks sent */ + __u64 sas_iuodchunks; /* Unordered data chunks received */ + __u64 sas_oodchunks; /* Ordered data chunks sent */ + __u64 sas_iodchunks; /* Ordered data chunks received */ + __u64 sas_octrlchunks; /* Control chunks sent */ + __u64 sas_ictrlchunks; /* Control chunks received */ +}; + +/* + * 8.1 sctp_bindx() + * + * The flags parameter is formed from the bitwise OR of zero or more of the + * following currently defined flags: + */ +#define SCTP_BINDX_ADD_ADDR 0x01 +#define SCTP_BINDX_REM_ADDR 0x02 + +/* This is the structure that is passed as an argument(optval) to + * getsockopt(SCTP_SOCKOPT_PEELOFF). + */ +typedef struct { + sctp_assoc_t associd; + int sd; +} sctp_peeloff_arg_t; + +typedef struct { + sctp_peeloff_arg_t p_arg; + unsigned flags; +} sctp_peeloff_flags_arg_t; + +/* + * Peer Address Thresholds socket option + */ +struct sctp_paddrthlds { + sctp_assoc_t spt_assoc_id; + struct sockaddr_storage spt_address; + __u16 spt_pathmaxrxt; + __u16 spt_pathpfthld; +}; + +/* Use a new structure with spt_pathcpthld for back compatibility */ +struct sctp_paddrthlds_v2 { + sctp_assoc_t spt_assoc_id; + struct sockaddr_storage spt_address; + __u16 spt_pathmaxrxt; + __u16 spt_pathpfthld; + __u16 spt_pathcpthld; +}; + +/* + * Socket Option for Getting the Association/Stream-Specific PR-SCTP Status + */ +struct sctp_prstatus { + sctp_assoc_t sprstat_assoc_id; + __u16 sprstat_sid; + __u16 sprstat_policy; + __u64 sprstat_abandoned_unsent; + __u64 sprstat_abandoned_sent; +}; + +struct sctp_default_prinfo { + sctp_assoc_t pr_assoc_id; + __u32 pr_value; + __u16 pr_policy; +}; + +struct sctp_info { + __u32 sctpi_tag; + __u32 sctpi_state; + __u32 sctpi_rwnd; + __u16 sctpi_unackdata; + __u16 sctpi_penddata; + __u16 sctpi_instrms; + __u16 sctpi_outstrms; + __u32 sctpi_fragmentation_point; + __u32 sctpi_inqueue; + __u32 sctpi_outqueue; + __u32 sctpi_overall_error; + __u32 sctpi_max_burst; + __u32 sctpi_maxseg; + __u32 sctpi_peer_rwnd; + __u32 sctpi_peer_tag; + __u8 sctpi_peer_capable; + __u8 sctpi_peer_sack; + __u16 __reserved1; + + /* assoc status info */ + __u64 sctpi_isacks; + __u64 sctpi_osacks; + __u64 sctpi_opackets; + __u64 sctpi_ipackets; + __u64 sctpi_rtxchunks; + __u64 sctpi_outofseqtsns; + __u64 sctpi_idupchunks; + __u64 sctpi_gapcnt; + __u64 sctpi_ouodchunks; + __u64 sctpi_iuodchunks; + __u64 sctpi_oodchunks; + __u64 sctpi_iodchunks; + __u64 sctpi_octrlchunks; + __u64 sctpi_ictrlchunks; + + /* primary transport info */ + struct sockaddr_storage sctpi_p_address; + __s32 sctpi_p_state; + __u32 sctpi_p_cwnd; + __u32 sctpi_p_srtt; + __u32 sctpi_p_rto; + __u32 sctpi_p_hbinterval; + __u32 sctpi_p_pathmaxrxt; + __u32 sctpi_p_sackdelay; + __u32 sctpi_p_sackfreq; + __u32 sctpi_p_ssthresh; + __u32 sctpi_p_partial_bytes_acked; + __u32 sctpi_p_flight_size; + __u16 sctpi_p_error; + __u16 __reserved2; + + /* sctp sock info */ + __u32 sctpi_s_autoclose; + __u32 sctpi_s_adaptation_ind; + __u32 sctpi_s_pd_point; + __u8 sctpi_s_nodelay; + __u8 sctpi_s_disable_fragments; + __u8 sctpi_s_v4mapped; + __u8 sctpi_s_frag_interleave; + __u32 sctpi_s_type; + __u32 __reserved3; +}; + +struct sctp_reset_streams { + sctp_assoc_t srs_assoc_id; + uint16_t srs_flags; + uint16_t srs_number_streams; /* 0 == ALL */ + uint16_t srs_stream_list[]; /* list if srs_num_streams is not 0 */ +}; + +struct sctp_add_streams { + sctp_assoc_t sas_assoc_id; + uint16_t sas_instrms; + uint16_t sas_outstrms; +}; + +struct sctp_event { + sctp_assoc_t se_assoc_id; + uint16_t se_type; + uint8_t se_on; +}; + +/* SCTP Stream schedulers */ +enum sctp_sched_type { + SCTP_SS_FCFS, + SCTP_SS_DEFAULT = SCTP_SS_FCFS, + SCTP_SS_PRIO, + SCTP_SS_RR, + SCTP_SS_MAX = SCTP_SS_RR +}; + +#endif /* _SCTP_H */ diff --git a/include/uapi/linux/seg6.h b/include/uapi/linux/seg6.h new file mode 100644 index 0000000..329163e --- /dev/null +++ b/include/uapi/linux/seg6.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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_SEG6_H +#define _LINUX_SEG6_H + +#include +#include /* For struct in6_addr. */ + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; /* Represents the last_entry field of SRH */ + __u8 flags; + __u16 tag; + + struct in6_addr segments[0]; +}; + +#define SR6_FLAG1_PROTECTED (1 << 6) +#define SR6_FLAG1_OAM (1 << 5) +#define SR6_FLAG1_ALERT (1 << 4) +#define SR6_FLAG1_HMAC (1 << 3) + +#define SR6_TLV_INGRESS 1 +#define SR6_TLV_EGRESS 2 +#define SR6_TLV_OPAQUE 3 +#define SR6_TLV_PADDING 4 +#define SR6_TLV_HMAC 5 + +#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC) + +struct sr6_tlv { + __u8 type; + __u8 len; + __u8 data[0]; +}; + +#endif diff --git a/include/uapi/linux/seg6_genl.h b/include/uapi/linux/seg6_genl.h new file mode 100644 index 0000000..0c23052 --- /dev/null +++ b/include/uapi/linux/seg6_genl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_GENL_H +#define _LINUX_SEG6_GENL_H + +#define SEG6_GENL_NAME "SEG6" +#define SEG6_GENL_VERSION 0x1 + +enum { + SEG6_ATTR_UNSPEC, + SEG6_ATTR_DST, + SEG6_ATTR_DSTLEN, + SEG6_ATTR_HMACKEYID, + SEG6_ATTR_SECRET, + SEG6_ATTR_SECRETLEN, + SEG6_ATTR_ALGID, + SEG6_ATTR_HMACINFO, + __SEG6_ATTR_MAX, +}; + +#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) + +enum { + SEG6_CMD_UNSPEC, + SEG6_CMD_SETHMAC, + SEG6_CMD_DUMPHMAC, + SEG6_CMD_SET_TUNSRC, + SEG6_CMD_GET_TUNSRC, + __SEG6_CMD_MAX, +}; + +#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) + +#endif diff --git a/include/uapi/linux/seg6_hmac.h b/include/uapi/linux/seg6_hmac.h new file mode 100644 index 0000000..3fb3412 --- /dev/null +++ b/include/uapi/linux/seg6_hmac.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h new file mode 100644 index 0000000..ee6d1dd --- /dev/null +++ b/include/uapi/linux/seg6_iptunnel.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include /* For struct ipv6_sr_hdr. */ + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[0]; +}; + +#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3)) + +enum { + SEG6_IPTUN_MODE_INLINE, + SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, +}; + +#endif diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h new file mode 100644 index 0000000..5312de8 --- /dev/null +++ b/include/uapi/linux/seg6_local.h @@ -0,0 +1,80 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + SEG6_LOCAL_BPF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + /* custom BPF action */ + SEG6_LOCAL_ACTION_END_BPF = 15, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +enum { + SEG6_LOCAL_BPF_PROG_UNSPEC, + SEG6_LOCAL_BPF_PROG, + SEG6_LOCAL_BPF_PROG_NAME, + __SEG6_LOCAL_BPF_PROG_MAX, +}; + +#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1) + +#endif diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h new file mode 100644 index 0000000..f84e7bc --- /dev/null +++ b/include/uapi/linux/snmp.h @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Definitions for MIBs + * + * Author: Hideaki YOSHIFUJI + */ + +#ifndef _LINUX_SNMP_H +#define _LINUX_SNMP_H + +/* ipstats mib definitions */ +/* + * RFC 1213: MIB-II + * RFC 2011 (updates 1213): SNMPv2-MIB-IP + * RFC 2863: Interfaces Group MIB + * RFC 2465: IPv6 MIB: General Group + * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables + */ +enum +{ + IPSTATS_MIB_NUM = 0, +/* frequently written fields in fast path, kept in same cache line */ + IPSTATS_MIB_INPKTS, /* InReceives */ + IPSTATS_MIB_INOCTETS, /* InOctets */ + IPSTATS_MIB_INDELIVERS, /* InDelivers */ + IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */ + IPSTATS_MIB_OUTPKTS, /* OutRequests */ + IPSTATS_MIB_OUTOCTETS, /* OutOctets */ +/* other fields */ + IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */ + IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */ + IPSTATS_MIB_INNOROUTES, /* InNoRoutes */ + IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */ + IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */ + IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */ + IPSTATS_MIB_INDISCARDS, /* InDiscards */ + IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */ + IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */ + IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */ + IPSTATS_MIB_REASMREQDS, /* ReasmReqds */ + IPSTATS_MIB_REASMOKS, /* ReasmOKs */ + IPSTATS_MIB_REASMFAILS, /* ReasmFails */ + IPSTATS_MIB_FRAGOKS, /* FragOKs */ + IPSTATS_MIB_FRAGFAILS, /* FragFails */ + IPSTATS_MIB_FRAGCREATES, /* FragCreates */ + IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */ + IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */ + IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */ + IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */ + IPSTATS_MIB_INMCASTOCTETS, /* InMcastOctets */ + IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */ + IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ + IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */ + IPSTATS_MIB_CSUMERRORS, /* InCsumErrors */ + IPSTATS_MIB_NOECTPKTS, /* InNoECTPkts */ + IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */ + IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */ + IPSTATS_MIB_CEPKTS, /* InCEPkts */ + IPSTATS_MIB_REASM_OVERLAPS, /* ReasmOverlaps */ + __IPSTATS_MIB_MAX +}; + +/* icmp mib definitions */ +/* + * RFC 1213: MIB-II ICMP Group + * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group + */ +enum +{ + ICMP_MIB_NUM = 0, + ICMP_MIB_INMSGS, /* InMsgs */ + ICMP_MIB_INERRORS, /* InErrors */ + ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */ + ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */ + ICMP_MIB_INPARMPROBS, /* InParmProbs */ + ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */ + ICMP_MIB_INREDIRECTS, /* InRedirects */ + ICMP_MIB_INECHOS, /* InEchos */ + ICMP_MIB_INECHOREPS, /* InEchoReps */ + ICMP_MIB_INTIMESTAMPS, /* InTimestamps */ + ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */ + ICMP_MIB_INADDRMASKS, /* InAddrMasks */ + ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */ + ICMP_MIB_OUTMSGS, /* OutMsgs */ + ICMP_MIB_OUTERRORS, /* OutErrors */ + ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */ + ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */ + ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */ + ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */ + ICMP_MIB_OUTREDIRECTS, /* OutRedirects */ + ICMP_MIB_OUTECHOS, /* OutEchos */ + ICMP_MIB_OUTECHOREPS, /* OutEchoReps */ + ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */ + ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */ + ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ + ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ + ICMP_MIB_CSUMERRORS, /* InCsumErrors */ + __ICMP_MIB_MAX +}; + +#define __ICMPMSG_MIB_MAX 512 /* Out+In for all 8-bit ICMP types */ + +/* icmp6 mib definitions */ +/* + * RFC 2466: ICMPv6-MIB + */ +enum +{ + ICMP6_MIB_NUM = 0, + ICMP6_MIB_INMSGS, /* InMsgs */ + ICMP6_MIB_INERRORS, /* InErrors */ + ICMP6_MIB_OUTMSGS, /* OutMsgs */ + ICMP6_MIB_OUTERRORS, /* OutErrors */ + ICMP6_MIB_CSUMERRORS, /* InCsumErrors */ + __ICMP6_MIB_MAX +}; + +#define __ICMP6MSG_MIB_MAX 512 /* Out+In for all 8-bit ICMPv6 types */ + +/* tcp mib definitions */ +/* + * RFC 1213: MIB-II TCP group + * RFC 2012 (updates 1213): SNMPv2-MIB-TCP + */ +enum +{ + TCP_MIB_NUM = 0, + TCP_MIB_RTOALGORITHM, /* RtoAlgorithm */ + TCP_MIB_RTOMIN, /* RtoMin */ + TCP_MIB_RTOMAX, /* RtoMax */ + TCP_MIB_MAXCONN, /* MaxConn */ + TCP_MIB_ACTIVEOPENS, /* ActiveOpens */ + TCP_MIB_PASSIVEOPENS, /* PassiveOpens */ + TCP_MIB_ATTEMPTFAILS, /* AttemptFails */ + TCP_MIB_ESTABRESETS, /* EstabResets */ + TCP_MIB_CURRESTAB, /* CurrEstab */ + TCP_MIB_INSEGS, /* InSegs */ + TCP_MIB_OUTSEGS, /* OutSegs */ + TCP_MIB_RETRANSSEGS, /* RetransSegs */ + TCP_MIB_INERRS, /* InErrs */ + TCP_MIB_OUTRSTS, /* OutRsts */ + TCP_MIB_CSUMERRORS, /* InCsumErrors */ + __TCP_MIB_MAX +}; + +/* udp mib definitions */ +/* + * RFC 1213: MIB-II UDP group + * RFC 2013 (updates 1213): SNMPv2-MIB-UDP + */ +enum +{ + UDP_MIB_NUM = 0, + UDP_MIB_INDATAGRAMS, /* InDatagrams */ + UDP_MIB_NOPORTS, /* NoPorts */ + UDP_MIB_INERRORS, /* InErrors */ + UDP_MIB_OUTDATAGRAMS, /* OutDatagrams */ + UDP_MIB_RCVBUFERRORS, /* RcvbufErrors */ + UDP_MIB_SNDBUFERRORS, /* SndbufErrors */ + UDP_MIB_CSUMERRORS, /* InCsumErrors */ + UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */ + __UDP_MIB_MAX +}; + +/* linux mib definitions */ +enum +{ + LINUX_MIB_NUM = 0, + LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */ + LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */ + LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */ + LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */ + LINUX_MIB_PRUNECALLED, /* PruneCalled */ + LINUX_MIB_RCVPRUNED, /* RcvPruned */ + LINUX_MIB_OFOPRUNED, /* OfoPruned */ + LINUX_MIB_OUTOFWINDOWICMPS, /* OutOfWindowIcmps */ + LINUX_MIB_LOCKDROPPEDICMPS, /* LockDroppedIcmps */ + LINUX_MIB_ARPFILTER, /* ArpFilter */ + LINUX_MIB_TIMEWAITED, /* TimeWaited */ + LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */ + LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */ + LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */ + LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */ + LINUX_MIB_DELAYEDACKS, /* DelayedACKs */ + LINUX_MIB_DELAYEDACKLOCKED, /* DelayedACKLocked */ + LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */ + LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */ + LINUX_MIB_LISTENDROPS, /* ListenDrops */ + LINUX_MIB_TCPHPHITS, /* TCPHPHits */ + LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */ + LINUX_MIB_TCPHPACKS, /* TCPHPAcks */ + LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */ + LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */ + LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */ + LINUX_MIB_TCPSACKREORDER, /* TCPSACKReorder */ + LINUX_MIB_TCPRENOREORDER, /* TCPRenoReorder */ + LINUX_MIB_TCPTSREORDER, /* TCPTSReorder */ + LINUX_MIB_TCPFULLUNDO, /* TCPFullUndo */ + LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */ + LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */ + LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */ + LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */ + LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */ + LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */ + LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */ + LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */ + LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ + LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ + LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ + LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */ + LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ + LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ + LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */ + LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */ + LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */ + LINUX_MIB_TCPDSACKRECV, /* TCPDSACKRecv */ + LINUX_MIB_TCPDSACKOFORECV, /* TCPDSACKOfoRecv */ + LINUX_MIB_TCPABORTONDATA, /* TCPAbortOnData */ + LINUX_MIB_TCPABORTONCLOSE, /* TCPAbortOnClose */ + LINUX_MIB_TCPABORTONMEMORY, /* TCPAbortOnMemory */ + LINUX_MIB_TCPABORTONTIMEOUT, /* TCPAbortOnTimeout */ + LINUX_MIB_TCPABORTONLINGER, /* TCPAbortOnLinger */ + LINUX_MIB_TCPABORTFAILED, /* TCPAbortFailed */ + LINUX_MIB_TCPMEMORYPRESSURES, /* TCPMemoryPressures */ + LINUX_MIB_TCPMEMORYPRESSURESCHRONO, /* TCPMemoryPressuresChrono */ + LINUX_MIB_TCPSACKDISCARD, /* TCPSACKDiscard */ + LINUX_MIB_TCPDSACKIGNOREDOLD, /* TCPSACKIgnoredOld */ + LINUX_MIB_TCPDSACKIGNOREDNOUNDO, /* TCPSACKIgnoredNoUndo */ + LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */ + LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */ + LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */ + LINUX_MIB_TCPMD5FAILURE, /* TCPMD5Failure */ + LINUX_MIB_SACKSHIFTED, + LINUX_MIB_SACKMERGED, + LINUX_MIB_SACKSHIFTFALLBACK, + LINUX_MIB_TCPBACKLOGDROP, + LINUX_MIB_PFMEMALLOCDROP, + LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */ + LINUX_MIB_TCPDEFERACCEPTDROP, + LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */ + LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */ + LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */ + LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */ + LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */ + LINUX_MIB_TCPRCVCOALESCE, /* TCPRcvCoalesce */ + LINUX_MIB_TCPBACKLOGCOALESCE, /* TCPBacklogCoalesce */ + LINUX_MIB_TCPOFOQUEUE, /* TCPOFOQueue */ + LINUX_MIB_TCPOFODROP, /* TCPOFODrop */ + LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */ + LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */ + LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */ + LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */ + LINUX_MIB_TCPFASTOPENACTIVEFAIL, /* TCPFastOpenActiveFail */ + LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/ + LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */ + LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ + LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ + LINUX_MIB_TCPFASTOPENBLACKHOLE, /* TCPFastOpenBlackholeDetect */ + LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ + LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ + LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */ + LINUX_MIB_TCPFROMZEROWINDOWADV, /* TCPFromZeroWindowAdv */ + LINUX_MIB_TCPTOZEROWINDOWADV, /* TCPToZeroWindowAdv */ + LINUX_MIB_TCPWANTZEROWINDOWADV, /* TCPWantZeroWindowAdv */ + LINUX_MIB_TCPSYNRETRANS, /* TCPSynRetrans */ + LINUX_MIB_TCPORIGDATASENT, /* TCPOrigDataSent */ + LINUX_MIB_TCPHYSTARTTRAINDETECT, /* TCPHystartTrainDetect */ + LINUX_MIB_TCPHYSTARTTRAINCWND, /* TCPHystartTrainCwnd */ + LINUX_MIB_TCPHYSTARTDELAYDETECT, /* TCPHystartDelayDetect */ + LINUX_MIB_TCPHYSTARTDELAYCWND, /* TCPHystartDelayCwnd */ + LINUX_MIB_TCPACKSKIPPEDSYNRECV, /* TCPACKSkippedSynRecv */ + LINUX_MIB_TCPACKSKIPPEDPAWS, /* TCPACKSkippedPAWS */ + LINUX_MIB_TCPACKSKIPPEDSEQ, /* TCPACKSkippedSeq */ + LINUX_MIB_TCPACKSKIPPEDFINWAIT2, /* TCPACKSkippedFinWait2 */ + LINUX_MIB_TCPACKSKIPPEDTIMEWAIT, /* TCPACKSkippedTimeWait */ + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, /* TCPACKSkippedChallenge */ + LINUX_MIB_TCPWINPROBE, /* TCPWinProbe */ + LINUX_MIB_TCPKEEPALIVE, /* TCPKeepAlive */ + LINUX_MIB_TCPMTUPFAIL, /* TCPMTUPFail */ + LINUX_MIB_TCPMTUPSUCCESS, /* TCPMTUPSuccess */ + LINUX_MIB_TCPDELIVERED, /* TCPDelivered */ + LINUX_MIB_TCPDELIVEREDCE, /* TCPDeliveredCE */ + LINUX_MIB_TCPACKCOMPRESSED, /* TCPAckCompressed */ + LINUX_MIB_TCPZEROWINDOWDROP, /* TCPZeroWindowDrop */ + LINUX_MIB_TCPRCVQDROP, /* TCPRcvQDrop */ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ + LINUX_MIB_TCPFASTOPENPASSIVEALTKEY, /* TCPFastOpenPassiveAltKey */ + LINUX_MIB_TCPTIMEOUTREHASH, /* TCPTimeoutRehash */ + LINUX_MIB_TCPDUPLICATEDATAREHASH, /* TCPDuplicateDataRehash */ + LINUX_MIB_TCPDSACKRECVSEGS, /* TCPDSACKRecvSegs */ + LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */ + __LINUX_MIB_MAX +}; + +/* linux Xfrm mib definitions */ +enum +{ + LINUX_MIB_XFRMNUM = 0, + LINUX_MIB_XFRMINERROR, /* XfrmInError */ + LINUX_MIB_XFRMINBUFFERERROR, /* XfrmInBufferError */ + LINUX_MIB_XFRMINHDRERROR, /* XfrmInHdrError */ + LINUX_MIB_XFRMINNOSTATES, /* XfrmInNoStates */ + LINUX_MIB_XFRMINSTATEPROTOERROR, /* XfrmInStateProtoError */ + LINUX_MIB_XFRMINSTATEMODEERROR, /* XfrmInStateModeError */ + LINUX_MIB_XFRMINSTATESEQERROR, /* XfrmInStateSeqError */ + LINUX_MIB_XFRMINSTATEEXPIRED, /* XfrmInStateExpired */ + LINUX_MIB_XFRMINSTATEMISMATCH, /* XfrmInStateMismatch */ + LINUX_MIB_XFRMINSTATEINVALID, /* XfrmInStateInvalid */ + LINUX_MIB_XFRMINTMPLMISMATCH, /* XfrmInTmplMismatch */ + LINUX_MIB_XFRMINNOPOLS, /* XfrmInNoPols */ + LINUX_MIB_XFRMINPOLBLOCK, /* XfrmInPolBlock */ + LINUX_MIB_XFRMINPOLERROR, /* XfrmInPolError */ + LINUX_MIB_XFRMOUTERROR, /* XfrmOutError */ + LINUX_MIB_XFRMOUTBUNDLEGENERROR, /* XfrmOutBundleGenError */ + LINUX_MIB_XFRMOUTBUNDLECHECKERROR, /* XfrmOutBundleCheckError */ + LINUX_MIB_XFRMOUTNOSTATES, /* XfrmOutNoStates */ + LINUX_MIB_XFRMOUTSTATEPROTOERROR, /* XfrmOutStateProtoError */ + LINUX_MIB_XFRMOUTSTATEMODEERROR, /* XfrmOutStateModeError */ + LINUX_MIB_XFRMOUTSTATESEQERROR, /* XfrmOutStateSeqError */ + LINUX_MIB_XFRMOUTSTATEEXPIRED, /* XfrmOutStateExpired */ + LINUX_MIB_XFRMOUTPOLBLOCK, /* XfrmOutPolBlock */ + LINUX_MIB_XFRMOUTPOLDEAD, /* XfrmOutPolDead */ + LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */ + LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/ + LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */ + LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */ + __LINUX_MIB_XFRMMAX +}; + +/* linux TLS mib definitions */ +enum +{ + LINUX_MIB_TLSNUM = 0, + LINUX_MIB_TLSCURRTXSW, /* TlsCurrTxSw */ + LINUX_MIB_TLSCURRRXSW, /* TlsCurrRxSw */ + LINUX_MIB_TLSCURRTXDEVICE, /* TlsCurrTxDevice */ + LINUX_MIB_TLSCURRRXDEVICE, /* TlsCurrRxDevice */ + LINUX_MIB_TLSTXSW, /* TlsTxSw */ + LINUX_MIB_TLSRXSW, /* TlsRxSw */ + LINUX_MIB_TLSTXDEVICE, /* TlsTxDevice */ + LINUX_MIB_TLSRXDEVICE, /* TlsRxDevice */ + LINUX_MIB_TLSDECRYPTERROR, /* TlsDecryptError */ + LINUX_MIB_TLSRXDEVICERESYNC, /* TlsRxDeviceResync */ + __LINUX_MIB_TLSMAX +}; + +#endif /* _LINUX_SNMP_H */ diff --git a/include/uapi/linux/sock_diag.h b/include/uapi/linux/sock_diag.h new file mode 100644 index 0000000..35c0ce6 --- /dev/null +++ b/include/uapi/linux/sock_diag.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __SOCK_DIAG_H__ +#define __SOCK_DIAG_H__ + +#include + +#define SOCK_DIAG_BY_FAMILY 20 +#define SOCK_DESTROY 21 + +struct sock_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; +}; + +enum { + SK_MEMINFO_RMEM_ALLOC, + SK_MEMINFO_RCVBUF, + SK_MEMINFO_WMEM_ALLOC, + SK_MEMINFO_SNDBUF, + SK_MEMINFO_FWD_ALLOC, + SK_MEMINFO_WMEM_QUEUED, + SK_MEMINFO_OPTMEM, + SK_MEMINFO_BACKLOG, + SK_MEMINFO_DROPS, + + SK_MEMINFO_VARS, +}; + +enum sknetlink_groups { + SKNLGRP_NONE, + SKNLGRP_INET_TCP_DESTROY, + SKNLGRP_INET_UDP_DESTROY, + SKNLGRP_INET6_TCP_DESTROY, + SKNLGRP_INET6_UDP_DESTROY, + __SKNLGRP_MAX, +}; +#define SKNLGRP_MAX (__SKNLGRP_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_REQ_NONE, + SK_DIAG_BPF_STORAGE_REQ_MAP_FD, + __SK_DIAG_BPF_STORAGE_REQ_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_REQ_MAX (__SK_DIAG_BPF_STORAGE_REQ_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_REP_NONE, + SK_DIAG_BPF_STORAGE, + __SK_DIAG_BPF_STORAGE_REP_MAX, +}; + +#define SK_DIAB_BPF_STORAGE_REP_MAX (__SK_DIAG_BPF_STORAGE_REP_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_NONE, + SK_DIAG_BPF_STORAGE_PAD, + SK_DIAG_BPF_STORAGE_MAP_ID, + SK_DIAG_BPF_STORAGE_MAP_VALUE, + __SK_DIAG_BPF_STORAGE_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_MAX (__SK_DIAG_BPF_STORAGE_MAX - 1) + +#endif /* __SOCK_DIAG_H__ */ diff --git a/include/uapi/linux/socket.h b/include/uapi/linux/socket.h new file mode 100644 index 0000000..debcf26 --- /dev/null +++ b/include/uapi/linux/socket.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SOCKET_H +#define _LINUX_SOCKET_H + +/* + * Desired design of maximum size and alignment (see RFC2553) + */ +#define _K_SS_MAXSIZE 128 /* Implementation specific max size */ + +typedef unsigned short __kernel_sa_family_t; + +/* + * The definition uses anonymous union and struct in order to control the + * default alignment. + */ +struct __kernel_sockaddr_storage { + union { + struct { + __kernel_sa_family_t ss_family; /* address family */ + /* Following field(s) are implementation specific */ + char __data[_K_SS_MAXSIZE - sizeof(unsigned short)]; + /* space to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ + }; + void *__align; /* implementation specific desired alignment */ + }; +}; + +#endif /* _LINUX_SOCKET_H */ diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h new file mode 100644 index 0000000..7d1bccb --- /dev/null +++ b/include/uapi/linux/sockios.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions of the socket-level I/O control calls. + * + * Version: @(#)sockios.h 1.0.2 03/09/93 + * + * Authors: 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_SOCKIOS_H +#define _LINUX_SOCKIOS_H + +#include +#include + +/* Linux-specific socket ioctls */ +#define SIOCINQ FIONREAD +#define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */ + +#define SOCK_IOC_TYPE 0x89 + +/* + * the timeval/timespec data structure layout is defined by libc, + * so we need to cover both possible versions on 32-bit. + */ +/* Get stamp (timeval) */ +#define SIOCGSTAMP_NEW _IOR(SOCK_IOC_TYPE, 0x06, long long[2]) +/* Get stamp (timespec) */ +#define SIOCGSTAMPNS_NEW _IOR(SOCK_IOC_TYPE, 0x07, long long[2]) + +#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__)) +/* on 64-bit and x32, avoid the ?: operator */ +#define SIOCGSTAMP SIOCGSTAMP_OLD +#define SIOCGSTAMPNS SIOCGSTAMPNS_OLD +#else +#define SIOCGSTAMP ((sizeof(struct timeval)) == 8 ? \ + SIOCGSTAMP_OLD : SIOCGSTAMP_NEW) +#define SIOCGSTAMPNS ((sizeof(struct timespec)) == 8 ? \ + SIOCGSTAMPNS_OLD : SIOCGSTAMPNS_NEW) +#endif + +/* Routing table calls. */ +#define SIOCADDRT 0x890B /* add routing table entry */ +#define SIOCDELRT 0x890C /* delete routing table entry */ +#define SIOCRTMSG 0x890D /* unused */ + +/* Socket configuration controls. */ +#define SIOCGIFNAME 0x8910 /* get iface name */ +#define SIOCSIFLINK 0x8911 /* set iface channel */ +#define SIOCGIFCONF 0x8912 /* get iface list */ +#define SIOCGIFFLAGS 0x8913 /* get flags */ +#define SIOCSIFFLAGS 0x8914 /* set flags */ +#define SIOCGIFADDR 0x8915 /* get PA address */ +#define SIOCSIFADDR 0x8916 /* set PA address */ +#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */ +#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */ +#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */ +#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */ +#define SIOCGIFNETMASK 0x891b /* get network PA mask */ +#define SIOCSIFNETMASK 0x891c /* set network PA mask */ +#define SIOCGIFMETRIC 0x891d /* get metric */ +#define SIOCSIFMETRIC 0x891e /* set metric */ +#define SIOCGIFMEM 0x891f /* get memory address (BSD) */ +#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */ +#define SIOCGIFMTU 0x8921 /* get MTU size */ +#define SIOCSIFMTU 0x8922 /* set MTU size */ +#define SIOCSIFNAME 0x8923 /* set interface name */ +#define SIOCSIFHWADDR 0x8924 /* set hardware address */ +#define SIOCGIFENCAP 0x8925 /* get/set encapsulations */ +#define SIOCSIFENCAP 0x8926 +#define SIOCGIFHWADDR 0x8927 /* Get hardware address */ +#define SIOCGIFSLAVE 0x8929 /* Driver slaving support */ +#define SIOCSIFSLAVE 0x8930 +#define SIOCADDMULTI 0x8931 /* Multicast address lists */ +#define SIOCDELMULTI 0x8932 +#define SIOCGIFINDEX 0x8933 /* name -> if_index mapping */ +#define SIOGIFINDEX SIOCGIFINDEX /* misprint compatibility :-) */ +#define SIOCSIFPFLAGS 0x8934 /* set/get extended flags set */ +#define SIOCGIFPFLAGS 0x8935 +#define SIOCDIFADDR 0x8936 /* delete PA address */ +#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ +#define SIOCGIFCOUNT 0x8938 /* get number of devices */ + +#define SIOCGIFBR 0x8940 /* Bridging support */ +#define SIOCSIFBR 0x8941 /* Set bridging options */ + +#define SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */ +#define SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */ + +/* SIOCGIFDIVERT was: 0x8944 Frame diversion support */ +/* SIOCSIFDIVERT was: 0x8945 Set frame diversion options */ + +#define SIOCETHTOOL 0x8946 /* Ethtool interface */ + +#define SIOCGMIIPHY 0x8947 /* Get address of MII PHY in use. */ +#define SIOCGMIIREG 0x8948 /* Read MII PHY register. */ +#define SIOCSMIIREG 0x8949 /* Write MII PHY register. */ + +#define SIOCWANDEV 0x894A /* get/set netdev parameters */ + +#define SIOCOUTQNSD 0x894B /* output queue size (not sent only) */ +#define SIOCGSKNS 0x894C /* get socket network namespace */ + +/* ARP cache control calls. */ + /* 0x8950 - 0x8952 * obsolete calls, don't re-use */ +#define SIOCDARP 0x8953 /* delete ARP table entry */ +#define SIOCGARP 0x8954 /* get ARP table entry */ +#define SIOCSARP 0x8955 /* set ARP table entry */ + +/* RARP cache control calls. */ +#define SIOCDRARP 0x8960 /* delete RARP table entry */ +#define SIOCGRARP 0x8961 /* get RARP table entry */ +#define SIOCSRARP 0x8962 /* set RARP table entry */ + +/* Driver configuration calls */ + +#define SIOCGIFMAP 0x8970 /* Get device parameters */ +#define SIOCSIFMAP 0x8971 /* Set device parameters */ + +/* DLCI configuration calls */ + +#define SIOCADDDLCI 0x8980 /* Create new DLCI device */ +#define SIOCDELDLCI 0x8981 /* Delete DLCI device */ + +#define SIOCGIFVLAN 0x8982 /* 802.1Q VLAN support */ +#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */ + +/* bonding calls */ + +#define SIOCBONDENSLAVE 0x8990 /* enslave a device to the bond */ +#define SIOCBONDRELEASE 0x8991 /* release a slave from the bond*/ +#define SIOCBONDSETHWADDR 0x8992 /* set the hw addr of the bond */ +#define SIOCBONDSLAVEINFOQUERY 0x8993 /* rtn info about slave state */ +#define SIOCBONDINFOQUERY 0x8994 /* rtn info about bond state */ +#define SIOCBONDCHANGEACTIVE 0x8995 /* update to a new active slave */ + +/* bridge calls */ +#define SIOCBRADDBR 0x89a0 /* create new bridge device */ +#define SIOCBRDELBR 0x89a1 /* remove bridge device */ +#define SIOCBRADDIF 0x89a2 /* add interface to bridge */ +#define SIOCBRDELIF 0x89a3 /* remove interface from bridge */ + +/* hardware time stamping: parameters in linux/net_tstamp.h */ +#define SIOCSHWTSTAMP 0x89b0 /* set and get config */ +#define SIOCGHWTSTAMP 0x89b1 /* get config */ + +/* Device private ioctl calls */ + +/* + * These 16 ioctls are available to devices via the do_ioctl() device + * vector. Each device should include this file and redefine these names + * as their own. Because these are device dependent it is a good idea + * _NOT_ to issue them to random objects and hope. + * + * THESE IOCTLS ARE _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X -DaveM + */ + +#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */ + +/* + * These 16 ioctl calls are protocol private + */ + +#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */ +#endif /* _LINUX_SOCKIOS_H */ diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h new file mode 100644 index 0000000..23e025f --- /dev/null +++ b/include/uapi/linux/stddef.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + + +#ifndef __always_inline +#define __always_inline __inline__ +#endif diff --git a/include/uapi/linux/sysinfo.h b/include/uapi/linux/sysinfo.h new file mode 100644 index 0000000..435d5c2 --- /dev/null +++ b/include/uapi/linux/sysinfo.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SYSINFO_H +#define _LINUX_SYSINFO_H + +#include + +#define SI_LOAD_SHIFT 16 +struct sysinfo { + __kernel_long_t uptime; /* Seconds since boot */ + __kernel_ulong_t loads[3]; /* 1, 5, and 15 minute load averages */ + __kernel_ulong_t totalram; /* Total usable main memory size */ + __kernel_ulong_t freeram; /* Available memory size */ + __kernel_ulong_t sharedram; /* Amount of shared memory */ + __kernel_ulong_t bufferram; /* Memory used by buffers */ + __kernel_ulong_t totalswap; /* Total swap space size */ + __kernel_ulong_t freeswap; /* swap space still available */ + __u16 procs; /* Number of current processes */ + __u16 pad; /* Explicit padding for m68k */ + __kernel_ulong_t totalhigh; /* Total high memory size */ + __kernel_ulong_t freehigh; /* Available high memory size */ + __u32 mem_unit; /* Memory unit size in bytes */ + char _f[20-2*sizeof(__kernel_ulong_t)-sizeof(__u32)]; /* Padding: libc5 uses this.. */ +}; + +#endif /* _LINUX_SYSINFO_H */ diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/uapi/linux/tc_act/tc_bpf.h new file mode 100644 index 0000000..653c4f9 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_bpf.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2015 Jiri Pirko + * + * 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_TC_BPF_H +#define __LINUX_TC_BPF_H + +#include + +struct tc_act_bpf { + tc_gen; +}; + +enum { + TCA_ACT_BPF_UNSPEC, + TCA_ACT_BPF_TM, + TCA_ACT_BPF_PARMS, + TCA_ACT_BPF_OPS_LEN, + TCA_ACT_BPF_OPS, + TCA_ACT_BPF_FD, + TCA_ACT_BPF_NAME, + TCA_ACT_BPF_PAD, + TCA_ACT_BPF_TAG, + TCA_ACT_BPF_ID, + __TCA_ACT_BPF_MAX, +}; +#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_connmark.h b/include/uapi/linux/tc_act/tc_connmark.h new file mode 100644 index 0000000..9f8f6f7 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_connmark.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_TC_CONNMARK_H +#define __UAPI_TC_CONNMARK_H + +#include +#include + +struct tc_connmark { + tc_gen; + __u16 zone; +}; + +enum { + TCA_CONNMARK_UNSPEC, + TCA_CONNMARK_PARMS, + TCA_CONNMARK_TM, + TCA_CONNMARK_PAD, + __TCA_CONNMARK_MAX +}; +#define TCA_CONNMARK_MAX (__TCA_CONNMARK_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_csum.h b/include/uapi/linux/tc_act/tc_csum.h new file mode 100644 index 0000000..94b2044 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_csum.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_CSUM_H +#define __LINUX_TC_CSUM_H + +#include +#include + +enum { + TCA_CSUM_UNSPEC, + TCA_CSUM_PARMS, + TCA_CSUM_TM, + TCA_CSUM_PAD, + __TCA_CSUM_MAX +}; +#define TCA_CSUM_MAX (__TCA_CSUM_MAX - 1) + +enum { + TCA_CSUM_UPDATE_FLAG_IPV4HDR = 1, + TCA_CSUM_UPDATE_FLAG_ICMP = 2, + TCA_CSUM_UPDATE_FLAG_IGMP = 4, + TCA_CSUM_UPDATE_FLAG_TCP = 8, + TCA_CSUM_UPDATE_FLAG_UDP = 16, + TCA_CSUM_UPDATE_FLAG_UDPLITE = 32, + TCA_CSUM_UPDATE_FLAG_SCTP = 64, +}; + +struct tc_csum { + tc_gen; + + __u32 update_flags; +}; + +#endif /* __LINUX_TC_CSUM_H */ diff --git a/include/uapi/linux/tc_act/tc_ct.h b/include/uapi/linux/tc_act/tc_ct.h new file mode 100644 index 0000000..5fb1d7a --- /dev/null +++ b/include/uapi/linux/tc_act/tc_ct.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_TC_CT_H +#define __UAPI_TC_CT_H + +#include +#include + +enum { + TCA_CT_UNSPEC, + TCA_CT_PARMS, + TCA_CT_TM, + TCA_CT_ACTION, /* u16 */ + TCA_CT_ZONE, /* u16 */ + TCA_CT_MARK, /* u32 */ + TCA_CT_MARK_MASK, /* u32 */ + TCA_CT_LABELS, /* u128 */ + TCA_CT_LABELS_MASK, /* u128 */ + TCA_CT_NAT_IPV4_MIN, /* be32 */ + TCA_CT_NAT_IPV4_MAX, /* be32 */ + TCA_CT_NAT_IPV6_MIN, /* struct in6_addr */ + TCA_CT_NAT_IPV6_MAX, /* struct in6_addr */ + TCA_CT_NAT_PORT_MIN, /* be16 */ + TCA_CT_NAT_PORT_MAX, /* be16 */ + TCA_CT_PAD, + __TCA_CT_MAX +}; + +#define TCA_CT_MAX (__TCA_CT_MAX - 1) + +#define TCA_CT_ACT_COMMIT (1 << 0) +#define TCA_CT_ACT_FORCE (1 << 1) +#define TCA_CT_ACT_CLEAR (1 << 2) +#define TCA_CT_ACT_NAT (1 << 3) +#define TCA_CT_ACT_NAT_SRC (1 << 4) +#define TCA_CT_ACT_NAT_DST (1 << 5) + +struct tc_ct { + tc_gen; +}; + +#endif /* __UAPI_TC_CT_H */ diff --git a/include/uapi/linux/tc_act/tc_ctinfo.h b/include/uapi/linux/tc_act/tc_ctinfo.h new file mode 100644 index 0000000..f5f26d9 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_ctinfo.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_TC_CTINFO_H +#define __UAPI_TC_CTINFO_H + +#include +#include + +struct tc_ctinfo { + tc_gen; +}; + +enum { + TCA_CTINFO_UNSPEC, + TCA_CTINFO_PAD, + TCA_CTINFO_TM, + TCA_CTINFO_ACT, + TCA_CTINFO_ZONE, + TCA_CTINFO_PARMS_DSCP_MASK, + TCA_CTINFO_PARMS_DSCP_STATEMASK, + TCA_CTINFO_PARMS_CPMARK_MASK, + TCA_CTINFO_STATS_DSCP_SET, + TCA_CTINFO_STATS_DSCP_ERROR, + TCA_CTINFO_STATS_CPMARK_SET, + __TCA_CTINFO_MAX +}; + +#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_defact.h b/include/uapi/linux/tc_act/tc_defact.h new file mode 100644 index 0000000..e3ecd8b --- /dev/null +++ b/include/uapi/linux/tc_act/tc_defact.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_DEF_H +#define __LINUX_TC_DEF_H + +#include + +struct tc_defact { + tc_gen; +}; + +enum { + TCA_DEF_UNSPEC, + TCA_DEF_TM, + TCA_DEF_PARMS, + TCA_DEF_DATA, + TCA_DEF_PAD, + __TCA_DEF_MAX +}; +#define TCA_DEF_MAX (__TCA_DEF_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_gact.h b/include/uapi/linux/tc_act/tc_gact.h new file mode 100644 index 0000000..37e5392 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_gact.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include +#include + +struct tc_gact { + tc_gen; + +}; + +struct tc_gact_p { +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum { + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + TCA_GACT_PAD, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_gate.h b/include/uapi/linux/tc_act/tc_gate.h new file mode 100644 index 0000000..f214b3a --- /dev/null +++ b/include/uapi/linux/tc_act/tc_gate.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* Copyright 2020 NXP */ + +#ifndef __LINUX_TC_GATE_H +#define __LINUX_TC_GATE_H + +#include + +struct tc_gate { + tc_gen; +}; + +enum { + TCA_GATE_ENTRY_UNSPEC, + TCA_GATE_ENTRY_INDEX, + TCA_GATE_ENTRY_GATE, + TCA_GATE_ENTRY_INTERVAL, + TCA_GATE_ENTRY_IPV, + TCA_GATE_ENTRY_MAX_OCTETS, + __TCA_GATE_ENTRY_MAX, +}; +#define TCA_GATE_ENTRY_MAX (__TCA_GATE_ENTRY_MAX - 1) + +enum { + TCA_GATE_ONE_ENTRY_UNSPEC, + TCA_GATE_ONE_ENTRY, + __TCA_GATE_ONE_ENTRY_MAX, +}; +#define TCA_GATE_ONE_ENTRY_MAX (__TCA_GATE_ONE_ENTRY_MAX - 1) + +enum { + TCA_GATE_UNSPEC, + TCA_GATE_TM, + TCA_GATE_PARMS, + TCA_GATE_PAD, + TCA_GATE_PRIORITY, + TCA_GATE_ENTRY_LIST, + TCA_GATE_BASE_TIME, + TCA_GATE_CYCLE_TIME, + TCA_GATE_CYCLE_TIME_EXT, + TCA_GATE_FLAGS, + TCA_GATE_CLOCKID, + __TCA_GATE_MAX, +}; +#define TCA_GATE_MAX (__TCA_GATE_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_ife.h b/include/uapi/linux/tc_act/tc_ife.h new file mode 100644 index 0000000..8c401f1 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_ife.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UAPI_TC_IFE_H +#define __UAPI_TC_IFE_H + +#include +#include +#include + +/* Flag bits for now just encoding/decoding; mutually exclusive */ +#define IFE_ENCODE 1 +#define IFE_DECODE 0 + +struct tc_ife { + tc_gen; + __u16 flags; +}; + +/*XXX: We need to encode the total number of bytes consumed */ +enum { + TCA_IFE_UNSPEC, + TCA_IFE_PARMS, + TCA_IFE_TM, + TCA_IFE_DMAC, + TCA_IFE_SMAC, + TCA_IFE_TYPE, + TCA_IFE_METALST, + TCA_IFE_PAD, + __TCA_IFE_MAX +}; +#define TCA_IFE_MAX (__TCA_IFE_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_ipt.h b/include/uapi/linux/tc_act/tc_ipt.h new file mode 100644 index 0000000..c48d7da --- /dev/null +++ b/include/uapi/linux/tc_act/tc_ipt.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_IPT_H +#define __LINUX_TC_IPT_H + +#include + +enum { + TCA_IPT_UNSPEC, + TCA_IPT_TABLE, + TCA_IPT_HOOK, + TCA_IPT_INDEX, + TCA_IPT_CNT, + TCA_IPT_TM, + TCA_IPT_TARG, + TCA_IPT_PAD, + __TCA_IPT_MAX +}; +#define TCA_IPT_MAX (__TCA_IPT_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h new file mode 100644 index 0000000..2500a00 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_mirred.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include +#include + +#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/ +#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ +#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ +#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ + +struct tc_mirred { + tc_gen; + int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ + __u32 ifindex; /* ifindex of egress port */ +}; + +enum { + TCA_MIRRED_UNSPEC, + TCA_MIRRED_TM, + TCA_MIRRED_PARMS, + TCA_MIRRED_PAD, + __TCA_MIRRED_MAX +}; +#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_mpls.h b/include/uapi/linux/tc_act/tc_mpls.h new file mode 100644 index 0000000..9360e95 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_mpls.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (C) 2019 Netronome Systems, Inc. */ + +#ifndef __LINUX_TC_MPLS_H +#define __LINUX_TC_MPLS_H + +#include + +#define TCA_MPLS_ACT_POP 1 +#define TCA_MPLS_ACT_PUSH 2 +#define TCA_MPLS_ACT_MODIFY 3 +#define TCA_MPLS_ACT_DEC_TTL 4 + +struct tc_mpls { + tc_gen; /* generic TC action fields. */ + int m_action; /* action of type TCA_MPLS_ACT_*. */ +}; + +enum { + TCA_MPLS_UNSPEC, + TCA_MPLS_TM, /* struct tcf_t; time values associated with action. */ + TCA_MPLS_PARMS, /* struct tc_mpls; action type and general TC fields. */ + TCA_MPLS_PAD, + TCA_MPLS_PROTO, /* be16; eth_type of pushed or next (for pop) header. */ + TCA_MPLS_LABEL, /* u32; MPLS label. Lower 20 bits are used. */ + TCA_MPLS_TC, /* u8; MPLS TC field. Lower 3 bits are used. */ + TCA_MPLS_TTL, /* u8; MPLS TTL field. Must not be 0. */ + TCA_MPLS_BOS, /* u8; MPLS BOS field. Either 1 or 0. */ + __TCA_MPLS_MAX, +}; +#define TCA_MPLS_MAX (__TCA_MPLS_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_nat.h b/include/uapi/linux/tc_act/tc_nat.h new file mode 100644 index 0000000..21399c2 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_nat.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_NAT_H +#define __LINUX_TC_NAT_H + +#include +#include + +enum { + TCA_NAT_UNSPEC, + TCA_NAT_PARMS, + TCA_NAT_TM, + TCA_NAT_PAD, + __TCA_NAT_MAX +}; +#define TCA_NAT_MAX (__TCA_NAT_MAX - 1) + +#define TCA_NAT_FLAG_EGRESS 1 + +struct tc_nat { + tc_gen; + __be32 old_addr; + __be32 new_addr; + __be32 mask; + __u32 flags; +}; + +#endif diff --git a/include/uapi/linux/tc_act/tc_pedit.h b/include/uapi/linux/tc_act/tc_pedit.h new file mode 100644 index 0000000..f3e61b0 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_pedit.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_PED_H +#define __LINUX_TC_PED_H + +#include +#include + +enum { + TCA_PEDIT_UNSPEC, + TCA_PEDIT_TM, + TCA_PEDIT_PARMS, + TCA_PEDIT_PAD, + TCA_PEDIT_PARMS_EX, + TCA_PEDIT_KEYS_EX, + TCA_PEDIT_KEY_EX, + __TCA_PEDIT_MAX +}; + +#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1) + +enum { + TCA_PEDIT_KEY_EX_HTYPE = 1, + TCA_PEDIT_KEY_EX_CMD = 2, + __TCA_PEDIT_KEY_EX_MAX +}; + +#define TCA_PEDIT_KEY_EX_MAX (__TCA_PEDIT_KEY_EX_MAX - 1) + + /* TCA_PEDIT_KEY_EX_HDR_TYPE_NETWROK is a special case for legacy users. It + * means no specific header type - offset is relative to the network layer + */ +enum pedit_header_type { + TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK = 0, + TCA_PEDIT_KEY_EX_HDR_TYPE_ETH = 1, + TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 = 2, + TCA_PEDIT_KEY_EX_HDR_TYPE_IP6 = 3, + TCA_PEDIT_KEY_EX_HDR_TYPE_TCP = 4, + TCA_PEDIT_KEY_EX_HDR_TYPE_UDP = 5, + __PEDIT_HDR_TYPE_MAX, +}; + +#define TCA_PEDIT_HDR_TYPE_MAX (__PEDIT_HDR_TYPE_MAX - 1) + +enum pedit_cmd { + TCA_PEDIT_KEY_EX_CMD_SET = 0, + TCA_PEDIT_KEY_EX_CMD_ADD = 1, + __PEDIT_CMD_MAX, +}; + +#define TCA_PEDIT_CMD_MAX (__PEDIT_CMD_MAX - 1) + +struct tc_pedit_key { + __u32 mask; /* AND */ + __u32 val; /*XOR */ + __u32 off; /*offset */ + __u32 at; + __u32 offmask; + __u32 shift; +}; + +struct tc_pedit_sel { + tc_gen; + unsigned char nkeys; + unsigned char flags; + struct tc_pedit_key keys[0]; +}; + +#define tc_pedit tc_pedit_sel + +#endif diff --git a/include/uapi/linux/tc_act/tc_sample.h b/include/uapi/linux/tc_act/tc_sample.h new file mode 100644 index 0000000..fee1bcc --- /dev/null +++ b/include/uapi/linux/tc_act/tc_sample.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_SAMPLE_H +#define __LINUX_TC_SAMPLE_H + +#include +#include +#include + +struct tc_sample { + tc_gen; +}; + +enum { + TCA_SAMPLE_UNSPEC, + TCA_SAMPLE_TM, + TCA_SAMPLE_PARMS, + TCA_SAMPLE_RATE, + TCA_SAMPLE_TRUNC_SIZE, + TCA_SAMPLE_PSAMPLE_GROUP, + TCA_SAMPLE_PAD, + __TCA_SAMPLE_MAX +}; +#define TCA_SAMPLE_MAX (__TCA_SAMPLE_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_skbedit.h b/include/uapi/linux/tc_act/tc_skbedit.h new file mode 100644 index 0000000..800e933 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_skbedit.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2008, 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: Alexander Duyck + */ + +#ifndef __LINUX_TC_SKBEDIT_H +#define __LINUX_TC_SKBEDIT_H + +#include + +#define SKBEDIT_F_PRIORITY 0x1 +#define SKBEDIT_F_QUEUE_MAPPING 0x2 +#define SKBEDIT_F_MARK 0x4 +#define SKBEDIT_F_PTYPE 0x8 +#define SKBEDIT_F_MASK 0x10 +#define SKBEDIT_F_INHERITDSFIELD 0x20 + +struct tc_skbedit { + tc_gen; +}; + +enum { + TCA_SKBEDIT_UNSPEC, + TCA_SKBEDIT_TM, + TCA_SKBEDIT_PARMS, + TCA_SKBEDIT_PRIORITY, + TCA_SKBEDIT_QUEUE_MAPPING, + TCA_SKBEDIT_MARK, + TCA_SKBEDIT_PAD, + TCA_SKBEDIT_PTYPE, + TCA_SKBEDIT_MASK, + TCA_SKBEDIT_FLAGS, + __TCA_SKBEDIT_MAX +}; +#define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_skbmod.h b/include/uapi/linux/tc_act/tc_skbmod.h new file mode 100644 index 0000000..c525b35 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_skbmod.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2016, Jamal Hadi Salim + * + * 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_TC_SKBMOD_H +#define __LINUX_TC_SKBMOD_H + +#include + +#define SKBMOD_F_DMAC 0x1 +#define SKBMOD_F_SMAC 0x2 +#define SKBMOD_F_ETYPE 0x4 +#define SKBMOD_F_SWAPMAC 0x8 + +struct tc_skbmod { + tc_gen; + __u64 flags; +}; + +enum { + TCA_SKBMOD_UNSPEC, + TCA_SKBMOD_TM, + TCA_SKBMOD_PARMS, + TCA_SKBMOD_DMAC, + TCA_SKBMOD_SMAC, + TCA_SKBMOD_ETYPE, + TCA_SKBMOD_PAD, + __TCA_SKBMOD_MAX +}; +#define TCA_SKBMOD_MAX (__TCA_SKBMOD_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_tunnel_key.h b/include/uapi/linux/tc_act/tc_tunnel_key.h new file mode 100644 index 0000000..3f10dc4 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_tunnel_key.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2016, Amir Vadai + * Copyright (c) 2016, Mellanox Technologies. All rights reserved. + * + * 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_TC_TUNNEL_KEY_H +#define __LINUX_TC_TUNNEL_KEY_H + +#include + +#define TCA_TUNNEL_KEY_ACT_SET 1 +#define TCA_TUNNEL_KEY_ACT_RELEASE 2 + +struct tc_tunnel_key { + tc_gen; + int t_action; +}; + +enum { + TCA_TUNNEL_KEY_UNSPEC, + TCA_TUNNEL_KEY_TM, + TCA_TUNNEL_KEY_PARMS, + TCA_TUNNEL_KEY_ENC_IPV4_SRC, /* be32 */ + TCA_TUNNEL_KEY_ENC_IPV4_DST, /* be32 */ + TCA_TUNNEL_KEY_ENC_IPV6_SRC, /* struct in6_addr */ + TCA_TUNNEL_KEY_ENC_IPV6_DST, /* struct in6_addr */ + TCA_TUNNEL_KEY_ENC_KEY_ID, /* be64 */ + TCA_TUNNEL_KEY_PAD, + TCA_TUNNEL_KEY_ENC_DST_PORT, /* be16 */ + TCA_TUNNEL_KEY_NO_CSUM, /* u8 */ + TCA_TUNNEL_KEY_ENC_OPTS, /* Nested TCA_TUNNEL_KEY_ENC_OPTS_ + * attributes + */ + TCA_TUNNEL_KEY_ENC_TOS, /* u8 */ + TCA_TUNNEL_KEY_ENC_TTL, /* u8 */ + __TCA_TUNNEL_KEY_MAX, +}; + +#define TCA_TUNNEL_KEY_MAX (__TCA_TUNNEL_KEY_MAX - 1) + +enum { + TCA_TUNNEL_KEY_ENC_OPTS_UNSPEC, + TCA_TUNNEL_KEY_ENC_OPTS_GENEVE, /* Nested + * TCA_TUNNEL_KEY_ENC_OPTS_ + * attributes + */ + TCA_TUNNEL_KEY_ENC_OPTS_VXLAN, /* Nested + * TCA_TUNNEL_KEY_ENC_OPTS_ + * attributes + */ + TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN, /* Nested + * TCA_TUNNEL_KEY_ENC_OPTS_ + * attributes + */ + __TCA_TUNNEL_KEY_ENC_OPTS_MAX, +}; + +#define TCA_TUNNEL_KEY_ENC_OPTS_MAX (__TCA_TUNNEL_KEY_ENC_OPTS_MAX - 1) + +enum { + TCA_TUNNEL_KEY_ENC_OPT_GENEVE_UNSPEC, + TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS, /* be16 */ + TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */ + TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */ + + __TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, +}; + +#define TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX \ + (__TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX - 1) + +enum { + TCA_TUNNEL_KEY_ENC_OPT_VXLAN_UNSPEC, + TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, /* u32 */ + __TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX, +}; + +#define TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX \ + (__TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX - 1) + +enum { + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_UNSPEC, + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER, /* u8 */ + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX, /* be32 */ + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR, /* u8 */ + TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID, /* u8 */ + __TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, +}; + +#define TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX \ + (__TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_act/tc_vlan.h b/include/uapi/linux/tc_act/tc_vlan.h new file mode 100644 index 0000000..168995b --- /dev/null +++ b/include/uapi/linux/tc_act/tc_vlan.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2014 Jiri Pirko + * + * 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_TC_VLAN_H +#define __LINUX_TC_VLAN_H + +#include + +#define TCA_VLAN_ACT_POP 1 +#define TCA_VLAN_ACT_PUSH 2 +#define TCA_VLAN_ACT_MODIFY 3 + +struct tc_vlan { + tc_gen; + int v_action; +}; + +enum { + TCA_VLAN_UNSPEC, + TCA_VLAN_TM, + TCA_VLAN_PARMS, + TCA_VLAN_PUSH_VLAN_ID, + TCA_VLAN_PUSH_VLAN_PROTOCOL, + TCA_VLAN_PAD, + TCA_VLAN_PUSH_VLAN_PRIORITY, + __TCA_VLAN_MAX, +}; +#define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_ematch/tc_em_cmp.h b/include/uapi/linux/tc_ematch/tc_em_cmp.h new file mode 100644 index 0000000..2549d9d --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_cmp.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_CMP_H +#define __LINUX_TC_EM_CMP_H + +#include +#include + +struct tcf_em_cmp { + __u32 val; + __u32 mask; + __u16 off; + __u8 align:4; + __u8 flags:4; + __u8 layer:4; + __u8 opnd:4; +}; + +enum { + TCF_EM_ALIGN_U8 = 1, + TCF_EM_ALIGN_U16 = 2, + TCF_EM_ALIGN_U32 = 4 +}; + +#define TCF_EM_CMP_TRANS 1 + +#endif diff --git a/include/uapi/linux/tc_ematch/tc_em_ipt.h b/include/uapi/linux/tc_ematch/tc_em_ipt.h new file mode 100644 index 0000000..49a6553 --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_ipt.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_IPT_H +#define __LINUX_TC_EM_IPT_H + +#include +#include + +enum { + TCA_EM_IPT_UNSPEC, + TCA_EM_IPT_HOOK, + TCA_EM_IPT_MATCH_NAME, + TCA_EM_IPT_MATCH_REVISION, + TCA_EM_IPT_NFPROTO, + TCA_EM_IPT_MATCH_DATA, + __TCA_EM_IPT_MAX +}; + +#define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1) + +#endif diff --git a/include/uapi/linux/tc_ematch/tc_em_meta.h b/include/uapi/linux/tc_ematch/tc_em_meta.h new file mode 100644 index 0000000..cf30b5b --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_meta.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_META_H +#define __LINUX_TC_EM_META_H + +#include +#include + +enum { + TCA_EM_META_UNSPEC, + TCA_EM_META_HDR, + TCA_EM_META_LVALUE, + TCA_EM_META_RVALUE, + __TCA_EM_META_MAX +}; +#define TCA_EM_META_MAX (__TCA_EM_META_MAX - 1) + +struct tcf_meta_val { + __u16 kind; + __u8 shift; + __u8 op; +}; + +#define TCF_META_TYPE_MASK (0xf << 12) +#define TCF_META_TYPE(kind) (((kind) & TCF_META_TYPE_MASK) >> 12) +#define TCF_META_ID_MASK 0x7ff +#define TCF_META_ID(kind) ((kind) & TCF_META_ID_MASK) + +enum { + TCF_META_TYPE_VAR, + TCF_META_TYPE_INT, + __TCF_META_TYPE_MAX +}; +#define TCF_META_TYPE_MAX (__TCF_META_TYPE_MAX - 1) + +enum { + TCF_META_ID_VALUE, + TCF_META_ID_RANDOM, + TCF_META_ID_LOADAVG_0, + TCF_META_ID_LOADAVG_1, + TCF_META_ID_LOADAVG_2, + TCF_META_ID_DEV, + TCF_META_ID_PRIORITY, + TCF_META_ID_PROTOCOL, + TCF_META_ID_PKTTYPE, + TCF_META_ID_PKTLEN, + TCF_META_ID_DATALEN, + TCF_META_ID_MACLEN, + TCF_META_ID_NFMARK, + TCF_META_ID_TCINDEX, + TCF_META_ID_RTCLASSID, + TCF_META_ID_RTIIF, + TCF_META_ID_SK_FAMILY, + TCF_META_ID_SK_STATE, + TCF_META_ID_SK_REUSE, + TCF_META_ID_SK_BOUND_IF, + TCF_META_ID_SK_REFCNT, + TCF_META_ID_SK_SHUTDOWN, + TCF_META_ID_SK_PROTO, + TCF_META_ID_SK_TYPE, + TCF_META_ID_SK_RCVBUF, + TCF_META_ID_SK_RMEM_ALLOC, + TCF_META_ID_SK_WMEM_ALLOC, + TCF_META_ID_SK_OMEM_ALLOC, + TCF_META_ID_SK_WMEM_QUEUED, + TCF_META_ID_SK_RCV_QLEN, + TCF_META_ID_SK_SND_QLEN, + TCF_META_ID_SK_ERR_QLEN, + TCF_META_ID_SK_FORWARD_ALLOCS, + TCF_META_ID_SK_SNDBUF, + TCF_META_ID_SK_ALLOCS, + __TCF_META_ID_SK_ROUTE_CAPS, /* unimplemented but in ABI already */ + TCF_META_ID_SK_HASH, + TCF_META_ID_SK_LINGERTIME, + TCF_META_ID_SK_ACK_BACKLOG, + TCF_META_ID_SK_MAX_ACK_BACKLOG, + TCF_META_ID_SK_PRIO, + TCF_META_ID_SK_RCVLOWAT, + TCF_META_ID_SK_RCVTIMEO, + TCF_META_ID_SK_SNDTIMEO, + TCF_META_ID_SK_SENDMSG_OFF, + TCF_META_ID_SK_WRITE_PENDING, + TCF_META_ID_VLAN_TAG, + TCF_META_ID_RXHASH, + __TCF_META_ID_MAX +}; +#define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1) + +struct tcf_meta_hdr { + struct tcf_meta_val left; + struct tcf_meta_val right; +}; + +#endif diff --git a/include/uapi/linux/tc_ematch/tc_em_nbyte.h b/include/uapi/linux/tc_ematch/tc_em_nbyte.h new file mode 100644 index 0000000..c76333f --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_nbyte.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_NBYTE_H +#define __LINUX_TC_EM_NBYTE_H + +#include +#include + +struct tcf_em_nbyte { + __u16 off; + __u16 len:12; + __u8 layer:4; +}; + +#endif diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h new file mode 100644 index 0000000..ee670e8 --- /dev/null +++ b/include/uapi/linux/tcp.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions for the TCP protocol. + * + * Version: @(#)tcp.h 1.0.2 04/28/93 + * + * Author: 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_TCP_H +#define _LINUX_TCP_H + +#include +#include +#include + +struct tcphdr { + __be16 source; + __be16 dest; + __be32 seq; + __be32 ack_seq; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 res1:4, + doff:4, + fin:1, + syn:1, + rst:1, + psh:1, + ack:1, + urg:1, + ece:1, + cwr:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 doff:4, + res1:4, + cwr:1, + ece:1, + urg:1, + ack:1, + psh:1, + rst:1, + syn:1, + fin:1; +#else +#error "Adjust your defines" +#endif + __be16 window; + __sum16 check; + __be16 urg_ptr; +}; + +/* + * The union cast uses a gcc extension to avoid aliasing problems + * (union is compatible to any of its members) + * This means this part of the code is -fstrict-aliasing safe now. + */ +union tcp_word_hdr { + struct tcphdr hdr; + __be32 words[5]; +}; + +#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) + +enum { + TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000), + TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000), + TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000), + TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000), + TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000), + TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000), + TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000), + TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000), + TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000), + TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000) +}; + +/* + * TCP general constants + */ +#define TCP_MSS_DEFAULT 536U /* IPv4 (RFC1122, RFC2581) */ +#define TCP_MSS_DESIRED 1220U /* IPv6 (tunneled), EDNS0 (RFC3226) */ + +/* TCP socket options */ +#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */ +#define TCP_MAXSEG 2 /* Limit MSS */ +#define TCP_CORK 3 /* Never send partially complete segments */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Block/reenable quick acks */ +#define TCP_CONGESTION 13 /* Congestion control algorithm */ +#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ +#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ +#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ +#define TCP_REPAIR 19 /* TCP sock is under repair right now */ +#define TCP_REPAIR_QUEUE 20 +#define TCP_QUEUE_SEQ 21 +#define TCP_REPAIR_OPTIONS 22 +#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ +#define TCP_TIMESTAMP 24 +#define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ +#define TCP_CC_INFO 26 /* Get Congestion Control (optional) info */ +#define TCP_SAVE_SYN 27 /* Record SYN headers for new connections */ +#define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */ +#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */ +#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */ +#define TCP_ULP 31 /* Attach a ULP to a TCP connection */ +#define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions */ +#define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie) */ +#define TCP_FASTOPEN_NO_COOKIE 34 /* Enable TFO without a TFO cookie */ +#define TCP_ZEROCOPY_RECEIVE 35 +#define TCP_INQ 36 /* Notify bytes available to read as a cmsg on read */ + +#define TCP_CM_INQ TCP_INQ + +#define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ + + +#define TCP_REPAIR_ON 1 +#define TCP_REPAIR_OFF 0 +#define TCP_REPAIR_OFF_NO_WP -1 /* Turn off without window probes */ + +struct tcp_repair_opt { + __u32 opt_code; + __u32 opt_val; +}; + +struct tcp_repair_window { + __u32 snd_wl1; + __u32 snd_wnd; + __u32 max_window; + + __u32 rcv_wnd; + __u32 rcv_wup; +}; + +enum { + TCP_NO_QUEUE, + TCP_RECV_QUEUE, + TCP_SEND_QUEUE, + TCP_QUEUES_NR, +}; + +/* why fastopen failed from client perspective */ +enum tcp_fastopen_client_fail { + TFO_STATUS_UNSPEC, /* catch-all */ + TFO_COOKIE_UNAVAILABLE, /* if not in TFO_CLIENT_NO_COOKIE mode */ + TFO_DATA_NOT_ACKED, /* SYN-ACK did not ack SYN data */ + TFO_SYN_RETRANSMITTED, /* SYN-ACK did not ack SYN data after timeout */ +}; + +/* for TCP_INFO socket option */ +#define TCPI_OPT_TIMESTAMPS 1 +#define TCPI_OPT_SACK 2 +#define TCPI_OPT_WSCALE 4 +#define TCPI_OPT_ECN 8 /* ECN was negociated at TCP session init */ +#define TCPI_OPT_ECN_SEEN 16 /* we received at least one packet with ECT */ +#define TCPI_OPT_SYN_DATA 32 /* SYN-ACK acked data in SYN sent or rcvd */ + +/* + * Sender's congestion state indicating normal or abnormal situations + * in the last round of packets sent. The state is driven by the ACK + * information and timer events. + */ +enum tcp_ca_state { + /* + * Nothing bad has been observed recently. + * No apparent reordering, packet loss, or ECN marks. + */ + TCP_CA_Open = 0, +#define TCPF_CA_Open (1< + +/* NETLINK_GENERIC related info + */ +#define TCP_METRICS_GENL_NAME "tcp_metrics" +#define TCP_METRICS_GENL_VERSION 0x1 + +enum tcp_metric_index { + TCP_METRIC_RTT, /* in ms units */ + TCP_METRIC_RTTVAR, /* in ms units */ + TCP_METRIC_SSTHRESH, + TCP_METRIC_CWND, + TCP_METRIC_REORDERING, + + TCP_METRIC_RTT_US, /* in usec units */ + TCP_METRIC_RTTVAR_US, /* in usec units */ + + /* Always last. */ + __TCP_METRIC_MAX, +}; + +#define TCP_METRIC_MAX (__TCP_METRIC_MAX - 1) + +enum { + TCP_METRICS_ATTR_UNSPEC, + TCP_METRICS_ATTR_ADDR_IPV4, /* u32 */ + TCP_METRICS_ATTR_ADDR_IPV6, /* binary */ + TCP_METRICS_ATTR_AGE, /* msecs */ + TCP_METRICS_ATTR_TW_TSVAL, /* u32, raw, rcv tsval */ + TCP_METRICS_ATTR_TW_TS_STAMP, /* s32, sec age */ + TCP_METRICS_ATTR_VALS, /* nested +1, u32 */ + TCP_METRICS_ATTR_FOPEN_MSS, /* u16 */ + TCP_METRICS_ATTR_FOPEN_SYN_DROPS, /* u16, count of drops */ + TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS, /* msecs age */ + TCP_METRICS_ATTR_FOPEN_COOKIE, /* binary */ + TCP_METRICS_ATTR_SADDR_IPV4, /* u32 */ + TCP_METRICS_ATTR_SADDR_IPV6, /* binary */ + TCP_METRICS_ATTR_PAD, + + __TCP_METRICS_ATTR_MAX, +}; + +#define TCP_METRICS_ATTR_MAX (__TCP_METRICS_ATTR_MAX - 1) + +enum { + TCP_METRICS_CMD_UNSPEC, + TCP_METRICS_CMD_GET, + TCP_METRICS_CMD_DEL, + + __TCP_METRICS_CMD_MAX, +}; + +#define TCP_METRICS_CMD_MAX (__TCP_METRICS_CMD_MAX - 1) + +#endif /* _LINUX_TCP_METRICS_H */ diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h new file mode 100644 index 0000000..de5bcd2 --- /dev/null +++ b/include/uapi/linux/tipc.h @@ -0,0 +1,313 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * include/uapi/linux/tipc.h: Header for TIPC socket interface + * + * Copyright (c) 2003-2006, 2015-2016 Ericsson AB + * Copyright (c) 2005, 2010-2011, Wind River Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_TIPC_H_ +#define _LINUX_TIPC_H_ + +#include +#include + +/* + * TIPC addressing primitives + */ + +struct tipc_socket_addr { + __u32 ref; + __u32 node; +}; + +struct tipc_service_addr { + __u32 type; + __u32 instance; +}; + +struct tipc_service_range { + __u32 type; + __u32 lower; + __u32 upper; +}; + +/* + * Application-accessible service types + */ + +#define TIPC_NODE_STATE 0 /* node state service type */ +#define TIPC_TOP_SRV 1 /* topology server service type */ +#define TIPC_LINK_STATE 2 /* link state service type */ +#define TIPC_RESERVED_TYPES 64 /* lowest user-allowed service type */ + +/* + * Publication scopes when binding service / service range + */ +enum tipc_scope { + TIPC_CLUSTER_SCOPE = 2, /* 0 can also be used */ + TIPC_NODE_SCOPE = 3 +}; + +/* + * Limiting values for messages + */ + +#define TIPC_MAX_USER_MSG_SIZE 66000U + +/* + * Message importance levels + */ + +#define TIPC_LOW_IMPORTANCE 0 +#define TIPC_MEDIUM_IMPORTANCE 1 +#define TIPC_HIGH_IMPORTANCE 2 +#define TIPC_CRITICAL_IMPORTANCE 3 + +/* + * Msg rejection/connection shutdown reasons + */ + +#define TIPC_OK 0 +#define TIPC_ERR_NO_NAME 1 +#define TIPC_ERR_NO_PORT 2 +#define TIPC_ERR_NO_NODE 3 +#define TIPC_ERR_OVERLOAD 4 +#define TIPC_CONN_SHUTDOWN 5 + +/* + * TIPC topology subscription service definitions + */ + +#define TIPC_SUB_PORTS 0x01 /* filter: evt at each match */ +#define TIPC_SUB_SERVICE 0x02 /* filter: evt at first up/last down */ +#define TIPC_SUB_CANCEL 0x04 /* filter: cancel a subscription */ + +#define TIPC_WAIT_FOREVER (~0) /* timeout for permanent subscription */ + +struct tipc_subscr { + struct tipc_service_range seq; /* range of interest */ + __u32 timeout; /* subscription duration (in ms) */ + __u32 filter; /* bitmask of filter options */ + char usr_handle[8]; /* available for subscriber use */ +}; + +#define TIPC_PUBLISHED 1 /* publication event */ +#define TIPC_WITHDRAWN 2 /* withdrawal event */ +#define TIPC_SUBSCR_TIMEOUT 3 /* subscription timeout event */ + +struct tipc_event { + __u32 event; /* event type */ + __u32 found_lower; /* matching range */ + __u32 found_upper; /* " " */ + struct tipc_socket_addr port; /* associated socket */ + struct tipc_subscr s; /* associated subscription */ +}; + +/* + * Socket API + */ + +#ifndef AF_TIPC +#define AF_TIPC 30 +#endif + +#ifndef PF_TIPC +#define PF_TIPC AF_TIPC +#endif + +#ifndef SOL_TIPC +#define SOL_TIPC 271 +#endif + +#define TIPC_ADDR_MCAST 1 +#define TIPC_SERVICE_RANGE 1 +#define TIPC_SERVICE_ADDR 2 +#define TIPC_SOCKET_ADDR 3 + +struct sockaddr_tipc { + unsigned short family; + unsigned char addrtype; + signed char scope; + union { + struct tipc_socket_addr id; + struct tipc_service_range nameseq; + struct { + struct tipc_service_addr name; + __u32 domain; + } name; + } addr; +}; + +/* + * Ancillary data objects supported by recvmsg() + */ + +#define TIPC_ERRINFO 1 /* error info */ +#define TIPC_RETDATA 2 /* returned data */ +#define TIPC_DESTNAME 3 /* destination name */ + +/* + * TIPC-specific socket option names + */ + +#define TIPC_IMPORTANCE 127 /* Default: TIPC_LOW_IMPORTANCE */ +#define TIPC_SRC_DROPPABLE 128 /* Default: based on socket type */ +#define TIPC_DEST_DROPPABLE 129 /* Default: based on socket type */ +#define TIPC_CONN_TIMEOUT 130 /* Default: 8000 (ms) */ +#define TIPC_NODE_RECVQ_DEPTH 131 /* Default: none (read only) */ +#define TIPC_SOCK_RECVQ_DEPTH 132 /* Default: none (read only) */ +#define TIPC_MCAST_BROADCAST 133 /* Default: TIPC selects. No arg */ +#define TIPC_MCAST_REPLICAST 134 /* Default: TIPC selects. No arg */ +#define TIPC_GROUP_JOIN 135 /* Takes struct tipc_group_req* */ +#define TIPC_GROUP_LEAVE 136 /* No argument */ +#define TIPC_SOCK_RECVQ_USED 137 /* Default: none (read only) */ +#define TIPC_NODELAY 138 /* Default: false */ + +/* + * Flag values + */ +#define TIPC_GROUP_LOOPBACK 0x1 /* Receive copy of sent msg when match */ +#define TIPC_GROUP_MEMBER_EVTS 0x2 /* Receive membership events in socket */ + +struct tipc_group_req { + __u32 type; /* group id */ + __u32 instance; /* member id */ + __u32 scope; /* cluster/node */ + __u32 flags; +}; + +/* + * Maximum sizes of TIPC bearer-related names (including terminating NULL) + * The string formatting for each name element is: + * media: media + * interface: media:interface name + * link: node:interface-node:interface + */ +#define TIPC_NODEID_LEN 16 +#define TIPC_MAX_MEDIA_NAME 16 +#define TIPC_MAX_IF_NAME 16 +#define TIPC_MAX_BEARER_NAME 32 +#define TIPC_MAX_LINK_NAME 68 + +#define SIOCGETLINKNAME SIOCPROTOPRIVATE +#define SIOCGETNODEID (SIOCPROTOPRIVATE + 1) + +struct tipc_sioc_ln_req { + __u32 peer; + __u32 bearer_id; + char linkname[TIPC_MAX_LINK_NAME]; +}; + +struct tipc_sioc_nodeid_req { + __u32 peer; + char node_id[TIPC_NODEID_LEN]; +}; + +/* + * TIPC Crypto, AEAD + */ +#define TIPC_AEAD_ALG_NAME (32) + +struct tipc_aead_key { + char alg_name[TIPC_AEAD_ALG_NAME]; + unsigned int keylen; /* in bytes */ + char key[]; +}; + +#define TIPC_AEAD_KEYLEN_MIN (16 + 4) +#define TIPC_AEAD_KEYLEN_MAX (32 + 4) +#define TIPC_AEAD_KEY_SIZE_MAX (sizeof(struct tipc_aead_key) + \ + TIPC_AEAD_KEYLEN_MAX) + +static __inline__ int tipc_aead_key_size(struct tipc_aead_key *key) +{ + return sizeof(*key) + key->keylen; +} + +/* The macros and functions below are deprecated: + */ + +#define TIPC_CFG_SRV 0 +#define TIPC_ZONE_SCOPE 1 + +#define TIPC_ADDR_NAMESEQ 1 +#define TIPC_ADDR_NAME 2 +#define TIPC_ADDR_ID 3 + +#define TIPC_NODE_BITS 12 +#define TIPC_CLUSTER_BITS 12 +#define TIPC_ZONE_BITS 8 + +#define TIPC_NODE_OFFSET 0 +#define TIPC_CLUSTER_OFFSET TIPC_NODE_BITS +#define TIPC_ZONE_OFFSET (TIPC_CLUSTER_OFFSET + TIPC_CLUSTER_BITS) + +#define TIPC_NODE_SIZE ((1UL << TIPC_NODE_BITS) - 1) +#define TIPC_CLUSTER_SIZE ((1UL << TIPC_CLUSTER_BITS) - 1) +#define TIPC_ZONE_SIZE ((1UL << TIPC_ZONE_BITS) - 1) + +#define TIPC_NODE_MASK (TIPC_NODE_SIZE << TIPC_NODE_OFFSET) +#define TIPC_CLUSTER_MASK (TIPC_CLUSTER_SIZE << TIPC_CLUSTER_OFFSET) +#define TIPC_ZONE_MASK (TIPC_ZONE_SIZE << TIPC_ZONE_OFFSET) + +#define TIPC_ZONE_CLUSTER_MASK (TIPC_ZONE_MASK | TIPC_CLUSTER_MASK) + +#define tipc_portid tipc_socket_addr +#define tipc_name tipc_service_addr +#define tipc_name_seq tipc_service_range + +static __inline__ __u32 tipc_addr(unsigned int zone, + unsigned int cluster, + unsigned int node) +{ + return (zone << TIPC_ZONE_OFFSET) | + (cluster << TIPC_CLUSTER_OFFSET) | + node; +} + +static __inline__ unsigned int tipc_zone(__u32 addr) +{ + return addr >> TIPC_ZONE_OFFSET; +} + +static __inline__ unsigned int tipc_cluster(__u32 addr) +{ + return (addr & TIPC_CLUSTER_MASK) >> TIPC_CLUSTER_OFFSET; +} + +static __inline__ unsigned int tipc_node(__u32 addr) +{ + return addr & TIPC_NODE_MASK; +} + +#endif diff --git a/include/uapi/linux/tipc_netlink.h b/include/uapi/linux/tipc_netlink.h new file mode 100644 index 0000000..dc0d23a --- /dev/null +++ b/include/uapi/linux/tipc_netlink.h @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (c) 2014, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_TIPC_NETLINK_H_ +#define _LINUX_TIPC_NETLINK_H_ + +#define TIPC_GENL_V2_NAME "TIPCv2" +#define TIPC_GENL_V2_VERSION 0x1 + +/* Netlink commands */ +enum { + TIPC_NL_UNSPEC, + TIPC_NL_LEGACY, + TIPC_NL_BEARER_DISABLE, + TIPC_NL_BEARER_ENABLE, + TIPC_NL_BEARER_GET, + TIPC_NL_BEARER_SET, + TIPC_NL_SOCK_GET, + TIPC_NL_PUBL_GET, + TIPC_NL_LINK_GET, + TIPC_NL_LINK_SET, + TIPC_NL_LINK_RESET_STATS, + TIPC_NL_MEDIA_GET, + TIPC_NL_MEDIA_SET, + TIPC_NL_NODE_GET, + TIPC_NL_NET_GET, + TIPC_NL_NET_SET, + TIPC_NL_NAME_TABLE_GET, + TIPC_NL_MON_SET, + TIPC_NL_MON_GET, + TIPC_NL_MON_PEER_GET, + TIPC_NL_PEER_REMOVE, + TIPC_NL_BEARER_ADD, + TIPC_NL_UDP_GET_REMOTEIP, + TIPC_NL_KEY_SET, + TIPC_NL_KEY_FLUSH, + TIPC_NL_ADDR_LEGACY_GET, + + __TIPC_NL_CMD_MAX, + TIPC_NL_CMD_MAX = __TIPC_NL_CMD_MAX - 1 +}; + +/* Top level netlink attributes */ +enum { + TIPC_NLA_UNSPEC, + TIPC_NLA_BEARER, /* nest */ + TIPC_NLA_SOCK, /* nest */ + TIPC_NLA_PUBL, /* nest */ + TIPC_NLA_LINK, /* nest */ + TIPC_NLA_MEDIA, /* nest */ + TIPC_NLA_NODE, /* nest */ + TIPC_NLA_NET, /* nest */ + TIPC_NLA_NAME_TABLE, /* nest */ + TIPC_NLA_MON, /* nest */ + TIPC_NLA_MON_PEER, /* nest */ + + __TIPC_NLA_MAX, + TIPC_NLA_MAX = __TIPC_NLA_MAX - 1 +}; + +/* Bearer info */ +enum { + TIPC_NLA_BEARER_UNSPEC, + TIPC_NLA_BEARER_NAME, /* string */ + TIPC_NLA_BEARER_PROP, /* nest */ + TIPC_NLA_BEARER_DOMAIN, /* u32 */ + TIPC_NLA_BEARER_UDP_OPTS, /* nest */ + + __TIPC_NLA_BEARER_MAX, + TIPC_NLA_BEARER_MAX = __TIPC_NLA_BEARER_MAX - 1 +}; + +enum { + TIPC_NLA_UDP_UNSPEC, + TIPC_NLA_UDP_LOCAL, /* sockaddr_storage */ + TIPC_NLA_UDP_REMOTE, /* sockaddr_storage */ + TIPC_NLA_UDP_MULTI_REMOTEIP, /* flag */ + + __TIPC_NLA_UDP_MAX, + TIPC_NLA_UDP_MAX = __TIPC_NLA_UDP_MAX - 1 +}; +/* Socket info */ +enum { + TIPC_NLA_SOCK_UNSPEC, + TIPC_NLA_SOCK_ADDR, /* u32 */ + TIPC_NLA_SOCK_REF, /* u32 */ + TIPC_NLA_SOCK_CON, /* nest */ + TIPC_NLA_SOCK_HAS_PUBL, /* flag */ + TIPC_NLA_SOCK_STAT, /* nest */ + TIPC_NLA_SOCK_TYPE, /* u32 */ + TIPC_NLA_SOCK_INO, /* u32 */ + TIPC_NLA_SOCK_UID, /* u32 */ + TIPC_NLA_SOCK_TIPC_STATE, /* u32 */ + TIPC_NLA_SOCK_COOKIE, /* u64 */ + TIPC_NLA_SOCK_PAD, /* flag */ + TIPC_NLA_SOCK_GROUP, /* nest */ + + __TIPC_NLA_SOCK_MAX, + TIPC_NLA_SOCK_MAX = __TIPC_NLA_SOCK_MAX - 1 +}; + +/* Link info */ +enum { + TIPC_NLA_LINK_UNSPEC, + TIPC_NLA_LINK_NAME, /* string */ + TIPC_NLA_LINK_DEST, /* u32 */ + TIPC_NLA_LINK_MTU, /* u32 */ + TIPC_NLA_LINK_BROADCAST, /* flag */ + TIPC_NLA_LINK_UP, /* flag */ + TIPC_NLA_LINK_ACTIVE, /* flag */ + TIPC_NLA_LINK_PROP, /* nest */ + TIPC_NLA_LINK_STATS, /* nest */ + TIPC_NLA_LINK_RX, /* u32 */ + TIPC_NLA_LINK_TX, /* u32 */ + + __TIPC_NLA_LINK_MAX, + TIPC_NLA_LINK_MAX = __TIPC_NLA_LINK_MAX - 1 +}; + +/* Media info */ +enum { + TIPC_NLA_MEDIA_UNSPEC, + TIPC_NLA_MEDIA_NAME, /* string */ + TIPC_NLA_MEDIA_PROP, /* nest */ + + __TIPC_NLA_MEDIA_MAX, + TIPC_NLA_MEDIA_MAX = __TIPC_NLA_MEDIA_MAX - 1 +}; + +/* Node info */ +enum { + TIPC_NLA_NODE_UNSPEC, + TIPC_NLA_NODE_ADDR, /* u32 */ + TIPC_NLA_NODE_UP, /* flag */ + TIPC_NLA_NODE_ID, /* data */ + TIPC_NLA_NODE_KEY, /* data */ + + __TIPC_NLA_NODE_MAX, + TIPC_NLA_NODE_MAX = __TIPC_NLA_NODE_MAX - 1 +}; + +/* Net info */ +enum { + TIPC_NLA_NET_UNSPEC, + TIPC_NLA_NET_ID, /* u32 */ + TIPC_NLA_NET_ADDR, /* u32 */ + TIPC_NLA_NET_NODEID, /* u64 */ + TIPC_NLA_NET_NODEID_W1, /* u64 */ + TIPC_NLA_NET_ADDR_LEGACY, /* flag */ + + __TIPC_NLA_NET_MAX, + TIPC_NLA_NET_MAX = __TIPC_NLA_NET_MAX - 1 +}; + +/* Name table info */ +enum { + TIPC_NLA_NAME_TABLE_UNSPEC, + TIPC_NLA_NAME_TABLE_PUBL, /* nest */ + + __TIPC_NLA_NAME_TABLE_MAX, + TIPC_NLA_NAME_TABLE_MAX = __TIPC_NLA_NAME_TABLE_MAX - 1 +}; + +/* Monitor info */ +enum { + TIPC_NLA_MON_UNSPEC, + TIPC_NLA_MON_ACTIVATION_THRESHOLD, /* u32 */ + TIPC_NLA_MON_REF, /* u32 */ + TIPC_NLA_MON_ACTIVE, /* flag */ + TIPC_NLA_MON_BEARER_NAME, /* string */ + TIPC_NLA_MON_PEERCNT, /* u32 */ + TIPC_NLA_MON_LISTGEN, /* u32 */ + + __TIPC_NLA_MON_MAX, + TIPC_NLA_MON_MAX = __TIPC_NLA_MON_MAX - 1 +}; + +/* Publication info */ +enum { + TIPC_NLA_PUBL_UNSPEC, + + TIPC_NLA_PUBL_TYPE, /* u32 */ + TIPC_NLA_PUBL_LOWER, /* u32 */ + TIPC_NLA_PUBL_UPPER, /* u32 */ + TIPC_NLA_PUBL_SCOPE, /* u32 */ + TIPC_NLA_PUBL_NODE, /* u32 */ + TIPC_NLA_PUBL_REF, /* u32 */ + TIPC_NLA_PUBL_KEY, /* u32 */ + + __TIPC_NLA_PUBL_MAX, + TIPC_NLA_PUBL_MAX = __TIPC_NLA_PUBL_MAX - 1 +}; + +/* Monitor peer info */ +enum { + TIPC_NLA_MON_PEER_UNSPEC, + + TIPC_NLA_MON_PEER_ADDR, /* u32 */ + TIPC_NLA_MON_PEER_DOMGEN, /* u32 */ + TIPC_NLA_MON_PEER_APPLIED, /* u32 */ + TIPC_NLA_MON_PEER_UPMAP, /* u64 */ + TIPC_NLA_MON_PEER_MEMBERS, /* tlv */ + TIPC_NLA_MON_PEER_UP, /* flag */ + TIPC_NLA_MON_PEER_HEAD, /* flag */ + TIPC_NLA_MON_PEER_LOCAL, /* flag */ + TIPC_NLA_MON_PEER_PAD, /* flag */ + + __TIPC_NLA_MON_PEER_MAX, + TIPC_NLA_MON_PEER_MAX = __TIPC_NLA_MON_PEER_MAX - 1 +}; + +/* Nest, socket group info */ +enum { + TIPC_NLA_SOCK_GROUP_ID, /* u32 */ + TIPC_NLA_SOCK_GROUP_OPEN, /* flag */ + TIPC_NLA_SOCK_GROUP_NODE_SCOPE, /* flag */ + TIPC_NLA_SOCK_GROUP_CLUSTER_SCOPE, /* flag */ + TIPC_NLA_SOCK_GROUP_INSTANCE, /* u32 */ + TIPC_NLA_SOCK_GROUP_BC_SEND_NEXT, /* u32 */ + + __TIPC_NLA_SOCK_GROUP_MAX, + TIPC_NLA_SOCK_GROUP_MAX = __TIPC_NLA_SOCK_GROUP_MAX - 1 +}; + +/* Nest, connection info */ +enum { + TIPC_NLA_CON_UNSPEC, + + TIPC_NLA_CON_FLAG, /* flag */ + TIPC_NLA_CON_NODE, /* u32 */ + TIPC_NLA_CON_SOCK, /* u32 */ + TIPC_NLA_CON_TYPE, /* u32 */ + TIPC_NLA_CON_INST, /* u32 */ + + __TIPC_NLA_CON_MAX, + TIPC_NLA_CON_MAX = __TIPC_NLA_CON_MAX - 1 +}; + +/* Nest, socket statistics info */ +enum { + TIPC_NLA_SOCK_STAT_RCVQ, /* u32 */ + TIPC_NLA_SOCK_STAT_SENDQ, /* u32 */ + TIPC_NLA_SOCK_STAT_LINK_CONG, /* flag */ + TIPC_NLA_SOCK_STAT_CONN_CONG, /* flag */ + TIPC_NLA_SOCK_STAT_DROP, /* u32 */ + + __TIPC_NLA_SOCK_STAT_MAX, + TIPC_NLA_SOCK_STAT_MAX = __TIPC_NLA_SOCK_STAT_MAX - 1 +}; + +/* Nest, link propreties. Valid for link, media and bearer */ +enum { + TIPC_NLA_PROP_UNSPEC, + + TIPC_NLA_PROP_PRIO, /* u32 */ + TIPC_NLA_PROP_TOL, /* u32 */ + TIPC_NLA_PROP_WIN, /* u32 */ + TIPC_NLA_PROP_MTU, /* u32 */ + TIPC_NLA_PROP_BROADCAST, /* u32 */ + TIPC_NLA_PROP_BROADCAST_RATIO, /* u32 */ + + __TIPC_NLA_PROP_MAX, + TIPC_NLA_PROP_MAX = __TIPC_NLA_PROP_MAX - 1 +}; + +/* Nest, statistics info */ +enum { + TIPC_NLA_STATS_UNSPEC, + + TIPC_NLA_STATS_RX_INFO, /* u32 */ + TIPC_NLA_STATS_RX_FRAGMENTS, /* u32 */ + TIPC_NLA_STATS_RX_FRAGMENTED, /* u32 */ + TIPC_NLA_STATS_RX_BUNDLES, /* u32 */ + TIPC_NLA_STATS_RX_BUNDLED, /* u32 */ + TIPC_NLA_STATS_TX_INFO, /* u32 */ + TIPC_NLA_STATS_TX_FRAGMENTS, /* u32 */ + TIPC_NLA_STATS_TX_FRAGMENTED, /* u32 */ + TIPC_NLA_STATS_TX_BUNDLES, /* u32 */ + TIPC_NLA_STATS_TX_BUNDLED, /* u32 */ + TIPC_NLA_STATS_MSG_PROF_TOT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_CNT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_TOT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P0, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P1, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P2, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P3, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P4, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P5, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P6, /* u32 */ + TIPC_NLA_STATS_RX_STATES, /* u32 */ + TIPC_NLA_STATS_RX_PROBES, /* u32 */ + TIPC_NLA_STATS_RX_NACKS, /* u32 */ + TIPC_NLA_STATS_RX_DEFERRED, /* u32 */ + TIPC_NLA_STATS_TX_STATES, /* u32 */ + TIPC_NLA_STATS_TX_PROBES, /* u32 */ + TIPC_NLA_STATS_TX_NACKS, /* u32 */ + TIPC_NLA_STATS_TX_ACKS, /* u32 */ + TIPC_NLA_STATS_RETRANSMITTED, /* u32 */ + TIPC_NLA_STATS_DUPLICATES, /* u32 */ + TIPC_NLA_STATS_LINK_CONGS, /* u32 */ + TIPC_NLA_STATS_MAX_QUEUE, /* u32 */ + TIPC_NLA_STATS_AVG_QUEUE, /* u32 */ + + __TIPC_NLA_STATS_MAX, + TIPC_NLA_STATS_MAX = __TIPC_NLA_STATS_MAX - 1 +}; + +#endif diff --git a/include/uapi/linux/tipc_sockets_diag.h b/include/uapi/linux/tipc_sockets_diag.h new file mode 100644 index 0000000..21b766e --- /dev/null +++ b/include/uapi/linux/tipc_sockets_diag.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* AF_TIPC sock_diag interface for querying open sockets */ + +#ifndef __TIPC_SOCKETS_DIAG_H__ +#define __TIPC_SOCKETS_DIAG_H__ + +#include +#include + +/* Request */ +struct tipc_sock_diag_req { + __u8 sdiag_family; /* must be AF_TIPC */ + __u8 sdiag_protocol; /* must be 0 */ + __u16 pad; /* must be 0 */ + __u32 tidiag_states; /* query*/ +}; +#endif /* __TIPC_SOCKETS_DIAG_H__ */ diff --git a/include/uapi/linux/tls.h b/include/uapi/linux/tls.h new file mode 100644 index 0000000..3b67540 --- /dev/null +++ b/include/uapi/linux/tls.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ +/* + * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _LINUX_TLS_H +#define _LINUX_TLS_H + +#include + +/* TLS socket options */ +#define TLS_TX 1 /* Set transmit parameters */ +#define TLS_RX 2 /* Set receive parameters */ + +/* Supported versions */ +#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF) +#define TLS_VERSION_MAJOR(ver) (((ver) >> 8) & 0xFF) + +#define TLS_VERSION_NUMBER(id) ((((id##_VERSION_MAJOR) & 0xFF) << 8) | \ + ((id##_VERSION_MINOR) & 0xFF)) + +#define TLS_1_2_VERSION_MAJOR 0x3 +#define TLS_1_2_VERSION_MINOR 0x3 +#define TLS_1_2_VERSION TLS_VERSION_NUMBER(TLS_1_2) + +#define TLS_1_3_VERSION_MAJOR 0x3 +#define TLS_1_3_VERSION_MINOR 0x4 +#define TLS_1_3_VERSION TLS_VERSION_NUMBER(TLS_1_3) + +/* Supported ciphers */ +#define TLS_CIPHER_AES_GCM_128 51 +#define TLS_CIPHER_AES_GCM_128_IV_SIZE 8 +#define TLS_CIPHER_AES_GCM_128_KEY_SIZE 16 +#define TLS_CIPHER_AES_GCM_128_SALT_SIZE 4 +#define TLS_CIPHER_AES_GCM_128_TAG_SIZE 16 +#define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8 + +#define TLS_CIPHER_AES_GCM_256 52 +#define TLS_CIPHER_AES_GCM_256_IV_SIZE 8 +#define TLS_CIPHER_AES_GCM_256_KEY_SIZE 32 +#define TLS_CIPHER_AES_GCM_256_SALT_SIZE 4 +#define TLS_CIPHER_AES_GCM_256_TAG_SIZE 16 +#define TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE 8 + +#define TLS_CIPHER_AES_CCM_128 53 +#define TLS_CIPHER_AES_CCM_128_IV_SIZE 8 +#define TLS_CIPHER_AES_CCM_128_KEY_SIZE 16 +#define TLS_CIPHER_AES_CCM_128_SALT_SIZE 4 +#define TLS_CIPHER_AES_CCM_128_TAG_SIZE 16 +#define TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE 8 + +#define TLS_SET_RECORD_TYPE 1 +#define TLS_GET_RECORD_TYPE 2 + +struct tls_crypto_info { + __u16 version; + __u16 cipher_type; +}; + +struct tls12_crypto_info_aes_gcm_128 { + struct tls_crypto_info info; + unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE]; + unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE]; + unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE]; + unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]; +}; + +struct tls12_crypto_info_aes_gcm_256 { + struct tls_crypto_info info; + unsigned char iv[TLS_CIPHER_AES_GCM_256_IV_SIZE]; + unsigned char key[TLS_CIPHER_AES_GCM_256_KEY_SIZE]; + unsigned char salt[TLS_CIPHER_AES_GCM_256_SALT_SIZE]; + unsigned char rec_seq[TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE]; +}; + +struct tls12_crypto_info_aes_ccm_128 { + struct tls_crypto_info info; + unsigned char iv[TLS_CIPHER_AES_CCM_128_IV_SIZE]; + unsigned char key[TLS_CIPHER_AES_CCM_128_KEY_SIZE]; + unsigned char salt[TLS_CIPHER_AES_CCM_128_SALT_SIZE]; + unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE]; +}; + +enum { + TLS_INFO_UNSPEC, + TLS_INFO_VERSION, + TLS_INFO_CIPHER, + TLS_INFO_TXCONF, + TLS_INFO_RXCONF, + __TLS_INFO_MAX, +}; +#define TLS_INFO_MAX (__TLS_INFO_MAX - 1) + +#define TLS_CONF_BASE 1 +#define TLS_CONF_SW 2 +#define TLS_CONF_HW 3 +#define TLS_CONF_HW_RECORD 4 + +#endif /* _LINUX_TLS_H */ diff --git a/include/uapi/linux/types.h b/include/uapi/linux/types.h new file mode 100644 index 0000000..999cb0f --- /dev/null +++ b/include/uapi/linux/types.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#include + +#ifndef __ASSEMBLY__ + +#include + + +/* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. + */ + +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#define __bitwise __bitwise__ + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __wsum; + +/* + * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid + * common 32/64-bit compat problems. + * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other + * architectures) and to 8-byte boundaries on 64-bit architectures. The new + * aligned_64 type enforces 8-byte alignment so that structs containing + * aligned_64 values have the same alignment on 32-bit and 64-bit architectures. + * No conversions are necessary between 32-bit user-space and a 64-bit kernel. + */ +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_be64 __be64 __attribute__((aligned(8))) +#define __aligned_le64 __le64 __attribute__((aligned(8))) + +typedef unsigned __bitwise __poll_t; + +#endif /* __ASSEMBLY__ */ +#endif /* _LINUX_TYPES_H */ diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h new file mode 100644 index 0000000..d0a7223 --- /dev/null +++ b/include/uapi/linux/udp.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * 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. + * + * Definitions for the UDP protocol. + * + * Version: @(#)udp.h 1.0.2 04/28/93 + * + * Author: 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_UDP_H +#define _LINUX_UDP_H + +#include + +struct udphdr { + __be16 source; + __be16 dest; + __be16 len; + __sum16 check; +}; + +/* UDP socket options */ +#define UDP_CORK 1 /* Never send partially complete segments */ +#define UDP_ENCAP 100 /* Set the socket to accept encapsulated packets */ +#define UDP_NO_CHECK6_TX 101 /* Disable sending checksum for UDP6X */ +#define UDP_NO_CHECK6_RX 102 /* Disable accpeting checksum for UDP6 */ +#define UDP_SEGMENT 103 /* Set GSO segmentation size */ +#define UDP_GRO 104 /* This socket can receive UDP GRO packets */ + +/* UDP encapsulation types */ +#define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ +#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */ +#define UDP_ENCAP_L2TPINUDP 3 /* rfc2661 */ +#define UDP_ENCAP_GTP0 4 /* GSM TS 09.60 */ +#define UDP_ENCAP_GTP1U 5 /* 3GPP TS 29.060 */ +#define UDP_ENCAP_RXRPC 6 +#define TCP_ENCAP_ESPINTCP 7 /* Yikes, this is really xfrm encap types. */ + +#endif /* _LINUX_UDP_H */ diff --git a/include/uapi/linux/unix_diag.h b/include/uapi/linux/unix_diag.h new file mode 100644 index 0000000..a198857 --- /dev/null +++ b/include/uapi/linux/unix_diag.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __UNIX_DIAG_H__ +#define __UNIX_DIAG_H__ + +#include + +struct unix_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 udiag_states; + __u32 udiag_ino; + __u32 udiag_show; + __u32 udiag_cookie[2]; +}; + +#define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ +#define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ +#define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ +#define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */ +#define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */ +#define UDIAG_SHOW_MEMINFO 0x00000020 /* show memory info of a socket */ +#define UDIAG_SHOW_UID 0x00000040 /* show socket's UID */ + +struct unix_diag_msg { + __u8 udiag_family; + __u8 udiag_type; + __u8 udiag_state; + __u8 pad; + + __u32 udiag_ino; + __u32 udiag_cookie[2]; +}; + +enum { + /* UNIX_DIAG_NONE, standard nl API requires this attribute! */ + UNIX_DIAG_NAME, + UNIX_DIAG_VFS, + UNIX_DIAG_PEER, + UNIX_DIAG_ICONS, + UNIX_DIAG_RQLEN, + UNIX_DIAG_MEMINFO, + UNIX_DIAG_SHUTDOWN, + UNIX_DIAG_UID, + + __UNIX_DIAG_MAX, +}; + +#define UNIX_DIAG_MAX (__UNIX_DIAG_MAX - 1) + +struct unix_diag_vfs { + __u32 udiag_vfs_ino; + __u32 udiag_vfs_dev; +}; + +struct unix_diag_rqlen { + __u32 udiag_rqueue; + __u32 udiag_wqueue; +}; + +#endif diff --git a/include/uapi/linux/veth.h b/include/uapi/linux/veth.h new file mode 100644 index 0000000..52b58e5 --- /dev/null +++ b/include/uapi/linux/veth.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __NET_VETH_H_ +#define __NET_VETH_H_ + +enum { + VETH_INFO_UNSPEC, + VETH_INFO_PEER, + + __VETH_INFO_MAX +#define VETH_INFO_MAX (__VETH_INFO_MAX - 1) +}; + +#endif diff --git a/include/uapi/linux/vm_sockets_diag.h b/include/uapi/linux/vm_sockets_diag.h new file mode 100644 index 0000000..6da42f9 --- /dev/null +++ b/include/uapi/linux/vm_sockets_diag.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* AF_VSOCK sock_diag(7) interface for querying open sockets */ + +#ifndef __VM_SOCKETS_DIAG_H__ +#define __VM_SOCKETS_DIAG_H__ + +#include + +/* Request */ +struct vsock_diag_req { + __u8 sdiag_family; /* must be AF_VSOCK */ + __u8 sdiag_protocol; /* must be 0 */ + __u16 pad; /* must be 0 */ + __u32 vdiag_states; /* query bitmap (e.g. 1 << TCP_LISTEN) */ + __u32 vdiag_ino; /* must be 0 (reserved) */ + __u32 vdiag_show; /* must be 0 (reserved) */ + __u32 vdiag_cookie[2]; +}; + +/* Response */ +struct vsock_diag_msg { + __u8 vdiag_family; /* AF_VSOCK */ + __u8 vdiag_type; /* SOCK_STREAM or SOCK_DGRAM */ + __u8 vdiag_state; /* sk_state (e.g. TCP_LISTEN) */ + __u8 vdiag_shutdown; /* local RCV_SHUTDOWN | SEND_SHUTDOWN */ + __u32 vdiag_src_cid; + __u32 vdiag_src_port; + __u32 vdiag_dst_cid; + __u32 vdiag_dst_port; + __u32 vdiag_ino; + __u32 vdiag_cookie[2]; +}; + +#endif /* __VM_SOCKETS_DIAG_H__ */ diff --git a/include/uapi/linux/xdp_diag.h b/include/uapi/linux/xdp_diag.h new file mode 100644 index 0000000..66b9973 --- /dev/null +++ b/include/uapi/linux/xdp_diag.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * xdp_diag: interface for query/monitor XDP sockets + * Copyright(c) 2019 Intel Corporation. + */ + +#ifndef _LINUX_XDP_DIAG_H +#define _LINUX_XDP_DIAG_H + +#include + +struct xdp_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 xdiag_ino; + __u32 xdiag_show; + __u32 xdiag_cookie[2]; +}; + +struct xdp_diag_msg { + __u8 xdiag_family; + __u8 xdiag_type; + __u16 pad; + __u32 xdiag_ino; + __u32 xdiag_cookie[2]; +}; + +#define XDP_SHOW_INFO (1 << 0) /* Basic information */ +#define XDP_SHOW_RING_CFG (1 << 1) +#define XDP_SHOW_UMEM (1 << 2) +#define XDP_SHOW_MEMINFO (1 << 3) +#define XDP_SHOW_STATS (1 << 4) + +enum { + XDP_DIAG_NONE, + XDP_DIAG_INFO, + XDP_DIAG_UID, + XDP_DIAG_RX_RING, + XDP_DIAG_TX_RING, + XDP_DIAG_UMEM, + XDP_DIAG_UMEM_FILL_RING, + XDP_DIAG_UMEM_COMPLETION_RING, + XDP_DIAG_MEMINFO, + XDP_DIAG_STATS, + __XDP_DIAG_MAX, +}; + +#define XDP_DIAG_MAX (__XDP_DIAG_MAX - 1) + +struct xdp_diag_info { + __u32 ifindex; + __u32 queue_id; +}; + +struct xdp_diag_ring { + __u32 entries; /*num descs */ +}; + +#define XDP_DU_F_ZEROCOPY (1 << 0) + +struct xdp_diag_umem { + __u64 size; + __u32 id; + __u32 num_pages; + __u32 chunk_size; + __u32 headroom; + __u32 ifindex; + __u32 queue_id; + __u32 flags; + __u32 refs; +}; + +struct xdp_diag_stats { + __u64 n_rx_dropped; + __u64 n_rx_invalid; + __u64 n_rx_full; + __u64 n_fill_ring_empty; + __u64 n_tx_invalid; + __u64 n_tx_ring_empty; +}; + +#endif /* _LINUX_XDP_DIAG_H */ diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h new file mode 100644 index 0000000..eec67a2 --- /dev/null +++ b/include/uapi/linux/xfrm.h @@ -0,0 +1,541 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include +#include + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union { + __be32 a4; + __be32 a6[4]; + struct in6_addr in6; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id { + xfrm_address_t daddr; + __be32 spi; + __u8 proto; +}; + +struct xfrm_sec_ctx { + __u8 ctx_doi; + __u8 ctx_alg; + __u16 ctx_len; + __u32 ctx_sid; + char ctx_str[0]; +}; + +/* Security Context Domains of Interpretation */ +#define XFRM_SC_DOI_RESERVED 0 +#define XFRM_SC_DOI_LSM 1 + +/* Security Context Algorithms */ +#define XFRM_SC_ALG_RESERVED 0 +#define XFRM_SC_ALG_SELINUX 1 + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ + +struct xfrm_selector { + xfrm_address_t daddr; + xfrm_address_t saddr; + __be16 dport; + __be16 dport_mask; + __be16 sport; + __be16 sport_mask; + __u16 family; + __u8 prefixlen_d; + __u8 prefixlen_s; + __u8 proto; + int ifindex; + __kernel_uid32_t user; +}; + +#define XFRM_INF (~(__u64)0) + +struct xfrm_lifetime_cfg { + __u64 soft_byte_limit; + __u64 hard_byte_limit; + __u64 soft_packet_limit; + __u64 hard_packet_limit; + __u64 soft_add_expires_seconds; + __u64 hard_add_expires_seconds; + __u64 soft_use_expires_seconds; + __u64 hard_use_expires_seconds; +}; + +struct xfrm_lifetime_cur { + __u64 bytes; + __u64 packets; + __u64 add_time; + __u64 use_time; +}; + +struct xfrm_replay_state { + __u32 oseq; + __u32 seq; + __u32 bitmap; +}; + +#define XFRMA_REPLAY_ESN_MAX 4096 + +struct xfrm_replay_state_esn { + unsigned int bmp_len; + __u32 oseq; + __u32 seq; + __u32 oseq_hi; + __u32 seq_hi; + __u32 replay_window; + __u32 bmp[0]; +}; + +struct xfrm_algo { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_algo_auth { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + unsigned int alg_trunc_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_algo_aead { + char alg_name[64]; + unsigned int alg_key_len; /* in bits */ + unsigned int alg_icv_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_stats { + __u32 replay_window; + __u32 replay; + __u32 integrity_failed; +}; + +enum { + XFRM_POLICY_TYPE_MAIN = 0, + XFRM_POLICY_TYPE_SUB = 1, + XFRM_POLICY_TYPE_MAX = 2, + XFRM_POLICY_TYPE_ANY = 255 +}; + +enum { + XFRM_POLICY_IN = 0, + XFRM_POLICY_OUT = 1, + XFRM_POLICY_FWD = 2, + XFRM_POLICY_MASK = 3, + XFRM_POLICY_MAX = 3 +}; + +enum { + XFRM_SHARE_ANY, /* No limitations */ + XFRM_SHARE_SESSION, /* For this session only */ + XFRM_SHARE_USER, /* For this user only */ + XFRM_SHARE_UNIQUE /* Use once */ +}; + +#define XFRM_MODE_TRANSPORT 0 +#define XFRM_MODE_TUNNEL 1 +#define XFRM_MODE_ROUTEOPTIMIZATION 2 +#define XFRM_MODE_IN_TRIGGER 3 +#define XFRM_MODE_BEET 4 +#define XFRM_MODE_MAX 5 + +/* Netlink configuration messages. */ +enum { + XFRM_MSG_BASE = 0x10, + + XFRM_MSG_NEWSA = 0x10, +#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA + XFRM_MSG_DELSA, +#define XFRM_MSG_DELSA XFRM_MSG_DELSA + XFRM_MSG_GETSA, +#define XFRM_MSG_GETSA XFRM_MSG_GETSA + + XFRM_MSG_NEWPOLICY, +#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY + XFRM_MSG_DELPOLICY, +#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY + XFRM_MSG_GETPOLICY, +#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY + + XFRM_MSG_ALLOCSPI, +#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI + XFRM_MSG_ACQUIRE, +#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE + XFRM_MSG_EXPIRE, +#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE + + XFRM_MSG_UPDPOLICY, +#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY + XFRM_MSG_UPDSA, +#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA + + XFRM_MSG_POLEXPIRE, +#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE + + XFRM_MSG_FLUSHSA, +#define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA + XFRM_MSG_FLUSHPOLICY, +#define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY + + XFRM_MSG_NEWAE, +#define XFRM_MSG_NEWAE XFRM_MSG_NEWAE + XFRM_MSG_GETAE, +#define XFRM_MSG_GETAE XFRM_MSG_GETAE + + XFRM_MSG_REPORT, +#define XFRM_MSG_REPORT XFRM_MSG_REPORT + + XFRM_MSG_MIGRATE, +#define XFRM_MSG_MIGRATE XFRM_MSG_MIGRATE + + XFRM_MSG_NEWSADINFO, +#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO + XFRM_MSG_GETSADINFO, +#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO + + XFRM_MSG_NEWSPDINFO, +#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO + XFRM_MSG_GETSPDINFO, +#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO + + XFRM_MSG_MAPPING, +#define XFRM_MSG_MAPPING XFRM_MSG_MAPPING + __XFRM_MSG_MAX +}; +#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1) + +#define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE) + +/* + * Generic LSM security context for comunicating to user space + * NOTE: Same format as sadb_x_sec_ctx + */ +struct xfrm_user_sec_ctx { + __u16 len; + __u16 exttype; + __u8 ctx_alg; /* LSMs: e.g., selinux == 1 */ + __u8 ctx_doi; + __u16 ctx_len; +}; + +struct xfrm_user_tmpl { + struct xfrm_id id; + __u16 family; + xfrm_address_t saddr; + __u32 reqid; + __u8 mode; + __u8 share; + __u8 optional; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; +}; + +struct xfrm_encap_tmpl { + __u16 encap_type; + __be16 encap_sport; + __be16 encap_dport; + xfrm_address_t encap_oa; +}; + +/* AEVENT flags */ +enum xfrm_ae_ftype_t { + XFRM_AE_UNSPEC, + XFRM_AE_RTHR=1, /* replay threshold*/ + XFRM_AE_RVAL=2, /* replay value */ + XFRM_AE_LVAL=4, /* lifetime value */ + XFRM_AE_ETHR=8, /* expiry timer threshold */ + XFRM_AE_CR=16, /* Event cause is replay update */ + XFRM_AE_CE=32, /* Event cause is timer expiry */ + XFRM_AE_CU=64, /* Event cause is policy update */ + __XFRM_AE_MAX + +#define XFRM_AE_MAX (__XFRM_AE_MAX - 1) +}; + +struct xfrm_userpolicy_type { + __u8 type; + __u16 reserved1; + __u8 reserved2; +}; + +/* Netlink message attributes. */ +enum xfrm_attr_type_t { + XFRMA_UNSPEC, + XFRMA_ALG_AUTH, /* struct xfrm_algo */ + XFRMA_ALG_CRYPT, /* struct xfrm_algo */ + XFRMA_ALG_COMP, /* struct xfrm_algo */ + XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */ + XFRMA_SA, /* struct xfrm_usersa_info */ + XFRMA_POLICY, /*struct xfrm_userpolicy_info */ + XFRMA_SEC_CTX, /* struct xfrm_sec_ctx */ + XFRMA_LTIME_VAL, + XFRMA_REPLAY_VAL, + XFRMA_REPLAY_THRESH, + XFRMA_ETIMER_THRESH, + XFRMA_SRCADDR, /* xfrm_address_t */ + XFRMA_COADDR, /* xfrm_address_t */ + XFRMA_LASTUSED, /* unsigned long */ + XFRMA_POLICY_TYPE, /* struct xfrm_userpolicy_type */ + XFRMA_MIGRATE, + XFRMA_ALG_AEAD, /* struct xfrm_algo_aead */ + XFRMA_KMADDRESS, /* struct xfrm_user_kmaddress */ + XFRMA_ALG_AUTH_TRUNC, /* struct xfrm_algo_auth */ + XFRMA_MARK, /* struct xfrm_mark */ + XFRMA_TFCPAD, /* __u32 */ + XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_state_esn */ + XFRMA_SA_EXTRA_FLAGS, /* __u32 */ + XFRMA_PROTO, /* __u8 */ + XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ + XFRMA_PAD, + XFRMA_OFFLOAD_DEV, /* struct xfrm_user_offload */ + XFRMA_SET_MARK, /* __u32 */ + XFRMA_SET_MARK_MASK, /* __u32 */ + XFRMA_IF_ID, /* __u32 */ + __XFRMA_MAX + +#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ +#define XFRMA_MAX (__XFRMA_MAX - 1) +}; + +struct xfrm_mark { + __u32 v; /* value */ + __u32 m; /* mask */ +}; + +enum xfrm_sadattr_type_t { + XFRMA_SAD_UNSPEC, + XFRMA_SAD_CNT, + XFRMA_SAD_HINFO, + __XFRMA_SAD_MAX + +#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1) +}; + +struct xfrmu_sadhinfo { + __u32 sadhcnt; /* current hash bkts */ + __u32 sadhmcnt; /* max allowed hash bkts */ +}; + +enum xfrm_spdattr_type_t { + XFRMA_SPD_UNSPEC, + XFRMA_SPD_INFO, + XFRMA_SPD_HINFO, + XFRMA_SPD_IPV4_HTHRESH, + XFRMA_SPD_IPV6_HTHRESH, + __XFRMA_SPD_MAX + +#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1) +}; + +struct xfrmu_spdinfo { + __u32 incnt; + __u32 outcnt; + __u32 fwdcnt; + __u32 inscnt; + __u32 outscnt; + __u32 fwdscnt; +}; + +struct xfrmu_spdhinfo { + __u32 spdhcnt; + __u32 spdhmcnt; +}; + +struct xfrmu_spdhthresh { + __u8 lbits; + __u8 rbits; +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + __u32 seq; + __u32 reqid; + __u16 family; + __u8 mode; /* XFRM_MODE_xxx */ + __u8 replay_window; + __u8 flags; +#define XFRM_STATE_NOECN 1 +#define XFRM_STATE_DECAP_DSCP 2 +#define XFRM_STATE_NOPMTUDISC 4 +#define XFRM_STATE_WILDRECV 8 +#define XFRM_STATE_ICMP 16 +#define XFRM_STATE_AF_UNSPEC 32 +#define XFRM_STATE_ALIGN4 64 +#define XFRM_STATE_ESN 128 +}; + +#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 +#define XFRM_SA_XFLAG_OSEQ_MAY_WRAP 2 + +struct xfrm_usersa_id { + xfrm_address_t daddr; + __be32 spi; + __u16 family; + __u8 proto; +}; + +struct xfrm_aevent_id { + struct xfrm_usersa_id sa_id; + xfrm_address_t saddr; + __u32 flags; + __u32 reqid; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + __u32 min; + __u32 max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + __u32 priority; + __u32 index; + __u8 dir; + __u8 action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + __u8 flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + /* Automatically expand selector to include matching ICMP payloads. */ +#define XFRM_POLICY_ICMP 2 + __u8 share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + __u32 index; + __u8 dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; + __u32 seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + __u8 hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + __u8 hard; +}; + +struct xfrm_usersa_flush { + __u8 proto; +}; + +struct xfrm_user_report { + __u8 proto; + struct xfrm_selector sel; +}; + +/* Used by MIGRATE to pass addresses IKE should use to perform + * SA negotiation with the peer */ +struct xfrm_user_kmaddress { + xfrm_address_t local; + xfrm_address_t remote; + __u32 reserved; + __u16 family; +}; + +struct xfrm_user_migrate { + xfrm_address_t old_daddr; + xfrm_address_t old_saddr; + xfrm_address_t new_daddr; + xfrm_address_t new_saddr; + __u8 proto; + __u8 mode; + __u16 reserved; + __u32 reqid; + __u16 old_family; + __u16 new_family; +}; + +struct xfrm_user_mapping { + struct xfrm_usersa_id id; + __u32 reqid; + xfrm_address_t old_saddr; + xfrm_address_t new_saddr; + __be16 old_sport; + __be16 new_sport; +}; + +struct xfrm_address_filter { + xfrm_address_t saddr; + xfrm_address_t daddr; + __u16 family; + __u8 splen; + __u8 dplen; +}; + +struct xfrm_user_offload { + int ifindex; + __u8 flags; +}; +#define XFRM_OFFLOAD_IPV6 1 +#define XFRM_OFFLOAD_INBOUND 2 + +/* backwards compatibility for userspace */ +#define XFRMGRP_ACQUIRE 1 +#define XFRMGRP_EXPIRE 2 +#define XFRMGRP_SA 4 +#define XFRMGRP_POLICY 8 +#define XFRMGRP_REPORT 0x20 + +enum xfrm_nlgroups { + XFRMNLGRP_NONE, +#define XFRMNLGRP_NONE XFRMNLGRP_NONE + XFRMNLGRP_ACQUIRE, +#define XFRMNLGRP_ACQUIRE XFRMNLGRP_ACQUIRE + XFRMNLGRP_EXPIRE, +#define XFRMNLGRP_EXPIRE XFRMNLGRP_EXPIRE + XFRMNLGRP_SA, +#define XFRMNLGRP_SA XFRMNLGRP_SA + XFRMNLGRP_POLICY, +#define XFRMNLGRP_POLICY XFRMNLGRP_POLICY + XFRMNLGRP_AEVENTS, +#define XFRMNLGRP_AEVENTS XFRMNLGRP_AEVENTS + XFRMNLGRP_REPORT, +#define XFRMNLGRP_REPORT XFRMNLGRP_REPORT + XFRMNLGRP_MIGRATE, +#define XFRMNLGRP_MIGRATE XFRMNLGRP_MIGRATE + XFRMNLGRP_MAPPING, +#define XFRMNLGRP_MAPPING XFRMNLGRP_MAPPING + __XFRMNLGRP_MAX +}; +#define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1) + +#endif /* _LINUX_XFRM_H */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..7041c46 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBBSD +#include +#endif + +#include "libnetlink.h" +#include "ll_map.h" +#include "rtm_map.h" +#include "json_print.h" + +extern int preferred_family; +extern int human_readable; +extern int use_iec; +extern int show_stats; +extern int show_details; +extern int show_raw; +extern int resolve_hosts; +extern int oneline; +extern int brief; +extern int json; +extern int pretty; +extern int timestamp; +extern int timestamp_short; +extern const char * _SL_; +extern int max_flush_loops; +extern int batch_mode; +extern int numeric; +extern bool do_all; + +#ifndef CONFDIR +#define CONFDIR "/etc/iproute2" +#endif + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) +#define NEXT_ARG_OK() (argc - 1 > 0) +#define NEXT_ARG_FWD() do { argv++; argc--; } while(0) +#define PREV_ARG() do { argv--; argc++; } while(0) + +#define TIME_UNITS_PER_SEC 1000000 +#define NSEC_PER_USEC 1000 +#define NSEC_PER_MSEC 1000000 +#define NSEC_PER_SEC 1000000000LL + +typedef struct +{ + __u16 flags; + __u16 bytelen; + __s16 bitlen; + /* These next two fields match rtvia */ + __u16 family; + __u32 data[64]; +} inet_prefix; + +enum { + PREFIXLEN_SPECIFIED = (1 << 0), + ADDRTYPE_INET = (1 << 1), + ADDRTYPE_UNSPEC = (1 << 2), + ADDRTYPE_MULTI = (1 << 3), + + ADDRTYPE_INET_UNSPEC = ADDRTYPE_INET | ADDRTYPE_UNSPEC, + ADDRTYPE_INET_MULTI = ADDRTYPE_INET | ADDRTYPE_MULTI +}; + +static inline void inet_prefix_reset(inet_prefix *p) +{ + p->flags = 0; +} + +static inline bool is_addrtype_inet(const inet_prefix *p) +{ + return p->flags & ADDRTYPE_INET; +} + +static inline bool is_addrtype_inet_unspec(const inet_prefix *p) +{ + return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET_UNSPEC; +} + +static inline bool is_addrtype_inet_multi(const inet_prefix *p) +{ + return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET_MULTI; +} + +static inline bool is_addrtype_inet_not_unspec(const inet_prefix *p) +{ + return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET; +} + +static inline bool is_addrtype_inet_not_multi(const inet_prefix *p) +{ + return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET; +} + +#define DN_MAXADDL 20 +#ifndef AF_DECnet +#define AF_DECnet 12 +#endif + +struct dn_naddr +{ + unsigned short a_len; + unsigned char a_addr[DN_MAXADDL]; +}; + +#ifndef AF_MPLS +# define AF_MPLS 28 +#endif +#ifndef IPPROTO_MPLS +#define IPPROTO_MPLS 137 +#endif + +#ifndef CLOCK_TAI +# define CLOCK_TAI 11 +#endif + +#ifndef AF_XDP +# define AF_XDP 44 +# if AF_MAX < 45 +# undef AF_MAX +# define AF_MAX 45 +# endif +#endif + +__u32 get_addr32(const char *name); +int get_addr_1(inet_prefix *dst, const char *arg, int family); +int get_prefix_1(inet_prefix *dst, char *arg, int family); +int get_addr(inet_prefix *dst, const char *arg, int family); +int get_prefix(inet_prefix *dst, char *arg, int family); +int mask2bits(__u32 netmask); +int get_addr_rta(inet_prefix *dst, const struct rtattr *rta, int family); +int get_addr_ila(__u64 *val, const char *arg); + +int read_prop(const char *dev, char *prop, long *value); +int get_hex(char c); +int get_integer(int *val, const char *arg, int base); +int get_unsigned(unsigned *val, const char *arg, int base); +int get_time_rtt(unsigned *val, const char *arg, int *raw); +#define get_byte get_u8 +#define get_ushort get_u16 +#define get_short get_s16 +int get_s64(__s64 *val, const char *arg, int base); +int get_u64(__u64 *val, const char *arg, int base); +int get_u32(__u32 *val, const char *arg, int base); +int get_s32(__s32 *val, const char *arg, int base); +int get_u16(__u16 *val, const char *arg, int base); +int get_u8(__u8 *val, const char *arg, int base); +int get_be64(__be64 *val, const char *arg, int base); +int get_be32(__be32 *val, const char *arg, int base); +int get_be16(__be16 *val, const char *arg, int base); +int get_addr64(__u64 *ap, const char *cp); + +int hex2mem(const char *buf, uint8_t *mem, int count); +char *hexstring_n2a(const __u8 *str, int len, char *buf, int blen); +__u8 *hexstring_a2n(const char *str, __u8 *buf, int blen, unsigned int *len); +#define ADDR64_BUF_SIZE sizeof("xxxx:xxxx:xxxx:xxxx") +int addr64_n2a(__u64 addr, char *buff, size_t len); + +int af_bit_len(int af); + +const char *format_host_r(int af, int len, const void *addr, + char *buf, int buflen); +#define format_host_rta_r(af, rta, buf, buflen) \ + format_host_r(af, RTA_PAYLOAD(rta), RTA_DATA(rta), \ + buf, buflen) + +const char *format_host(int af, int lne, const void *addr); +#define format_host_rta(af, rta) \ + format_host(af, RTA_PAYLOAD(rta), RTA_DATA(rta)) +const char *rt_addr_n2a_r(int af, int len, const void *addr, + char *buf, int buflen); +const char *rt_addr_n2a(int af, int len, const void *addr); +#define rt_addr_n2a_rta(af, rta) \ + rt_addr_n2a(af, RTA_PAYLOAD(rta), RTA_DATA(rta)) + +int read_family(const char *name); +const char *family_name(int family); + +void missarg(const char *) __attribute__((noreturn)); +void invarg(const char *, const char *) __attribute__((noreturn)); +void duparg(const char *, const char *) __attribute__((noreturn)); +void duparg2(const char *, const char *) __attribute__((noreturn)); +int nodev(const char *dev); +int check_ifname(const char *); +int check_altifname(const char *name); +int get_ifname(char *, const char *); +const char *get_ifname_rta(int ifindex, const struct rtattr *rta); +bool matches(const char *prefix, const char *string); +int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits); +int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta); + +const char *mpls_ntop(int af, const void *addr, char *str, size_t len); +int mpls_pton(int af, const char *src, void *addr, size_t alen); + +extern int __iproute2_hz_internal; +int __get_hz(void); + +static __inline__ int get_hz(void) +{ + if (__iproute2_hz_internal == 0) + __iproute2_hz_internal = __get_hz(); + return __iproute2_hz_internal; +} + +extern int __iproute2_user_hz_internal; +int __get_user_hz(void); + +static __inline__ int get_user_hz(void) +{ + if (__iproute2_user_hz_internal == 0) + __iproute2_user_hz_internal = __get_user_hz(); + return __iproute2_user_hz_internal; +} + +static inline __u32 nl_mgrp(__u32 group) +{ + if (group > 31 ) { + fprintf(stderr, "Use setsockopt for this group %d\n", group); + exit(-1); + } + return group ? (1 << (group - 1)) : 0; +} + +/* courtesy of bridge-utils */ +static inline unsigned long __tv_to_jiffies(const struct timeval *tv) +{ + unsigned long long jif; + + jif = 1000000ULL * tv->tv_sec + tv->tv_usec; + + return jif/10000; +} + +static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) +{ + unsigned long long tvusec; + + tvusec = 10000ULL*jiffies; + tv->tv_sec = tvusec/1000000; + tv->tv_usec = tvusec - 1000000 * tv->tv_sec; +} + +void print_escape_buf(const __u8 *buf, size_t len, const char *escape); + +int print_timestamp(FILE *fp); +void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n); + +unsigned int print_name_and_link(const char *fmt, + const char *name, struct rtattr *tb[]); + +#define BIT(nr) (1UL << (nr)) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) + +#ifndef offsetof +# define offsetof(type, member) ((size_t) &((type *)0)->member) +#endif + +#ifndef min +# define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) +#endif + +#ifndef __check_format_string +# define __check_format_string(pos_str, pos_args) \ + __attribute__ ((format (printf, (pos_str), (pos_args)))) +#endif + +#define _textify(x) #x +#define textify(x) _textify(x) + +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) + +extern int cmdlineno; +ssize_t getcmdline(char **line, size_t *len, FILE *in); +int makeargs(char *line, char *argv[], int maxargs); + +char *int_to_str(int val, char *buf); +int get_guid(__u64 *guid, const char *arg); +int get_real_family(int rtm_type, int rtm_family); + +int cmd_exec(const char *cmd, char **argv, bool do_fork, + int (*setup)(void *), void *arg); +int make_path(const char *path, mode_t mode); +char *find_cgroup2_mount(bool do_mount); +__u64 get_cgroup2_id(const char *path); +char *get_cgroup2_path(__u64 id, bool full); +int get_command_name(const char *pid, char *comm, size_t len); + +int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64, + struct rtattr *tb[]); + +#ifdef NEED_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size); +size_t strlcat(char *dst, const char *src, size_t size); +#endif + +void drop_cap(void); + +int get_time(unsigned int *time, const char *str); +int get_time64(__s64 *time, const char *str); +char *sprint_time(__u32 time, char *buf); +char *sprint_time64(__s64 time, char *buf); + +#endif /* __UTILS_H__ */ diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..89d0597 --- /dev/null +++ b/include/version.h @@ -0,0 +1 @@ +static const char version[] = "5.9.0"; diff --git a/include/xt-internal.h b/include/xt-internal.h new file mode 100644 index 0000000..89c73e4 --- /dev/null +++ b/include/xt-internal.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _XTABLES_INTERNAL_H +#define _XTABLES_INTERNAL_H 1 + +#ifndef XT_LIB_DIR +# define XT_LIB_DIR "/lib/xtables" +#endif + +/* protocol family dependent informations */ +struct afinfo { + /* protocol family */ + int family; + + /* prefix of library name (ex "libipt_" */ + char *libprefix; + + /* used by setsockopt (ex IPPROTO_IP */ + int ipproto; + + /* kernel module (ex "ip_tables" */ + char *kmod; + + /* optname to check revision support of match */ + int so_rev_match; + + /* optname to check revision support of match */ + int so_rev_target; +}; + +enum xt_tryload { + DONT_LOAD, + DURING_LOAD, + TRY_LOAD, + LOAD_MUST_SUCCEED +}; + +struct xtables_rule_match { + struct xtables_rule_match *next; + struct xtables_match *match; + /* Multiple matches of the same type: the ones before + the current one are completed from parsing point of view */ + unsigned int completed; +}; + +extern char *lib_dir; + +extern void *fw_calloc(size_t count, size_t size); +extern void *fw_malloc(size_t size); + +extern const char *modprobe_program; +extern int xtables_insmod(const char *modname, const char *modprobe, int quiet); +extern int load_xtables_ko(const char *modprobe, int quiet); + +/* This is decleared in ip[6]tables.c */ +extern struct afinfo afinfo; + +/* Keeping track of external matches and targets: linked lists. */ +extern struct xtables_match *xtables_matches; +extern struct xtables_target *xtables_targets; + +extern struct xtables_match *find_match(const char *name, enum xt_tryload, + struct xtables_rule_match **match); +extern struct xtables_target *find_target(const char *name, enum xt_tryload); + +extern void _init(void); + +#endif /* _XTABLES_INTERNAL_H */ diff --git a/include/xtables.h b/include/xtables.h new file mode 100644 index 0000000..583619f --- /dev/null +++ b/include/xtables.h @@ -0,0 +1,598 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _XTABLES_H +#define _XTABLES_H + +/* + * Changing any structs/functions may incur a needed change + * in libxtables_vcurrent/vage too. + */ + +#include /* PF_* */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif +#ifndef IPPROTO_MH +# define IPPROTO_MH 135 +#endif +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +#include + +struct in_addr; + +/* + * .size is here so that there is a somewhat reasonable check + * against the chosen .type. + */ +#define XTOPT_POINTER(stype, member) \ + .ptroff = offsetof(stype, member), \ + .size = sizeof(((stype *)NULL)->member) +#define XTOPT_TABLEEND {.name = NULL} + +/** + * Select the format the input has to conform to, as well as the target type + * (area pointed to with XTOPT_POINTER). Note that the storing is not always + * uniform. @cb->val will be populated with as much as there is space, i.e. + * exactly 2 items for ranges, but the target area can receive more values + * (e.g. in case of ranges), or less values (e.g. %XTTYPE_HOSTMASK). + * + * %XTTYPE_NONE: option takes no argument + * %XTTYPE_UINT*: standard integer + * %XTTYPE_UINT*RC: colon-separated range of standard integers + * %XTTYPE_DOUBLE: double-precision floating point number + * %XTTYPE_STRING: arbitrary string + * %XTTYPE_TOSMASK: 8-bit TOS value with optional mask + * %XTTYPE_MARKMASK32: 32-bit mark with optional mask + * %XTTYPE_SYSLOGLEVEL: syslog level by name or number + * %XTTYPE_HOST: one host or address (ptr: union nf_inet_addr) + * %XTTYPE_HOSTMASK: one host or address, with an optional prefix length + * (ptr: union nf_inet_addr; only host portion is stored) + * %XTTYPE_PROTOCOL: protocol number/name from /etc/protocols (ptr: uint8_t) + * %XTTYPE_PORT: 16-bit port name or number (supports %XTOPT_NBO) + * %XTTYPE_PORTRC: colon-separated port range (names acceptable), + * (supports %XTOPT_NBO) + * %XTTYPE_PLEN: prefix length + * %XTTYPE_PLENMASK: prefix length (ptr: union nf_inet_addr) + * %XTTYPE_ETHERMAC: Ethernet MAC address in hex form + */ +enum xt_option_type { + XTTYPE_NONE, + XTTYPE_UINT8, + XTTYPE_UINT16, + XTTYPE_UINT32, + XTTYPE_UINT64, + XTTYPE_UINT8RC, + XTTYPE_UINT16RC, + XTTYPE_UINT32RC, + XTTYPE_UINT64RC, + XTTYPE_DOUBLE, + XTTYPE_STRING, + XTTYPE_TOSMASK, + XTTYPE_MARKMASK32, + XTTYPE_SYSLOGLEVEL, + XTTYPE_HOST, + XTTYPE_HOSTMASK, + XTTYPE_PROTOCOL, + XTTYPE_PORT, + XTTYPE_PORTRC, + XTTYPE_PLEN, + XTTYPE_PLENMASK, + XTTYPE_ETHERMAC, +}; + +/** + * %XTOPT_INVERT: option is invertible (usable with !) + * %XTOPT_MAND: option is mandatory + * %XTOPT_MULTI: option may be specified multiple times + * %XTOPT_PUT: store value into memory at @ptroff + * %XTOPT_NBO: store value in network-byte order + * (only certain XTTYPEs recognize this) + */ +enum xt_option_flags { + XTOPT_INVERT = 1 << 0, + XTOPT_MAND = 1 << 1, + XTOPT_MULTI = 1 << 2, + XTOPT_PUT = 1 << 3, + XTOPT_NBO = 1 << 4, +}; + +/** + * @name: name of option + * @type: type of input and validation method, see %XTTYPE_* + * @id: unique number (within extension) for option, 0-31 + * @excl: bitmask of flags that cannot be used with this option + * @also: bitmask of flags that must be used with this option + * @flags: bitmask of option flags, see %XTOPT_* + * @ptroff: offset into private structure for member + * @size: size of the item pointed to by @ptroff; this is a safeguard + * @min: lowest allowed value (for singular integral types) + * @max: highest allowed value (for singular integral types) + */ +struct xt_option_entry { + const char *name; + enum xt_option_type type; + unsigned int id, excl, also, flags; + unsigned int ptroff; + size_t size; + unsigned int min, max; +}; + +/** + * @arg: input from command line + * @ext_name: name of extension currently being processed + * @entry: current option being processed + * @data: per-extension kernel data block + * @xflags: options of the extension that have been used + * @invert: whether option was used with ! + * @nvals: number of results in uXX_multi + * @val: parsed result + * @udata: per-extension private scratch area + * (cf. xtables_{match,target}->udata_size) + */ +struct xt_option_call { + const char *arg, *ext_name; + const struct xt_option_entry *entry; + void *data; + unsigned int xflags; + bool invert; + uint8_t nvals; + union { + uint8_t u8, u8_range[2], syslog_level, protocol; + uint16_t u16, u16_range[2], port, port_range[2]; + uint32_t u32, u32_range[2]; + uint64_t u64, u64_range[2]; + double dbl; + struct { + union nf_inet_addr haddr, hmask; + uint8_t hlen; + }; + struct { + uint8_t tos_value, tos_mask; + }; + struct { + uint32_t mark, mask; + }; + uint8_t ethermac[6]; + } val; + /* Wished for a world where the ones below were gone: */ + union { + struct xt_entry_match **match; + struct xt_entry_target **target; + }; + void *xt_entry; + void *udata; +}; + +/** + * @ext_name: name of extension currently being processed + * @data: per-extension (kernel) data block + * @udata: per-extension private scratch area + * (cf. xtables_{match,target}->udata_size) + * @xflags: options of the extension that have been used + */ +struct xt_fcheck_call { + const char *ext_name; + void *data, *udata; + unsigned int xflags; +}; + +/** + * A "linear"/linked-list based name<->id map, for files similar to + * /etc/iproute2/. + */ +struct xtables_lmap { + char *name; + int id; + struct xtables_lmap *next; +}; + +enum xtables_ext_flags { + XTABLES_EXT_ALIAS = 1 << 0, +}; + +#if XTABLES_VERSION_CODE >= 12 +struct xt_xlate; + +struct xt_xlate_mt_params { + const void *ip; + const struct xt_entry_match *match; + int numeric; + bool escape_quotes; +}; + +struct xt_xlate_tg_params { + const void *ip; + const struct xt_entry_target *target; + int numeric; + bool escape_quotes; +}; +#endif + +/* Include file for additions: new matches and targets. */ +struct xtables_match +{ + /* + * ABI/API version this module requires. Must be first member, + * as the rest of this struct may be subject to ABI changes. + */ + const char *version; + + struct xtables_match *next; + + const char *name; + const char *real_name; + + /* Revision of match (0 by default). */ + uint8_t revision; + + /* Extension flags */ + uint8_t ext_flags; + + uint16_t family; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevant for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct xt_entry_match *m); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + /* ip is struct ipt_ip * for example */ + void (*print)(const void *ip, + const struct xt_entry_match *match, int numeric); + + /* Saves the match info in parsable form to stdout. */ + /* ip is struct ipt_ip * for example */ + void (*save)(const void *ip, const struct xt_entry_match *match); + + /* Print match name or alias */ + const char *(*alias)(const struct xt_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* New parser */ + void (*x6_parse)(struct xt_option_call *); + void (*x6_fcheck)(struct xt_fcheck_call *); + const struct xt_option_entry *x6_options; + +#if XTABLES_VERSION_CODE >= 12 + /* Translate iptables to nft */ + int (*xlate)(struct xt_xlate *xl, + const struct xt_xlate_mt_params *params); +#endif + + /* Size of per-extension instance extra "global" scratch space */ + size_t udata_size; + + /* Ignore these men behind the curtain: */ + void *udata; + unsigned int option_offset; + struct xt_entry_match *m; + unsigned int mflags; + unsigned int loaded; /* simulate loading so options are merged properly */ +}; + +struct xtables_target +{ + /* + * ABI/API version this module requires. Must be first member, + * as the rest of this struct may be subject to ABI changes. + */ + const char *version; + + struct xtables_target *next; + + + const char *name; + + /* Real target behind this, if any. */ + const char *real_name; + + /* Revision of target (0 by default). */ + uint8_t revision; + + /* Extension flags */ + uint8_t ext_flags; + + uint16_t family; + + + /* Size of target data. */ + size_t size; + + /* Size of target data relevant for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct xt_entry_target *t); + + /* Function which parses command options; returns true if it + ate an option */ + /* entry is struct ipt_entry for example */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_target **targetinfo); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const void *ip, + const struct xt_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const void *ip, + const struct xt_entry_target *target); + + /* Print target name or alias */ + const char *(*alias)(const struct xt_entry_target *target); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* New parser */ + void (*x6_parse)(struct xt_option_call *); + void (*x6_fcheck)(struct xt_fcheck_call *); + const struct xt_option_entry *x6_options; + +#if XTABLES_VERSION_CODE >= 12 + /* Translate iptables to nft */ + int (*xlate)(struct xt_xlate *xl, + const struct xt_xlate_tg_params *params); +#endif + + size_t udata_size; + + /* Ignore these men behind the curtain: */ + void *udata; + unsigned int option_offset; + struct xt_entry_target *t; + unsigned int tflags; + unsigned int used; + unsigned int loaded; /* simulate loading so options are merged properly */ +}; + +struct xtables_rule_match { + struct xtables_rule_match *next; + struct xtables_match *match; + /* Multiple matches of the same type: the ones before + the current one are completed from parsing point of view */ + bool completed; +}; + +/** + * struct xtables_pprot - + * + * A few hardcoded protocols for 'all' and in case the user has no + * /etc/protocols. + */ +struct xtables_pprot { + const char *name; + uint8_t num; +}; + +enum xtables_tryload { + XTF_DONT_LOAD, + XTF_DURING_LOAD, + XTF_TRY_LOAD, + XTF_LOAD_MUST_SUCCEED, +}; + +enum xtables_exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM, + RESOURCE_PROBLEM, + XTF_ONLY_ONCE, + XTF_NO_INVERT, + XTF_BAD_VALUE, + XTF_ONE_ACTION, +}; + +struct xtables_globals +{ + unsigned int option_offset; + const char *program_name, *program_version; + struct option *orig_opts; + struct option *opts; + void (*exit_err)(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); + int (*compat_rev)(const char *name, uint8_t rev, int opt); +}; + +#define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false} + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *xtables_modprobe_program; +extern struct xtables_match *xtables_matches; +extern struct xtables_target *xtables_targets; + +extern void xtables_init(void); +extern void xtables_set_nfproto(uint8_t); +extern void *xtables_calloc(size_t, size_t); +extern void *xtables_malloc(size_t); +extern void *xtables_realloc(void *, size_t); + +extern int xtables_insmod(const char *, const char *, bool); +extern int xtables_load_ko(const char *, bool); +extern int xtables_set_params(struct xtables_globals *xtp); +extern void xtables_free_opts(int reset_offset); +extern struct option *xtables_merge_options(struct option *origopts, + struct option *oldopts, const struct option *newopts, + unsigned int *option_offset); + +extern int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto); +extern struct xtables_match *xtables_find_match(const char *name, + enum xtables_tryload, struct xtables_rule_match **match); +extern struct xtables_target *xtables_find_target(const char *name, + enum xtables_tryload); +extern int xtables_compatible_revision(const char *name, uint8_t revision, + int opt); + +extern void xtables_rule_matches_free(struct xtables_rule_match **matches); + +/* Your shared library should call one of these. */ +extern void xtables_register_match(struct xtables_match *me); +extern void xtables_register_matches(struct xtables_match *, unsigned int); +extern void xtables_register_target(struct xtables_target *me); +extern void xtables_register_targets(struct xtables_target *, unsigned int); + +extern bool xtables_strtoul(const char *, char **, uintmax_t *, + uintmax_t, uintmax_t); +extern bool xtables_strtoui(const char *, char **, unsigned int *, + unsigned int, unsigned int); +extern int xtables_service_to_port(const char *name, const char *proto); +extern uint16_t xtables_parse_port(const char *port, const char *proto); +extern void +xtables_parse_interface(const char *arg, char *vianame, unsigned char *mask); + +/* this is a special 64bit data type that is 8-byte aligned */ +#define aligned_u64 uint64_t __attribute__((aligned(8))) + +extern struct xtables_globals *xt_params; +#define xtables_error (xt_params->exit_err) + +extern void xtables_param_act(unsigned int, const char *, ...); + +extern const char *xtables_ipaddr_to_numeric(const struct in_addr *); +extern const char *xtables_ipaddr_to_anyname(const struct in_addr *); +extern const char *xtables_ipmask_to_numeric(const struct in_addr *); +extern struct in_addr *xtables_numeric_to_ipaddr(const char *); +extern struct in_addr *xtables_numeric_to_ipmask(const char *); +extern int xtables_ipmask_to_cidr(const struct in_addr *); +extern void xtables_ipparse_any(const char *, struct in_addr **, + struct in_addr *, unsigned int *); +extern void xtables_ipparse_multiple(const char *, struct in_addr **, + struct in_addr **, unsigned int *); + +extern struct in6_addr *xtables_numeric_to_ip6addr(const char *); +extern const char *xtables_ip6addr_to_numeric(const struct in6_addr *); +extern const char *xtables_ip6addr_to_anyname(const struct in6_addr *); +extern const char *xtables_ip6mask_to_numeric(const struct in6_addr *); +extern int xtables_ip6mask_to_cidr(const struct in6_addr *); +extern void xtables_ip6parse_any(const char *, struct in6_addr **, + struct in6_addr *, unsigned int *); +extern void xtables_ip6parse_multiple(const char *, struct in6_addr **, + struct in6_addr **, unsigned int *); + +/** + * Print the specified value to standard output, quoting dangerous + * characters if required. + */ +extern void xtables_save_string(const char *value); + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + +extern void xtables_print_num(uint64_t number, unsigned int format); + +#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) +# ifdef _INIT +# undef _init +# define _init _INIT +# endif + extern void init_extensions(void); + extern void init_extensions4(void); + extern void init_extensions6(void); +#else +# define _init __attribute__((constructor)) _INIT +#endif + +extern const struct xtables_pprot xtables_chain_protos[]; +extern uint16_t xtables_parse_protocol(const char *s); + +/* kernel revision handling */ +extern int kernel_version; +extern void get_kernel_version(void); +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) +#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF) +#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF) +#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF) + +/* xtoptions.c */ +extern void xtables_option_metavalidate(const char *, + const struct xt_option_entry *); +extern struct option *xtables_options_xfrm(struct option *, struct option *, + const struct xt_option_entry *, + unsigned int *); +extern void xtables_option_parse(struct xt_option_call *); +extern void xtables_option_tpcall(unsigned int, char **, bool, + struct xtables_target *, void *); +extern void xtables_option_mpcall(unsigned int, char **, bool, + struct xtables_match *, void *); +extern void xtables_option_tfcall(struct xtables_target *); +extern void xtables_option_mfcall(struct xtables_match *); +extern void xtables_options_fcheck(const char *, unsigned int, + const struct xt_option_entry *); + +extern struct xtables_lmap *xtables_lmap_init(const char *); +extern void xtables_lmap_free(struct xtables_lmap *); +extern int xtables_lmap_name2id(const struct xtables_lmap *, const char *); +extern const char *xtables_lmap_id2name(const struct xtables_lmap *, int); + +#ifdef XTABLES_INTERNAL + +/* Shipped modules rely on this... */ + +# ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +# endif + +extern void _init(void); + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _XTABLES_H */ diff --git a/ip/.gitignore b/ip/.gitignore new file mode 100644 index 0000000..b95b232 --- /dev/null +++ b/ip/.gitignore @@ -0,0 +1,2 @@ +ip +rtmon diff --git a/ip/Makefile b/ip/Makefile new file mode 100644 index 0000000..4cad619 --- /dev/null +++ b/ip/Makefile @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0 +IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ + rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \ + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o iptoken.o \ + ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o iplink_dummy.o \ + iplink_ifb.o iplink_nlmon.o iplink_team.o iplink_vcan.o iplink_vxcan.o \ + iplink_vlan.o link_veth.o link_gre.o iplink_can.o iplink_xdp.o \ + iplink_macvlan.o ipl2tp.o link_vti.o link_vti6.o link_xfrm.o \ + iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \ + link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \ + iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ + iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \ + ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \ + ipnexthop.o ipmptcp.o iplink_bareudp.o + +RTMONOBJ=rtmon.o + +include ../config.mk + +ALLOBJ=$(IPOBJ) $(RTMONOBJ) +SCRIPTS=ifcfg rtpr routel routef +TARGETS=ip rtmon + +all: $(TARGETS) $(SCRIPTS) + +ip: $(IPOBJ) $(LIBNETLINK) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +rtmon: $(RTMONOBJ) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) + install -m 0755 $(SCRIPTS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(ALLOBJ) $(TARGETS) + +SHARED_LIBS ?= y +ifeq ($(SHARED_LIBS),y) + +LDLIBS += -ldl +LDFLAGS += -Wl,-export-dynamic + +else + +ip: static-syms.o +static-syms.o: static-syms.h +static-syms.h: $(wildcard *.c) + files="$^" ; \ + for s in `grep -B 3 '\ $@ + +endif diff --git a/ip/ifcfg b/ip/ifcfg new file mode 100755 index 0000000..5b34dec --- /dev/null +++ b/ip/ifcfg @@ -0,0 +1,150 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 + +CheckForwarding () { + local sbase fwd + sbase=/proc/sys/net/ipv4/conf + fwd=0 + if [ -d $sbase ]; then + for dir in $sbase/*/forwarding; do + fwd=$(( fwd + $(cat "$dir") )) + done + else + fwd=2 + fi + return $fwd +} + +RestartRDISC () { + killall -HUP rdisc || rdisc -fs +} + +ABCMaskLen () { + local class; + + class=${1%%.*} + if [ "$1" = "" -o $class -eq 0 -o $class -ge 224 ]; then return 0 + elif [ $class -ge 224 ]; then return 0 + elif [ $class -ge 192 ]; then return 24 + elif [ $class -ge 128 ]; then return 16 + else return 8; fi +} + +label="label $1" +ldev="$1" +dev=${1%:*} +if [ "$dev" = "" -o "$1" = "help" ]; then + echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2 + echo " add - add new address" 1>&2 + echo " del - delete address" 1>&2 + echo " stop - completely disable IP" 1>&2 + exit 1 +fi +shift + +CheckForwarding +fwd=$? +if [ $fwd -ne 0 ]; then + echo "Forwarding is ON or its state is unknown ($fwd). OK, No RDISC." 1>&2 +fi + + +deleting=0 +case "$1" in +add) shift ;; +stop) + if [ "$ldev" != "$dev" ]; then + echo "Cannot stop alias $ldev" 1>&2 + exit 1; + fi + ip -4 addr flush dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 ;; +del*) + deleting=1; shift ;; +*) +esac + +ipaddr= +pfxlen= +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + if [ "$ipaddr" = "" ]; then + echo "$1 is bad IP address." 1>&2 + exit 1 + fi +fi +shift + +peer=$1 +if [ "$peer" != "" ]; then + if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then + echo "Peer address with non-trivial netmask." 1>&2 + exit 1 + fi + pfx="$ipaddr peer $peer" +else + if [ "$ipaddr" = "" ]; then + echo "Missing IP address argument." 1>&2 + exit 1 + fi + if [ "$pfxlen" = "" ]; then + ABCMaskLen $ipaddr + pfxlen=$? + fi + pfx="$ipaddr/$pfxlen" +fi + +if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then + label= +fi + +if [ $deleting -ne 0 ]; then + ip addr del $pfx dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 +fi + + +if ! ip link set up dev $dev ; then + echo "Error: cannot enable interface $dev." 1>&2 + exit 1 +fi +if [ "$ipaddr" = "" ]; then exit 0; fi + +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Error: some host already uses address $ipaddr on $dev." 1>&2 + exit 1 +fi + +if ! ip address add $pfx brd + dev $dev $label; then + echo "Error: failed to add $pfx on $dev." 1>&2 + exit 1 +fi + +arping -q -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2 ; + arping -q -U -c 1 -I $dev $ipaddr ) >/dev/null 2>&1 /dev/null 2>&1 +ip route add unreachable 255.255.255.255 >/dev/null 2>&1 +if [ "`ip link ls $dev | grep -c MULTICAST`" -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global >/dev/null 2>&1 +fi + +if [ $fwd -eq 0 ]; then + if [ $noarp -eq 0 ]; then + ip ro append default dev $dev metric 30000 scope global + elif [ "$peer" != "" ]; then + if ping -q -c 2 -w 4 $peer ; then + ip ro append default via $peer dev $dev metric 30001 + fi + fi + RestartRDISC +fi + +exit 0 diff --git a/ip/ila_common.h b/ip/ila_common.h new file mode 100644 index 0000000..f99c267 --- /dev/null +++ b/ip/ila_common.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ILA_COMMON_H_ +#define _ILA_COMMON_H_ + +#include +#include + +static inline char *ila_csum_mode2name(__u8 csum_mode) +{ + switch (csum_mode) { + case ILA_CSUM_ADJUST_TRANSPORT: + return "adj-transport"; + case ILA_CSUM_NEUTRAL_MAP: + return "neutral-map"; + case ILA_CSUM_NO_ACTION: + return "no-action"; + case ILA_CSUM_NEUTRAL_MAP_AUTO: + return "neutral-map-auto"; + default: + return "unknown"; + } +} + +static inline int ila_csum_name2mode(char *name) +{ + if (strcmp(name, "adj-transport") == 0) + return ILA_CSUM_ADJUST_TRANSPORT; + else if (strcmp(name, "neutral-map") == 0) + return ILA_CSUM_NEUTRAL_MAP; + else if (strcmp(name, "neutral-map-auto") == 0) + return ILA_CSUM_NEUTRAL_MAP_AUTO; + else if (strcmp(name, "no-action") == 0) + return ILA_CSUM_NO_ACTION; + else if (strcmp(name, "neutral-map-auto") == 0) + return ILA_CSUM_NEUTRAL_MAP_AUTO; + else + return -1; +} + +static inline char *ila_ident_type2name(__u8 ident_type) +{ + switch (ident_type) { + case ILA_ATYPE_IID: + return "iid"; + case ILA_ATYPE_LUID: + return "luid"; + case ILA_ATYPE_VIRT_V4: + return "virt-v4"; + case ILA_ATYPE_VIRT_UNI_V6: + return "virt-uni-v6"; + case ILA_ATYPE_VIRT_MULTI_V6: + return "virt-multi-v6"; + case ILA_ATYPE_NONLOCAL_ADDR: + return "nonlocal-addr"; + case ILA_ATYPE_USE_FORMAT: + return "use-format"; + default: + return "unknown"; + } +} + +static inline int ila_ident_name2type(char *name) +{ + if (!strcmp(name, "luid")) + return ILA_ATYPE_LUID; + else if (!strcmp(name, "use-format")) + return ILA_ATYPE_USE_FORMAT; +#if 0 /* No kernel support for configuring these yet */ + else if (!strcmp(name, "iid")) + return ILA_ATYPE_IID; + else if (!strcmp(name, "virt-v4")) + return ILA_ATYPE_VIRT_V4; + else if (!strcmp(name, "virt-uni-v6")) + return ILA_ATYPE_VIRT_UNI_V6; + else if (!strcmp(name, "virt-multi-v6")) + return ILA_ATYPE_VIRT_MULTI_V6; + else if (!strcmp(name, "nonlocal-addr")) + return ILA_ATYPE_NONLOCAL_ADDR; +#endif + else + return -1; +} + +static inline char *ila_hook_type2name(__u8 hook_type) +{ + switch (hook_type) { + case ILA_HOOK_ROUTE_OUTPUT: + return "output"; + case ILA_HOOK_ROUTE_INPUT: + return "input"; + default: + return "unknown"; + } +} + +static inline int ila_hook_name2type(char *name) +{ + if (!strcmp(name, "output")) + return ILA_HOOK_ROUTE_OUTPUT; + else if (!strcmp(name, "input")) + return ILA_HOOK_ROUTE_INPUT; + else + return -1; +} + +#endif /* _ILA_COMMON_H_ */ diff --git a/ip/ip.c b/ip/ip.c new file mode 100644 index 0000000..ac44502 --- /dev/null +++ b/ip/ip.c @@ -0,0 +1,327 @@ +/* + * ip.c "ip" utility frontend. + * + * 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. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "utils.h" +#include "ip_common.h" +#include "namespace.h" +#include "color.h" +#include "rt_names.h" + +int preferred_family = AF_UNSPEC; +int human_readable; +int use_iec; +int show_stats; +int show_details; +int oneline; +int brief; +int json; +int timestamp; +int force; +int max_flush_loops = 10; +int batch_mode; +bool do_all; + +struct rtnl_handle rth = { .fd = -1 }; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" + " ip [ -force ] -batch filename\n" + "where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n" + " tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n" + " netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n" + " vrf | sr | nexthop | mptcp }\n" + " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" + " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" + " -f[amily] { inet | inet6 | mpls | bridge | link } |\n" + " -4 | -6 | -I | -D | -M | -B | -0 |\n" + " -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n" + " -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n" + " -rc[vbuf] [size] | -n[etns] name | -N[umeric] | -a[ll] |\n" + " -c[olor]}\n"); + exit(-1); +} + +static int do_help(int argc, char **argv) +{ + usage(); + return 0; +} + +static const struct cmd { + const char *cmd; + int (*func)(int argc, char **argv); +} cmds[] = { + { "address", do_ipaddr }, + { "addrlabel", do_ipaddrlabel }, + { "maddress", do_multiaddr }, + { "route", do_iproute }, + { "rule", do_iprule }, + { "neighbor", do_ipneigh }, + { "neighbour", do_ipneigh }, + { "ntable", do_ipntable }, + { "ntbl", do_ipntable }, + { "link", do_iplink }, + { "l2tp", do_ipl2tp }, + { "fou", do_ipfou }, + { "ila", do_ipila }, + { "macsec", do_ipmacsec }, + { "tunnel", do_iptunnel }, + { "tunl", do_iptunnel }, + { "tuntap", do_iptuntap }, + { "tap", do_iptuntap }, + { "token", do_iptoken }, + { "tcpmetrics", do_tcp_metrics }, + { "tcp_metrics", do_tcp_metrics }, + { "monitor", do_ipmonitor }, + { "xfrm", do_xfrm }, + { "mroute", do_multiroute }, + { "mrule", do_multirule }, + { "netns", do_netns }, + { "netconf", do_ipnetconf }, + { "vrf", do_ipvrf}, + { "sr", do_seg6 }, + { "nexthop", do_ipnh }, + { "mptcp", do_mptcp }, + { "help", do_help }, + { 0 } +}; + +static int do_cmd(const char *argv0, int argc, char **argv) +{ + const struct cmd *c; + + for (c = cmds; c->cmd; ++c) { + if (matches(argv0, c->cmd) == 0) + return -(c->func(argc-1, argv+1)); + } + + fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0); + return EXIT_FAILURE; +} + +static int batch(const char *name) +{ + char *line = NULL; + size_t len = 0; + int ret = EXIT_SUCCESS; + int orig_family = preferred_family; + + batch_mode = 1; + + if (name && strcmp(name, "-") != 0) { + if (freopen(name, "r", stdin) == NULL) { + fprintf(stderr, + "Cannot open file \"%s\" for reading: %s\n", + name, strerror(errno)); + return EXIT_FAILURE; + } + } + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return EXIT_FAILURE; + } + + cmdlineno = 0; + while (getcmdline(&line, &len, stdin) != -1) { + char *largv[100]; + int largc; + + preferred_family = orig_family; + + largc = makeargs(line, largv, 100); + if (largc == 0) + continue; /* blank line */ + + if (do_cmd(largv[0], largc, largv)) { + fprintf(stderr, "Command failed %s:%d\n", + name, cmdlineno); + ret = EXIT_FAILURE; + if (!force) + break; + } + } + if (line) + free(line); + + rtnl_close(&rth); + return ret; +} + + +int main(int argc, char **argv) +{ + char *basename; + char *batch_file = NULL; + int color = 0; + + /* to run vrf exec without root, capabilities might be set, drop them + * if not needed as the first thing. + * execv will drop them for the child command. + * vrf exec requires: + * - cap_dac_override to create the cgroup subdir in /sys + * - cap_sys_admin to load the BPF program + * - cap_net_admin to set the socket into the cgroup + */ + if (argc < 3 || strcmp(argv[1], "vrf") != 0 || + strcmp(argv[2], "exec") != 0) + drop_cap(); + + basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else + basename++; + + while (argc > 1) { + char *opt = argv[1]; + + if (strcmp(opt, "--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + if (matches(opt, "-loops") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + max_flush_loops = atoi(argv[1]); + } else if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "help") == 0) + usage(); + else + preferred_family = read_family(argv[1]); + if (preferred_family == AF_UNSPEC) + invarg("invalid protocol family", argv[1]); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (strcmp(opt, "-0") == 0) { + preferred_family = AF_PACKET; + } else if (strcmp(opt, "-D") == 0) { + preferred_family = AF_DECnet; + } else if (strcmp(opt, "-M") == 0) { + preferred_family = AF_MPLS; + } else if (strcmp(opt, "-B") == 0) { + preferred_family = AF_BRIDGE; + } else if (matches(opt, "-human") == 0 || + matches(opt, "-human-readable") == 0) { + ++human_readable; + } else if (matches(opt, "-iec") == 0) { + ++use_iec; + } else if (matches(opt, "-stats") == 0 || + matches(opt, "-statistics") == 0) { + ++show_stats; + } else if (matches(opt, "-details") == 0) { + ++show_details; + } else if (matches(opt, "-resolve") == 0) { + ++resolve_hosts; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; + } else if (matches(opt, "-timestamp") == 0) { + ++timestamp; + } else if (matches(opt, "-tshort") == 0) { + ++timestamp; + ++timestamp_short; + } else if (matches(opt, "-Version") == 0) { + printf("ip utility, iproute2-%s\n", version); + exit(0); + } else if (matches(opt, "-force") == 0) { + ++force; + } else if (matches(opt, "-batch") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + batch_file = argv[1]; + } else if (matches(opt, "-brief") == 0) { + ++brief; + } else if (matches(opt, "-json") == 0) { + ++json; + } else if (matches(opt, "-pretty") == 0) { + ++pretty; + } else if (matches(opt, "-rcvbuf") == 0) { + unsigned int size; + + argc--; + argv++; + if (argc <= 1) + usage(); + if (get_unsigned(&size, argv[1], 0)) { + fprintf(stderr, "Invalid rcvbuf size '%s'\n", + argv[1]); + exit(-1); + } + rcvbuf = size; + } else if (matches_color(opt, &color)) { + } else if (matches(opt, "-help") == 0) { + usage(); + } else if (matches(opt, "-netns") == 0) { + NEXT_ARG(); + if (netns_switch(argv[1])) + exit(-1); + } else if (matches(opt, "-Numeric") == 0) { + ++numeric; + } else if (matches(opt, "-all") == 0) { + do_all = true; + } else { + fprintf(stderr, + "Option \"%s\" is unknown, try \"ip -help\".\n", + opt); + exit(-1); + } + argc--; argv++; + } + + _SL_ = oneline ? "\\" : "\n"; + + check_enable_color(color, json); + + if (batch_file) + return batch(batch_file); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + rtnl_set_strict_dump(&rth); + + if (strlen(basename) > 2) + return do_cmd(basename+2, argc, argv); + + if (argc > 1) + return do_cmd(argv[1], argc-1, argv+1); + + rtnl_close(&rth); + usage(); +} diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c new file mode 100644 index 0000000..5399f91 --- /dev/null +++ b/ip/ip6tunnel.c @@ -0,0 +1,450 @@ +/* + * Copyright (C)2006 USAGI/WIDE Project + * + * 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, see . + */ +/* + * Author: + * Masahide NAKAMURA @USAGI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tunnel.h" +#include "ip_common.h" + +#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000) +#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF) + +#define DEFAULT_TNL_HOP_LIMIT (64) + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n" + " [ mode { ip6ip6 | ipip6 | ip6gre | vti6 | any } ]\n" + " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n" + " [ encaplimit ELIM ]\n" + " [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n" + " [ dscp inherit ]\n" + " [ [no]allow-localremote ]\n" + " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" + "\n" + "Where: NAME := STRING\n" + " ADDR := IPV6_ADDRESS\n" + " ELIM := { none | 0..255 }(default=%d)\n" + " TTL := 0..255 (default=%d)\n" + " TCLASS := { 0x0..0xff | inherit }\n" + " FLOWLABEL := { 0x0..0xfffff | inherit }\n" + " KEY := { DOTTED_QUAD | NUMBER }\n", + IPV6_DEFAULT_TNL_ENCAP_LIMIT, + DEFAULT_TNL_HOP_LIMIT); + exit(-1); +} + +static void print_tunnel(const void *t) +{ + const struct ip6_tnl_parm2 *p = t; + SPRINT_BUF(b1); + + /* Do not use format_host() for local addr, + * symbolic name will not be useful. + */ + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name); + snprintf(b1, sizeof(b1), "%s/ipv6", tnl_strproto(p->proto)); + print_string(PRINT_ANY, "mode", "%s ", b1); + print_string(PRINT_FP, NULL, "%s", "remote "); + print_color_string(PRINT_ANY, COLOR_INET6, "remote", "%s ", + format_host_r(AF_INET6, 16, &p->raddr, b1, sizeof(b1))); + print_string(PRINT_FP, NULL, "%s", "local "); + print_color_string(PRINT_ANY, COLOR_INET6, "local", "%s", + rt_addr_n2a_r(AF_INET6, 16, &p->laddr, b1, sizeof(b1))); + + if (p->link) { + const char *n = ll_index_to_name(p->link); + + if (n) + print_string(PRINT_ANY, "link", " dev %s", n); + } + + if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT) + print_null(PRINT_ANY, "ip6_tnl_f_ign_encap_limit", + " encaplimit none", NULL); + else + print_uint(PRINT_ANY, "encap_limit", " encaplimit %u", + p->encap_limit); + + if (p->hop_limit) + print_uint(PRINT_ANY, "hoplimit", " hoplimit %u", p->hop_limit); + else + print_string(PRINT_FP, "hoplimit", " hoplimit %s", "inherit"); + + if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS) { + print_null(PRINT_ANY, "ip6_tnl_f_use_orig_tclass", + " tclass inherit", NULL); + } else { + __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS); + + snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20)); + print_string(PRINT_ANY, "tclass", " tclass %s", b1); + } + + if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) { + print_null(PRINT_ANY, "ip6_tnl_f_use_orig_flowlabel", + " flowlabel inherit", NULL); + } else { + __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL); + + snprintf(b1, sizeof(b1), "0x%05x", val); + print_string(PRINT_ANY, "flowlabel", " flowlabel %s", b1); + } + + snprintf(b1, sizeof(b1), "0x%08x", ntohl(p->flowinfo)); + print_string(PRINT_ANY, "flowinfo", " (flowinfo %s)", b1); + + if (p->flags & IP6_TNL_F_RCV_DSCP_COPY) + print_null(PRINT_ANY, "ip6_tnl_f_rcv_dscp_copy", + " dscp inherit", NULL); + + if (p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) + print_null(PRINT_ANY, "ip6_tnl_f_allow_local_remote", + " allow-localremote", NULL); + + tnl_print_gre_flags(p->proto, p->i_flags, p->o_flags, + p->i_key, p->o_key); + + close_json_object(); +} + +static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p) +{ + int count = 0; + const char *medium = NULL; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipv6/ipv6") == 0 || + strcmp(*argv, "ip6ip6") == 0) + p->proto = IPPROTO_IPV6; + else if (strcmp(*argv, "vti6") == 0) { + p->proto = IPPROTO_IPV6; + p->i_flags |= VTI_ISVTI; + } else if (strcmp(*argv, "ip/ipv6") == 0 || + strcmp(*argv, "ipv4/ipv6") == 0 || + strcmp(*argv, "ipip6") == 0 || + strcmp(*argv, "ip4ip6") == 0) + p->proto = IPPROTO_IPIP; + else if (strcmp(*argv, "ip6gre") == 0 || + strcmp(*argv, "gre/ipv6") == 0) + p->proto = IPPROTO_GRE; + else if (strcmp(*argv, "any/ipv6") == 0 || + strcmp(*argv, "any") == 0) + p->proto = 0; + else { + fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv); + exit(-1); + } + } else if (strcmp(*argv, "remote") == 0) { + inet_prefix raddr; + + NEXT_ARG(); + get_addr(&raddr, *argv, AF_INET6); + memcpy(&p->raddr, &raddr.data, sizeof(p->raddr)); + } else if (strcmp(*argv, "local") == 0) { + inet_prefix laddr; + + NEXT_ARG(); + get_addr(&laddr, *argv, AF_INET6); + memcpy(&p->laddr, &laddr.data, sizeof(p->laddr)); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + medium = *argv; + } else if (strcmp(*argv, "encaplimit") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "none") == 0) { + p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + } else { + __u8 uval; + + if (get_u8(&uval, *argv, 0) < -1) + invarg("invalid ELIM", *argv); + p->encap_limit = uval; + p->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } else if (strcmp(*argv, "hoplimit") == 0 || + strcmp(*argv, "ttl") == 0 || + strcmp(*argv, "hlim") == 0) { + __u8 uval; + + NEXT_ARG(); + if (get_u8(&uval, *argv, 0)) + invarg("invalid TTL", *argv); + p->hop_limit = uval; + } else if (strcmp(*argv, "tclass") == 0 || + strcmp(*argv, "tc") == 0 || + strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u8 uval; + + NEXT_ARG(); + p->flowinfo &= ~IP6_FLOWINFO_TCLASS; + if (strcmp(*argv, "inherit") == 0) + p->flags |= IP6_TNL_F_USE_ORIG_TCLASS; + else { + if (get_u8(&uval, *argv, 16)) + invarg("invalid TClass", *argv); + p->flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS; + p->flags &= ~IP6_TNL_F_USE_ORIG_TCLASS; + } + } else if (strcmp(*argv, "flowlabel") == 0 || + strcmp(*argv, "fl") == 0) { + __u32 uval; + + NEXT_ARG(); + p->flowinfo &= ~IP6_FLOWINFO_FLOWLABEL; + if (strcmp(*argv, "inherit") == 0) + p->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL; + else { + if (get_u32(&uval, *argv, 16)) + invarg("invalid Flowlabel", *argv); + if (uval > 0xFFFFF) + invarg("invalid Flowlabel", *argv); + p->flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL; + p->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; + } + } else if (strcmp(*argv, "dscp") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) + invarg("not inherit", *argv); + p->flags |= IP6_TNL_F_RCV_DSCP_COPY; + } else if (strcmp(*argv, "allow-localremote") == 0) { + p->flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "noallow-localremote") == 0) { + p->flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "key") == 0) { + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->o_flags |= GRE_KEY; + p->i_key = p->o_key = tnl_parse_key("key", *argv); + } else if (strcmp(*argv, "ikey") == 0) { + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->i_key = tnl_parse_key("ikey", *argv); + } else if (strcmp(*argv, "okey") == 0) { + NEXT_ARG(); + p->o_flags |= GRE_KEY; + p->o_key = tnl_parse_key("okey", *argv); + } else if (strcmp(*argv, "seq") == 0) { + p->i_flags |= GRE_SEQ; + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "iseq") == 0) { + p->i_flags |= GRE_SEQ; + } else if (strcmp(*argv, "oseq") == 0) { + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "csum") == 0) { + p->i_flags |= GRE_CSUM; + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "icsum") == 0) { + p->i_flags |= GRE_CSUM; + } else if (strcmp(*argv, "ocsum") == 0) { + p->o_flags |= GRE_CSUM; + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } else if (matches(*argv, "help") == 0) + usage(); + if (p->name[0]) + duparg2("name", *argv); + if (get_ifname(p->name, *argv)) + invarg("\"name\" not a valid ifname", *argv); + if (cmd == SIOCCHGTUNNEL && count == 0) { + struct ip6_tnl_parm2 old_p = {}; + + if (tnl_get_ioctl(*argv, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + if (medium) { + p->link = ll_name_to_index(medium); + if (!p->link) + return nodev(medium); + } + return 0; +} + +static void ip6_tnl_parm_init(struct ip6_tnl_parm2 *p, int apply_default) +{ + memset(p, 0, sizeof(*p)); + p->proto = IPPROTO_IPV6; + if (apply_default) { + p->hop_limit = DEFAULT_TNL_HOP_LIMIT; + p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + } +} + +static void ip6_tnl_parm_initialize(const struct tnl_print_nlmsg_info *info) +{ + const struct ifinfomsg *ifi = info->ifi; + const struct ip6_tnl_parm2 *p1 = info->p1; + struct ip6_tnl_parm2 *p2 = info->p2; + + ip6_tnl_parm_init(p2, 0); + if (ifi->ifi_type == ARPHRD_IP6GRE) + p2->proto = IPPROTO_GRE; + p2->link = ifi->ifi_index; + strcpy(p2->name, p1->name); +} + +static bool ip6_tnl_parm_match(const struct tnl_print_nlmsg_info *info) +{ + const struct ip6_tnl_parm2 *p1 = info->p1; + const struct ip6_tnl_parm2 *p2 = info->p2; + + return ((!p1->link || p1->link == p2->link) && + (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && + (IN6_IS_ADDR_UNSPECIFIED(&p1->laddr) || + IN6_ARE_ADDR_EQUAL(&p1->laddr, &p2->laddr)) && + (IN6_IS_ADDR_UNSPECIFIED(&p1->raddr) || + IN6_ARE_ADDR_EQUAL(&p1->raddr, &p2->raddr)) && + (!p1->proto || !p2->proto || p1->proto == p2->proto) && + (!p1->encap_limit || p1->encap_limit == p2->encap_limit) && + (!p1->hop_limit || p1->hop_limit == p2->hop_limit) && + (!(p1->flowinfo & IP6_FLOWINFO_TCLASS) || + !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_TCLASS)) && + (!(p1->flowinfo & IP6_FLOWINFO_FLOWLABEL) || + !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_FLOWLABEL)) && + (!p1->flags || (p1->flags & p2->flags))); +} + +static int do_show(int argc, char **argv) +{ + struct ip6_tnl_parm2 p, p1; + + ip6_tnl_parm_init(&p, 0); + p.proto = 0; /* default to any */ + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + if (!p.name[0] || show_stats) { + struct tnl_print_nlmsg_info info = { + .p1 = &p, + .p2 = &p1, + .init = ip6_tnl_parm_initialize, + .match = ip6_tnl_parm_match, + .print = print_tunnel, + }; + + return do_tunnels_list(&info); + } + + if (tnl_get_ioctl(p.name, &p)) + return -1; + + print_tunnel(&p); + return 0; +} + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip6_tnl_parm2 p; + const char *basedev = "ip6tnl0"; + + ip6_tnl_parm_init(&p, 1); + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (!*p.name) + fprintf(stderr, "Tunnel interface name not specified\n"); + + if (p.proto == IPPROTO_GRE) + basedev = "ip6gre0"; + else if (p.i_flags & VTI_ISVTI) + basedev = "ip6_vti0"; + + return tnl_add_ioctl(cmd, basedev, p.name, &p); +} + +static int do_del(int argc, char **argv) +{ + struct ip6_tnl_parm2 p; + const char *basedev = "ip6tnl0"; + + ip6_tnl_parm_init(&p, 1); + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + if (p.proto == IPPROTO_GRE) + basedev = "ip6gre0"; + else if (p.i_flags & VTI_ISVTI) + basedev = "ip6_vti0"; + + return tnl_del_ioctl(basedev, p.name, &p); +} + +int do_ip6tunnel(int argc, char **argv) +{ + switch (preferred_family) { + case AF_UNSPEC: + preferred_family = AF_INET6; + break; + case AF_INET6: + break; + default: + fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family); + exit(-1); + } + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); + if (matches(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); + if (matches(*argv, "delete") == 0) + return do_del(argc - 1, argv + 1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc - 1, argv + 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip -f inet6 tunnel help\".\n", *argv); + exit(-1); +} diff --git a/ip/ip_common.h b/ip/ip_common.h new file mode 100644 index 0000000..d604f75 --- /dev/null +++ b/ip/ip_common.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _IP_COMMON_H_ +#define _IP_COMMON_H_ + +#include + +#include "json_print.h" + +struct link_filter { + int ifindex; + int family; + int oneline; + int showqueue; + inet_prefix pfx; + int scope, scopemask; + int flags, flagmask; + int up; + char *label; + int flushed; + char *flushb; + int flushp; + int flushe; + int group; + int master; + char *kind; + char *slave_kind; + int target_nsid; +}; + +int get_operstate(const char *name); +int print_linkinfo(struct nlmsghdr *n, void *arg); +int print_addrinfo(struct nlmsghdr *n, void *arg); +int print_addrlabel(struct nlmsghdr *n, void *arg); +int print_neigh(struct nlmsghdr *n, void *arg); +int ipaddr_list_link(int argc, char **argv); +void ipaddr_get_vf_rate(int, int *, int *, const char *); +void iplink_usage(void) __attribute__((noreturn)); + +void iproute_reset_filter(int ifindex); +void ipmroute_reset_filter(int ifindex); +void ipaddr_reset_filter(int oneline, int ifindex); +void ipneigh_reset_filter(int ifindex); +void ipnetconf_reset_filter(int ifindex); + +int print_route(struct nlmsghdr *n, void *arg); +int print_mroute(struct nlmsghdr *n, void *arg); +int print_prefix(struct nlmsghdr *n, void *arg); +int print_rule(struct nlmsghdr *n, void *arg); +int print_netconf(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg); +int print_nexthop(struct nlmsghdr *n, void *arg); +void netns_map_init(void); +void netns_nsid_socket_init(void); +int print_nsid(struct nlmsghdr *n, void *arg); +char *get_name_from_nsid(int nsid); +int get_netnsid_from_name(const char *name); +int set_netnsid_from_name(const char *name, int nsid); +int do_ipaddr(int argc, char **argv); +int do_ipaddrlabel(int argc, char **argv); +int do_iproute(int argc, char **argv); +int do_iprule(int argc, char **argv); +int do_ipneigh(int argc, char **argv); +int do_ipntable(int argc, char **argv); +int do_iptunnel(int argc, char **argv); +int do_ip6tunnel(int argc, char **argv); +int do_iptuntap(int argc, char **argv); +int do_iplink(int argc, char **argv); +int do_ipmacsec(int argc, char **argv); +int do_ipmonitor(int argc, char **argv); +int do_multiaddr(int argc, char **argv); +int do_multiroute(int argc, char **argv); +int do_multirule(int argc, char **argv); +int do_netns(int argc, char **argv); +int do_xfrm(int argc, char **argv); +int do_ipl2tp(int argc, char **argv); +int do_ipfou(int argc, char **argv); +int do_ipila(int argc, char **argv); +int do_tcp_metrics(int argc, char **argv); +int do_ipnetconf(int argc, char **argv); +int do_iptoken(int argc, char **argv); +int do_ipvrf(int argc, char **argv); +void vrf_reset(void); +int netns_identify_pid(const char *pidstr, char *name, int len); +int do_seg6(int argc, char **argv); +int do_ipnh(int argc, char **argv); +int do_mptcp(int argc, char **argv); + +int iplink_get(char *name, __u32 filt_mask); +int iplink_ifla_xstats(int argc, char **argv); + +int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo); +void free_nlmsg_chain(struct nlmsg_chain *info); + +static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb) +{ + __u32 table = r->rtm_table; + + if (tb[RTA_TABLE]) + table = rta_getattr_u32(tb[RTA_TABLE]); + return table; +} + +extern struct rtnl_handle rth; + +struct iplink_req { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; +}; + +struct link_util { + struct link_util *next; + const char *id; + int maxattr; + int (*parse_opt)(struct link_util *, int, char **, + struct nlmsghdr *); + void (*print_opt)(struct link_util *, FILE *, + struct rtattr *[]); + void (*print_xstats)(struct link_util *, FILE *, + struct rtattr *); + void (*print_help)(struct link_util *, int, char **, + FILE *); + int (*parse_ifla_xstats)(struct link_util *, + int, char **); + int (*print_ifla_xstats)(struct nlmsghdr *, void *); +}; + +struct link_util *get_link_kind(const char *kind); + +int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type); + +/* iplink_bridge.c */ +void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len); +int bridge_parse_xstats(struct link_util *lu, int argc, char **argv); +int bridge_print_xstats(struct nlmsghdr *n, void *arg); + +int bond_parse_xstats(struct link_util *lu, int argc, char **argv); +int bond_print_xstats(struct nlmsghdr *n, void *arg); + +/* iproute_lwtunnel.c */ +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp, + int encap_attr, int encap_type_attr); +void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap); + +/* iplink_xdp.c */ +int xdp_parse(int *argc, char ***argv, struct iplink_req *req, const char *ifname, + bool generic, bool drv, bool offload); +void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details); + +/* iplink_vrf.c */ +__u32 ipvrf_get_table(const char *name); +int name_is_vrf(const char *name); + +#ifndef INFINITY_LIFE_TIME +#define INFINITY_LIFE_TIME 0xFFFFFFFFU +#endif + +#ifndef LABEL_MAX_MASK +#define LABEL_MAX_MASK 0xFFFFFU +#endif + +void print_num(FILE *fp, unsigned int width, uint64_t count); +void print_rt_flags(FILE *fp, unsigned int flags); +void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix); +void print_rta_gateway(FILE *fp, unsigned char family, + const struct rtattr *rta); +#endif /* _IP_COMMON_H_ */ diff --git a/ip/ipaddress.c b/ip/ipaddress.c new file mode 100644 index 0000000..2b4cb48 --- /dev/null +++ b/ip/ipaddress.c @@ -0,0 +1,2482 @@ +/* + * ipaddress.c "ip address". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ll_map.h" +#include "ip_common.h" +#include "color.h" + +enum { + IPADD_LIST, + IPADD_FLUSH, + IPADD_SAVE, +}; + +static struct link_filter filter; +static int do_link; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + if (do_link) + iplink_usage(); + + fprintf(stderr, + "Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n" + " [ CONFFLAG-LIST ]\n" + " ip address del IFADDR dev IFNAME [mngtmpaddr]\n" + " ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n" + " [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n" + " ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n" + " [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n" + " [ label LABEL ] [up] [ vrf NAME ] ]\n" + " ip address {showdump|restore}\n" + "IFADDR := PREFIX | ADDR peer PREFIX\n" + " [ broadcast ADDR ] [ anycast ADDR ]\n" + " [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n" + "SCOPE-ID := [ host | link | global | NUMBER ]\n" + "FLAG-LIST := [ FLAG-LIST ] FLAG\n" + "FLAG := [ permanent | dynamic | secondary | primary |\n" + " [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n" + " CONFFLAG-LIST ]\n" + "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n" + "CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n" + "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n" + "LFT := forever | SECONDS\n" + "TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n" + " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n" + " gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n" + " nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n" + " hsr | macsec | netdevsim }\n"); + + exit(-1); +} + +static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) +{ + open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<"); + if (flags & IFF_UP && !(flags & IFF_RUNNING)) + print_string(PRINT_ANY, NULL, + flags ? "%s," : "%s", "NO-CARRIER"); + flags &= ~IFF_RUNNING; +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); + _PF(UP); + _PF(LOWER_UP); + _PF(DORMANT); + _PF(ECHO); +#undef _PF + if (flags) + print_hex(PRINT_ANY, NULL, "%x", flags); + if (mdown) + print_string(PRINT_ANY, NULL, ",%s", "M-DOWN"); + close_json_array(PRINT_ANY, "> "); +} + +static const char *oper_states[] = { + "UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN", + "TESTING", "DORMANT", "UP" +}; + +static void print_operstate(FILE *f, __u8 state) +{ + if (state >= ARRAY_SIZE(oper_states)) { + if (is_json_context()) + print_uint(PRINT_JSON, "operstate_index", NULL, state); + else + print_0xhex(PRINT_FP, NULL, "state %#llx", state); + } else if (brief) { + print_color_string(PRINT_ANY, + oper_state_color(state), + "operstate", + "%-14s ", + oper_states[state]); + } else { + if (is_json_context()) + print_string(PRINT_JSON, + "operstate", + NULL, oper_states[state]); + else { + fprintf(f, "state "); + color_fprintf(f, oper_state_color(state), + "%s ", oper_states[state]); + } + } +} + +int get_operstate(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(oper_states); i++) + if (strcasecmp(name, oper_states[i]) == 0) + return i; + return -1; +} + +static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1]) +{ + int qlen; + + if (tb[IFLA_TXQLEN]) + qlen = rta_getattr_u32(tb[IFLA_TXQLEN]); + else { + struct ifreq ifr = {}; + int s = socket(AF_INET, SOCK_STREAM, 0); + + if (s < 0) + return; + + strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME])); + if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { + fprintf(stderr, + "ioctl(SIOCGIFTXQLEN) failed: %s\n", + strerror(errno)); + close(s); + return; + } + close(s); + qlen = ifr.ifr_qlen; + } + if (qlen) + print_int(PRINT_ANY, "txqlen", "qlen %d", qlen); +} + +static const char *link_modes[] = { + "DEFAULT", "DORMANT" +}; + +static void print_linkmode(FILE *f, struct rtattr *tb) +{ + unsigned int mode = rta_getattr_u8(tb); + + if (mode >= ARRAY_SIZE(link_modes)) + print_int(PRINT_ANY, + "linkmode_index", + "mode %d ", + mode); + else + print_string(PRINT_ANY, + "linkmode", + "mode %s " + , link_modes[mode]); +} + +static char *parse_link_kind(struct rtattr *tb, bool slave) +{ + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + int attr = slave ? IFLA_INFO_SLAVE_KIND : IFLA_INFO_KIND; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb); + + if (linkinfo[attr]) + return RTA_DATA(linkinfo[attr]); + + return ""; +} + +static int match_link_kind(struct rtattr **tb, const char *kind, bool slave) +{ + if (!tb[IFLA_LINKINFO]) + return -1; + + return strcmp(parse_link_kind(tb[IFLA_LINKINFO], slave), kind); +} + +static void print_linktype(FILE *fp, struct rtattr *tb) +{ + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct link_util *lu; + struct link_util *slave_lu; + char slave[32]; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb); + open_json_object("linkinfo"); + + if (linkinfo[IFLA_INFO_KIND]) { + const char *kind + = rta_getattr_str(linkinfo[IFLA_INFO_KIND]); + + print_nl(); + print_string(PRINT_ANY, "info_kind", " %s ", kind); + + lu = get_link_kind(kind); + if (lu && lu->print_opt) { + struct rtattr *attr[lu->maxattr+1], **data = NULL; + + if (linkinfo[IFLA_INFO_DATA]) { + parse_rtattr_nested(attr, lu->maxattr, + linkinfo[IFLA_INFO_DATA]); + data = attr; + } + open_json_object("info_data"); + lu->print_opt(lu, fp, data); + close_json_object(); + + if (linkinfo[IFLA_INFO_XSTATS] && show_stats && + lu->print_xstats) { + open_json_object("info_xstats"); + lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]); + close_json_object(); + } + } + } + + if (linkinfo[IFLA_INFO_SLAVE_KIND]) { + const char *slave_kind + = rta_getattr_str(linkinfo[IFLA_INFO_SLAVE_KIND]); + + print_nl(); + print_string(PRINT_ANY, + "info_slave_kind", + " %s_slave ", + slave_kind); + + snprintf(slave, sizeof(slave), "%s_slave", slave_kind); + + slave_lu = get_link_kind(slave); + if (slave_lu && slave_lu->print_opt) { + struct rtattr *attr[slave_lu->maxattr+1], **data = NULL; + + if (linkinfo[IFLA_INFO_SLAVE_DATA]) { + parse_rtattr_nested(attr, slave_lu->maxattr, + linkinfo[IFLA_INFO_SLAVE_DATA]); + data = attr; + } + open_json_object("info_slave_data"); + slave_lu->print_opt(slave_lu, fp, data); + close_json_object(); + } + } + close_json_object(); +} + +static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr) +{ + struct rtattr *inet6_attr; + struct rtattr *tb[IFLA_INET6_MAX + 1]; + + inet6_attr = parse_rtattr_one_nested(AF_INET6, af_spec_attr); + if (!inet6_attr) + return; + + parse_rtattr_nested(tb, IFLA_INET6_MAX, inet6_attr); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + __u8 mode = rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); + SPRINT_BUF(b1); + + switch (mode) { + case IN6_ADDR_GEN_MODE_EUI64: + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "eui64"); + break; + case IN6_ADDR_GEN_MODE_NONE: + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "none"); + break; + case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "stable_secret"); + break; + case IN6_ADDR_GEN_MODE_RANDOM: + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "random"); + break; + default: + snprintf(b1, sizeof(b1), "%#.2hhx", mode); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + b1); + break; + } + } +} + +static void print_vf_stats64(FILE *fp, struct rtattr *vfstats); + +static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo) +{ + struct ifla_vf_mac *vf_mac; + struct ifla_vf_broadcast *vf_broadcast; + struct ifla_vf_tx_rate *vf_tx_rate; + struct rtattr *vf[IFLA_VF_MAX + 1] = {}; + + SPRINT_BUF(b1); + + if (vfinfo->rta_type != IFLA_VF_INFO) { + fprintf(stderr, "BUG: rta type is %d\n", vfinfo->rta_type); + return; + } + + parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo); + + vf_mac = RTA_DATA(vf[IFLA_VF_MAC]); + vf_broadcast = RTA_DATA(vf[IFLA_VF_BROADCAST]); + vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf); + + print_string(PRINT_ANY, + "link_type", + " link/%s ", + ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); + + print_color_string(PRINT_ANY, COLOR_MAC, + "address", "%s", + ll_addr_n2a((unsigned char *) &vf_mac->mac, + ifi->ifi_type == ARPHRD_ETHER ? + ETH_ALEN : INFINIBAND_ALEN, + ifi->ifi_type, + b1, sizeof(b1))); + + if (vf[IFLA_VF_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) { + print_string(PRINT_FP, NULL, " peer ", NULL); + print_bool(PRINT_JSON, + "link_pointtopoint", NULL, true); + } else + print_string(PRINT_FP, NULL, " brd ", NULL); + + print_color_string(PRINT_ANY, COLOR_MAC, + "broadcast", "%s", + ll_addr_n2a((unsigned char *) &vf_broadcast->broadcast, + ifi->ifi_type == ARPHRD_ETHER ? + ETH_ALEN : INFINIBAND_ALEN, + ifi->ifi_type, + b1, sizeof(b1))); + } + + if (vf[IFLA_VF_VLAN_LIST]) { + struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST]; + int rem = RTA_PAYLOAD(vfvlanlist); + + open_json_array(PRINT_JSON, "vlan_list"); + for (i = RTA_DATA(vfvlanlist); + RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + struct ifla_vf_vlan_info *vf_vlan_info = RTA_DATA(i); + SPRINT_BUF(b2); + + open_json_object(NULL); + if (vf_vlan_info->vlan) + print_int(PRINT_ANY, + "vlan", + ", vlan %d", + vf_vlan_info->vlan); + if (vf_vlan_info->qos) + print_int(PRINT_ANY, + "qos", + ", qos %d", + vf_vlan_info->qos); + if (vf_vlan_info->vlan_proto && + vf_vlan_info->vlan_proto != htons(ETH_P_8021Q)) + print_string(PRINT_ANY, + "protocol", + ", vlan protocol %s", + ll_proto_n2a( + vf_vlan_info->vlan_proto, + b2, sizeof(b2))); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); + } else { + struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]); + + if (vf_vlan->vlan) + print_int(PRINT_ANY, + "vlan", + ", vlan %d", + vf_vlan->vlan); + if (vf_vlan->qos) + print_int(PRINT_ANY, "qos", ", qos %d", vf_vlan->qos); + } + + if (vf_tx_rate->rate) + print_uint(PRINT_ANY, + "tx_rate", + ", tx rate %u (Mbps)", + vf_tx_rate->rate); + + if (vf[IFLA_VF_RATE]) { + struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]); + int max_tx = vf_rate->max_tx_rate; + int min_tx = vf_rate->min_tx_rate; + + if (is_json_context()) { + open_json_object("rate"); + print_uint(PRINT_JSON, "max_tx", NULL, max_tx); + print_uint(PRINT_ANY, "min_tx", NULL, min_tx); + close_json_object(); + } else { + if (max_tx) + fprintf(fp, ", max_tx_rate %uMbps", max_tx); + if (min_tx) + fprintf(fp, ", min_tx_rate %uMbps", min_tx); + } + } + + if (vf[IFLA_VF_SPOOFCHK]) { + struct ifla_vf_spoofchk *vf_spoofchk = + RTA_DATA(vf[IFLA_VF_SPOOFCHK]); + + if (vf_spoofchk->setting != -1) + print_bool(PRINT_ANY, + "spoofchk", + vf_spoofchk->setting ? + ", spoof checking on" : ", spoof checking off", + vf_spoofchk->setting); + } + + if (vf[IFLA_VF_IB_NODE_GUID]) { + struct ifla_vf_guid *guid = RTA_DATA(vf[IFLA_VF_IB_NODE_GUID]); + uint64_t node_guid = ntohll(guid->guid); + + print_string(PRINT_ANY, "node guid", ", NODE_GUID %s", + ll_addr_n2a((const unsigned char *)&node_guid, + sizeof(node_guid), ARPHRD_INFINIBAND, + b1, sizeof(b1))); + } + if (vf[IFLA_VF_IB_PORT_GUID]) { + struct ifla_vf_guid *guid = RTA_DATA(vf[IFLA_VF_IB_PORT_GUID]); + uint64_t port_guid = ntohll(guid->guid); + + print_string(PRINT_ANY, "port guid", ", PORT_GUID %s", + ll_addr_n2a((const unsigned char *)&port_guid, + sizeof(port_guid), ARPHRD_INFINIBAND, + b1, sizeof(b1))); + } + if (vf[IFLA_VF_LINK_STATE]) { + struct ifla_vf_link_state *vf_linkstate = + RTA_DATA(vf[IFLA_VF_LINK_STATE]); + + if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO) + print_string(PRINT_ANY, + "link_state", + ", link-state %s", + "auto"); + else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE) + print_string(PRINT_ANY, + "link_state", + ", link-state %s", + "enable"); + else + print_string(PRINT_ANY, + "link_state", + ", link-state %s", + "disable"); + } + + if (vf[IFLA_VF_TRUST]) { + struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]); + + if (vf_trust->setting != -1) + print_bool(PRINT_ANY, + "trust", + vf_trust->setting ? ", trust on" : ", trust off", + vf_trust->setting); + } + + if (vf[IFLA_VF_RSS_QUERY_EN]) { + struct ifla_vf_rss_query_en *rss_query = + RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]); + + if (rss_query->setting != -1) + print_bool(PRINT_ANY, + "query_rss_en", + rss_query->setting ? ", query_rss on" + : ", query_rss off", + rss_query->setting); + } + + if (vf[IFLA_VF_STATS] && show_stats) + print_vf_stats64(fp, vf[IFLA_VF_STATS]); +} + +void print_num(FILE *fp, unsigned int width, uint64_t count) +{ + const char *prefix = "kMGTPE"; + const unsigned int base = use_iec ? 1024 : 1000; + uint64_t powi = 1; + uint16_t powj = 1; + uint8_t precision = 2; + char buf[64]; + + if (!human_readable || count < base) { + fprintf(fp, "%-*"PRIu64" ", width, count); + return; + } + + /* increase value by a factor of 1000/1024 and print + * if result is something a human can read + */ + for (;;) { + powi *= base; + if (count / base < powi) + break; + + if (!prefix[1]) + break; + ++prefix; + } + + /* try to guess a good number of digits for precision */ + for (; precision > 0; precision--) { + powj *= 10; + if (count / powi < powj) + break; + } + + snprintf(buf, sizeof(buf), "%.*f%c%s", precision, + (double) count / powi, *prefix, use_iec ? "i" : ""); + + fprintf(fp, "%-*s ", width, buf); +} + +static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) +{ + struct rtattr *vf[IFLA_VF_STATS_MAX + 1]; + + if (vfstats->rta_type != IFLA_VF_STATS) { + fprintf(stderr, "BUG: rta type is %d\n", vfstats->rta_type); + return; + } + + parse_rtattr_nested(vf, IFLA_VF_STATS_MAX, vfstats); + + if (is_json_context()) { + open_json_object("stats"); + + /* RX stats */ + open_json_object("rx"); + print_u64(PRINT_JSON, "bytes", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES])); + print_u64(PRINT_JSON, "packets", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS])); + print_u64(PRINT_JSON, "multicast", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST])); + print_u64(PRINT_JSON, "broadcast", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST])); + if (vf[IFLA_VF_STATS_RX_DROPPED]) + print_u64(PRINT_JSON, "dropped", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED])); + close_json_object(); + + /* TX stats */ + open_json_object("tx"); + print_u64(PRINT_JSON, "tx_bytes", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES])); + print_u64(PRINT_JSON, "tx_packets", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS])); + if (vf[IFLA_VF_STATS_TX_DROPPED]) + print_u64(PRINT_JSON, "dropped", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED])); + close_json_object(); + close_json_object(); + } else { + /* RX stats */ + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX: bytes packets mcast bcast "); + if (vf[IFLA_VF_STATS_RX_DROPPED]) + fprintf(fp, " dropped "); + fprintf(fp, "%s", _SL_); + fprintf(fp, " "); + + print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES])); + print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS])); + print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST])); + print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST])); + if (vf[IFLA_VF_STATS_RX_DROPPED]) + print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED])); + + /* TX stats */ + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX: bytes packets "); + if (vf[IFLA_VF_STATS_TX_DROPPED]) + fprintf(fp, " dropped "); + fprintf(fp, "%s", _SL_); + fprintf(fp, " "); + + print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES])); + print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS])); + if (vf[IFLA_VF_STATS_TX_DROPPED]) + print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED])); + } +} + +static void __print_link_stats(FILE *fp, struct rtattr *tb[]) +{ + const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES]; + struct rtnl_link_stats64 _s, *s = &_s; + int ret; + + ret = get_rtnl_link_stats_rta(s, tb); + if (ret < 0) + return; + + if (is_json_context()) { + open_json_object((ret == sizeof(*s)) ? "stats64" : "stats"); + + /* RX stats */ + open_json_object("rx"); + print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes); + print_u64(PRINT_JSON, "packets", NULL, s->rx_packets); + print_u64(PRINT_JSON, "errors", NULL, s->rx_errors); + print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped); + print_u64(PRINT_JSON, "over_errors", NULL, s->rx_over_errors); + print_u64(PRINT_JSON, "multicast", NULL, s->multicast); + if (s->rx_compressed) + print_u64(PRINT_JSON, + "compressed", NULL, s->rx_compressed); + + /* RX error stats */ + if (show_stats > 1) { + print_u64(PRINT_JSON, + "length_errors", + NULL, s->rx_length_errors); + print_u64(PRINT_JSON, + "crc_errors", + NULL, s->rx_crc_errors); + print_u64(PRINT_JSON, + "frame_errors", + NULL, s->rx_frame_errors); + print_u64(PRINT_JSON, + "fifo_errors", + NULL, s->rx_fifo_errors); + print_u64(PRINT_JSON, + "missed_errors", + NULL, s->rx_missed_errors); + if (s->rx_nohandler) + print_u64(PRINT_JSON, + "nohandler", NULL, s->rx_nohandler); + } + close_json_object(); + + /* TX stats */ + open_json_object("tx"); + print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes); + print_u64(PRINT_JSON, "packets", NULL, s->tx_packets); + print_u64(PRINT_JSON, "errors", NULL, s->tx_errors); + print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped); + print_u64(PRINT_JSON, + "carrier_errors", + NULL, s->tx_carrier_errors); + print_u64(PRINT_JSON, "collisions", NULL, s->collisions); + if (s->tx_compressed) + print_u64(PRINT_JSON, + "compressed", NULL, s->tx_compressed); + + /* TX error stats */ + if (show_stats > 1) { + print_u64(PRINT_JSON, + "aborted_errors", + NULL, s->tx_aborted_errors); + print_u64(PRINT_JSON, + "fifo_errors", + NULL, s->tx_fifo_errors); + print_u64(PRINT_JSON, + "window_errors", + NULL, s->tx_window_errors); + print_u64(PRINT_JSON, + "heartbeat_errors", + NULL, s->tx_heartbeat_errors); + if (carrier_changes) + print_u64(PRINT_JSON, "carrier_changes", NULL, + rta_getattr_u32(carrier_changes)); + } + + close_json_object(); + close_json_object(); + } else { + /* RX stats */ + fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", + s->rx_compressed ? "compressed" : "", _SL_); + + fprintf(fp, " "); + print_num(fp, 10, s->rx_bytes); + print_num(fp, 8, s->rx_packets); + print_num(fp, 7, s->rx_errors); + print_num(fp, 7, s->rx_dropped); + print_num(fp, 7, s->rx_over_errors); + print_num(fp, 7, s->multicast); + if (s->rx_compressed) + print_num(fp, 7, s->rx_compressed); + + /* RX error stats */ + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s%s", + s->rx_nohandler ? " nohandler" : "", _SL_); + fprintf(fp, " "); + print_num(fp, 8, s->rx_length_errors); + print_num(fp, 7, s->rx_crc_errors); + print_num(fp, 7, s->rx_frame_errors); + print_num(fp, 7, s->rx_fifo_errors); + print_num(fp, 7, s->rx_missed_errors); + if (s->rx_nohandler) + print_num(fp, 7, s->rx_nohandler); + } + fprintf(fp, "%s", _SL_); + + /* TX stats */ + fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", + s->tx_compressed ? "compressed" : "", _SL_); + + fprintf(fp, " "); + print_num(fp, 10, s->tx_bytes); + print_num(fp, 8, s->tx_packets); + print_num(fp, 7, s->tx_errors); + print_num(fp, 7, s->tx_dropped); + print_num(fp, 7, s->tx_carrier_errors); + print_num(fp, 7, s->collisions); + if (s->tx_compressed) + print_num(fp, 7, s->tx_compressed); + + /* TX error stats */ + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX errors: aborted fifo window heartbeat"); + if (carrier_changes) + fprintf(fp, " transns"); + fprintf(fp, "%s", _SL_); + + fprintf(fp, " "); + print_num(fp, 8, s->tx_aborted_errors); + print_num(fp, 7, s->tx_fifo_errors); + print_num(fp, 7, s->tx_window_errors); + print_num(fp, 7, s->tx_heartbeat_errors); + if (carrier_changes) + print_num(fp, 7, + rta_getattr_u32(carrier_changes)); + } + } +} + +static void print_link_stats(FILE *fp, struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); + __print_link_stats(fp, tb); + print_nl(); +} + +static int print_linkinfo_brief(FILE *fp, const char *name, + const struct ifinfomsg *ifi, + struct rtattr *tb[]) +{ + unsigned int m_flag = 0; + + m_flag = print_name_and_link("%-16s ", name, tb); + + if (tb[IFLA_OPERSTATE]) + print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE])); + + if (filter.family == AF_PACKET) { + SPRINT_BUF(b1); + + if (tb[IFLA_ADDRESS]) { + print_color_string(PRINT_ANY, COLOR_MAC, + "address", "%s ", + ll_addr_n2a( + RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + + if (filter.family == AF_PACKET) { + print_link_flags(fp, ifi->ifi_flags, m_flag); + print_string(PRINT_FP, NULL, "%s", "\n"); + } + + fflush(fp); + return 0; +} + +static const char *link_events[] = { + [IFLA_EVENT_NONE] = "NONE", + [IFLA_EVENT_REBOOT] = "REBOOT", + [IFLA_EVENT_FEATURES] = "FEATURE CHANGE", + [IFLA_EVENT_BONDING_FAILOVER] = "BONDING FAILOVER", + [IFLA_EVENT_NOTIFY_PEERS] = "NOTIFY PEERS", + [IFLA_EVENT_IGMP_RESEND] = "RESEND IGMP", + [IFLA_EVENT_BONDING_OPTIONS] = "BONDING OPTION" +}; + +static void print_link_event(FILE *f, __u32 event) +{ + if (event >= ARRAY_SIZE(link_events)) + print_int(PRINT_ANY, "event", "event %d ", event); + else { + if (event) + print_string(PRINT_ANY, + "event", "event %s ", + link_events[event]); + } +} + +int print_linkinfo(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + const char *name; + unsigned int m_flag = 0; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return -1; + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return -1; + + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, NLA_F_NESTED); + + name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]); + if (!name) + return -1; + + if (filter.label) + return 0; + + if (tb[IFLA_GROUP]) { + int group = rta_getattr_u32(tb[IFLA_GROUP]); + + if (filter.group != -1 && group != filter.group) + return -1; + } + + if (tb[IFLA_MASTER]) { + int master = rta_getattr_u32(tb[IFLA_MASTER]); + + if (filter.master > 0 && master != filter.master) + return -1; + } else if (filter.master > 0) + return -1; + + if (filter.kind && match_link_kind(tb, filter.kind, 0)) + return -1; + + if (filter.slave_kind && match_link_kind(tb, filter.slave_kind, 1)) + return -1; + + if (n->nlmsg_type == RTM_DELLINK) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (brief) + return print_linkinfo_brief(fp, name, ifi, tb); + + print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index); + + m_flag = print_name_and_link("%s: ", name, tb); + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + print_int(PRINT_ANY, + "mtu", "mtu %u ", + rta_getattr_u32(tb[IFLA_MTU])); + if (tb[IFLA_XDP]) + xdp_dump(fp, tb[IFLA_XDP], do_link, false); + if (tb[IFLA_QDISC]) + print_string(PRINT_ANY, + "qdisc", + "qdisc %s ", + rta_getattr_str(tb[IFLA_QDISC])); + if (tb[IFLA_MASTER]) { + int master = rta_getattr_u32(tb[IFLA_MASTER]); + + print_string(PRINT_ANY, + "master", "master %s ", + ll_index_to_name(master)); + } + + if (tb[IFLA_OPERSTATE]) + print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE])); + + if (do_link && tb[IFLA_LINKMODE]) + print_linkmode(fp, tb[IFLA_LINKMODE]); + + if (tb[IFLA_GROUP]) { + int group = rta_getattr_u32(tb[IFLA_GROUP]); + + print_string(PRINT_ANY, + "group", + "group %s ", + rtnl_group_n2a(group, b1, sizeof(b1))); + } + + if (filter.showqueue) + print_queuelen(fp, tb); + + if (tb[IFLA_EVENT]) + print_link_event(fp, rta_getattr_u32(tb[IFLA_EVENT])); + + if (!filter.family || filter.family == AF_PACKET || show_details) { + print_nl(); + print_string(PRINT_ANY, + "link_type", + " link/%s ", + ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); + if (tb[IFLA_ADDRESS]) { + print_color_string(PRINT_ANY, + COLOR_MAC, + "address", + "%s", + ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) { + print_string(PRINT_FP, NULL, " peer ", NULL); + print_bool(PRINT_JSON, + "link_pointtopoint", NULL, true); + } else { + print_string(PRINT_FP, NULL, " brd ", NULL); + } + print_color_string(PRINT_ANY, + COLOR_MAC, + "broadcast", + "%s", + ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_PERM_ADDRESS]) { + unsigned int len = RTA_PAYLOAD(tb[IFLA_PERM_ADDRESS]); + + if (!tb[IFLA_ADDRESS] || + RTA_PAYLOAD(tb[IFLA_ADDRESS]) != len || + memcmp(RTA_DATA(tb[IFLA_PERM_ADDRESS]), + RTA_DATA(tb[IFLA_ADDRESS]), len)) { + print_string(PRINT_FP, NULL, " permaddr ", NULL); + print_color_string(PRINT_ANY, + COLOR_MAC, + "permaddr", + "%s", + ll_addr_n2a(RTA_DATA(tb[IFLA_PERM_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_PERM_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + } + + if (tb[IFLA_LINK_NETNSID]) { + int id = rta_getattr_u32(tb[IFLA_LINK_NETNSID]); + + if (is_json_context()) { + print_int(PRINT_JSON, "link_netnsid", NULL, id); + } else { + if (id >= 0) { + char *name = get_name_from_nsid(id); + + if (name) + print_string(PRINT_FP, NULL, + " link-netns %s", name); + else + print_int(PRINT_FP, NULL, + " link-netnsid %d", id); + } else + print_string(PRINT_FP, NULL, + " link-netnsid %s", "unknown"); + } + } + + if (tb[IFLA_NEW_NETNSID]) { + int id = rta_getattr_u32(tb[IFLA_NEW_NETNSID]); + char *name = get_name_from_nsid(id); + + if (name) + print_string(PRINT_FP, NULL, " new-netns %s", name); + else + print_int(PRINT_FP, NULL, " new-netnsid %d", id); + } + if (tb[IFLA_NEW_IFINDEX]) { + int id = rta_getattr_u32(tb[IFLA_NEW_IFINDEX]); + + print_int(PRINT_FP, NULL, " new-ifindex %d", id); + } + + if (tb[IFLA_PROTO_DOWN]) { + if (rta_getattr_u8(tb[IFLA_PROTO_DOWN])) + print_bool(PRINT_ANY, + "proto_down", " protodown on ", true); + } + + if (show_details) { + if (tb[IFLA_PROMISCUITY]) + print_uint(PRINT_ANY, + "promiscuity", + " promiscuity %u ", + rta_getattr_u32(tb[IFLA_PROMISCUITY])); + + if (tb[IFLA_MIN_MTU]) + print_uint(PRINT_ANY, + "min_mtu", "minmtu %u ", + rta_getattr_u32(tb[IFLA_MIN_MTU])); + + if (tb[IFLA_MAX_MTU]) + print_uint(PRINT_ANY, + "max_mtu", "maxmtu %u ", + rta_getattr_u32(tb[IFLA_MAX_MTU])); + + if (tb[IFLA_LINKINFO]) + print_linktype(fp, tb[IFLA_LINKINFO]); + + if (do_link && tb[IFLA_AF_SPEC]) + print_af_spec(fp, tb[IFLA_AF_SPEC]); + + if (tb[IFLA_NUM_TX_QUEUES]) + print_uint(PRINT_ANY, + "num_tx_queues", + "numtxqueues %u ", + rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES])); + + if (tb[IFLA_NUM_RX_QUEUES]) + print_uint(PRINT_ANY, + "num_rx_queues", + "numrxqueues %u ", + rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES])); + + if (tb[IFLA_GSO_MAX_SIZE]) + print_uint(PRINT_ANY, + "gso_max_size", + "gso_max_size %u ", + rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE])); + + if (tb[IFLA_GSO_MAX_SEGS]) + print_uint(PRINT_ANY, + "gso_max_segs", + "gso_max_segs %u ", + rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS])); + + if (tb[IFLA_PHYS_PORT_NAME]) + print_string(PRINT_ANY, + "phys_port_name", + "portname %s ", + rta_getattr_str(tb[IFLA_PHYS_PORT_NAME])); + + if (tb[IFLA_PHYS_PORT_ID]) { + print_string(PRINT_ANY, + "phys_port_id", + "portid %s ", + hexstring_n2a( + RTA_DATA(tb[IFLA_PHYS_PORT_ID]), + RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]), + b1, sizeof(b1))); + } + + if (tb[IFLA_PHYS_SWITCH_ID]) { + print_string(PRINT_ANY, + "phys_switch_id", + "switchid %s ", + hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]), + RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]), + b1, sizeof(b1))); + } + } + + if ((do_link || show_details) && tb[IFLA_IFALIAS]) { + print_string(PRINT_FP, NULL, "%s ", _SL_); + print_string(PRINT_ANY, + "ifalias", + "alias %s", + rta_getattr_str(tb[IFLA_IFALIAS])); + } + + if ((do_link || show_details) && tb[IFLA_XDP]) + xdp_dump(fp, tb[IFLA_XDP], true, true); + + if (do_link && show_stats) { + print_nl(); + __print_link_stats(fp, tb); + } + + if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) { + struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST]; + int rem = RTA_PAYLOAD(vflist); + + open_json_array(PRINT_JSON, "vfinfo_list"); + for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + open_json_object(NULL); + print_vfinfo(fp, ifi, i); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); + } + + if (tb[IFLA_PROP_LIST]) { + struct rtattr *i, *proplist = tb[IFLA_PROP_LIST]; + int rem = RTA_PAYLOAD(proplist); + + open_json_array(PRINT_JSON, "altnames"); + for (i = RTA_DATA(proplist); RTA_OK(i, rem); + i = RTA_NEXT(i, rem)) { + if (i->rta_type != IFLA_ALT_IFNAME) + continue; + print_string(PRINT_FP, NULL, "%s altname ", _SL_); + print_string(PRINT_ANY, NULL, + "%s", rta_getattr_str(i)); + } + close_json_array(PRINT_JSON, NULL); + } + + print_string(PRINT_FP, NULL, "%s", "\n"); + fflush(fp); + return 1; +} + +static int flush_update(void) +{ + + /* + * Note that the kernel may delete multiple addresses for one + * delete request (e.g. if ipv4 address promotion is disabled). + * Since a flush operation is really a series of delete requests + * its possible that we may request an address delete that has + * already been done by the kernel. Therefore, ignore EADDRNOTAVAIL + * errors returned from a flush request + */ + if ((rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) && + (errno != EADDRNOTAVAIL)) { + perror("Failed to send flush request"); + return -1; + } + filter.flushp = 0; + return 0; +} + +static int set_lifetime(unsigned int *lifetime, char *argv) +{ + if (strcmp(argv, "forever") == 0) + *lifetime = INFINITY_LIFE_TIME; + else if (get_u32(lifetime, argv, 0)) + return -1; + + return 0; +} + +static unsigned int get_ifa_flags(struct ifaddrmsg *ifa, + struct rtattr *ifa_flags_attr) +{ + return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) : + ifa->ifa_flags; +} + +/* Mapping from argument to address flag mask and attributes */ +static const struct ifa_flag_data_t { + const char *name; + unsigned long mask; + bool readonly; + bool v6only; +} ifa_flag_data[] = { + { .name = "secondary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false}, + { .name = "temporary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false}, + { .name = "nodad", .mask = IFA_F_NODAD, .readonly = false, .v6only = true}, + { .name = "optimistic", .mask = IFA_F_OPTIMISTIC, .readonly = false, .v6only = true}, + { .name = "dadfailed", .mask = IFA_F_DADFAILED, .readonly = true, .v6only = true}, + { .name = "home", .mask = IFA_F_HOMEADDRESS, .readonly = false, .v6only = true}, + { .name = "deprecated", .mask = IFA_F_DEPRECATED, .readonly = true, .v6only = true}, + { .name = "tentative", .mask = IFA_F_TENTATIVE, .readonly = true, .v6only = true}, + { .name = "permanent", .mask = IFA_F_PERMANENT, .readonly = true, .v6only = true}, + { .name = "mngtmpaddr", .mask = IFA_F_MANAGETEMPADDR, .readonly = false, .v6only = true}, + { .name = "noprefixroute", .mask = IFA_F_NOPREFIXROUTE, .readonly = false, .v6only = false}, + { .name = "autojoin", .mask = IFA_F_MCAUTOJOIN, .readonly = false, .v6only = false}, + { .name = "stable-privacy", .mask = IFA_F_STABLE_PRIVACY, .readonly = true, .v6only = true}, +}; + +/* Returns a pointer to the data structure for a particular interface flag, or null if no flag could be found */ +static const struct ifa_flag_data_t* lookup_flag_data_by_name(const char* flag_name) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) { + if (strcmp(flag_name, ifa_flag_data[i].name) == 0) + return &ifa_flag_data[i]; + } + return NULL; +} + +static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa, + unsigned int flags) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ifa_flag_data); i++) { + const struct ifa_flag_data_t* flag_data = &ifa_flag_data[i]; + + if (flag_data->mask == IFA_F_PERMANENT) { + if (!(flags & flag_data->mask)) + print_bool(PRINT_ANY, + "dynamic", "dynamic ", true); + } else if (flags & flag_data->mask) { + if (flag_data->mask == IFA_F_SECONDARY && + ifa->ifa_family == AF_INET6) { + print_bool(PRINT_ANY, + "temporary", "temporary ", true); + } else { + print_string(PRINT_FP, NULL, + "%s ", flag_data->name); + print_bool(PRINT_JSON, + flag_data->name, NULL, true); + } + } + + flags &= ~flag_data->mask; + } + + if (flags) { + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%02x", flags); + print_string(PRINT_JSON, "ifa_flags", NULL, b1); + } else { + fprintf(fp, "flags %02x ", flags); + } + } + +} + +static int get_filter(const char *arg) +{ + bool inv = false; + + if (arg[0] == '-') { + inv = true; + arg++; + } + + /* Special cases */ + if (strcmp(arg, "dynamic") == 0) { + inv = !inv; + arg = "permanent"; + } else if (strcmp(arg, "primary") == 0) { + inv = !inv; + arg = "secondary"; + } + + const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(arg); + if (flag_data == NULL) + return -1; + + if (inv) + filter.flags &= ~flag_data->mask; + else + filter.flags |= flag_data->mask; + filter.flagmask |= flag_data->mask; + return 0; +} + +static int ifa_label_match_rta(int ifindex, const struct rtattr *rta) +{ + const char *label; + + if (!filter.label) + return 0; + + if (rta) + label = RTA_DATA(rta); + else + label = ll_index_to_name(ifindex); + + return fnmatch(filter.label, label, 0); +} + +int print_addrinfo(struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + int len = n->nlmsg_len; + unsigned int ifa_flags; + struct rtattr *rta_tb[IFA_MAX+1]; + + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + len -= NLMSG_LENGTH(sizeof(*ifa)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) + return 0; + + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + ifa_flags = get_ifa_flags(ifa, rta_tb[IFA_FLAGS]); + + if (!rta_tb[IFA_LOCAL]) + rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; + if (!rta_tb[IFA_ADDRESS]) + rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; + + if (filter.ifindex && filter.ifindex != ifa->ifa_index) + return 0; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + return 0; + if ((filter.flags ^ ifa_flags) & filter.flagmask) + return 0; + + if (filter.family && filter.family != ifa->ifa_family) + return 0; + + if (ifa_label_match_rta(ifa->ifa_index, rta_tb[IFA_LABEL])) + return 0; + + if (inet_addr_match_rta(&filter.pfx, rta_tb[IFA_LOCAL])) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELADDR; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++rth.seq; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (!brief) { + const char *name; + + if (filter.oneline || filter.flushb) { + const char *dev = ll_index_to_name(ifa->ifa_index); + + if (is_json_context()) { + print_int(PRINT_JSON, + "index", NULL, ifa->ifa_index); + print_string(PRINT_JSON, "dev", NULL, dev); + } else { + fprintf(fp, "%u: %s", ifa->ifa_index, dev); + } + } + + name = family_name(ifa->ifa_family); + if (*name != '?') { + print_string(PRINT_ANY, "family", " %s ", name); + } else { + print_int(PRINT_ANY, "family_index", " family %d ", + ifa->ifa_family); + } + } + + if (rta_tb[IFA_LOCAL]) { + print_color_string(PRINT_ANY, + ifa_family_color(ifa->ifa_family), + "local", "%s", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_LOCAL])); + if (rta_tb[IFA_ADDRESS] && + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), + RTA_DATA(rta_tb[IFA_LOCAL]), + ifa->ifa_family == AF_INET ? 4 : 16)) { + print_string(PRINT_FP, NULL, " %s ", "peer"); + print_color_string(PRINT_ANY, + ifa_family_color(ifa->ifa_family), + "address", + "%s", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_ADDRESS])); + } + print_int(PRINT_ANY, "prefixlen", "/%d ", ifa->ifa_prefixlen); + + if (rta_tb[IFA_RT_PRIORITY]) + print_uint(PRINT_ANY, "metric", "metric %u ", + rta_getattr_u32(rta_tb[IFA_RT_PRIORITY])); + } + + if (brief) + goto brief_exit; + + if (rta_tb[IFA_BROADCAST]) { + print_string(PRINT_FP, NULL, "%s ", "brd"); + print_color_string(PRINT_ANY, + ifa_family_color(ifa->ifa_family), + "broadcast", + "%s ", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_BROADCAST])); + } + + if (rta_tb[IFA_ANYCAST]) { + print_string(PRINT_FP, NULL, "%s ", "any"); + print_color_string(PRINT_ANY, + ifa_family_color(ifa->ifa_family), + "anycast", + "%s ", + format_host_rta(ifa->ifa_family, + rta_tb[IFA_ANYCAST])); + } + + print_string(PRINT_ANY, + "scope", + "scope %s ", + rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + + print_ifa_flags(fp, ifa, ifa_flags); + + if (rta_tb[IFA_LABEL]) + print_string(PRINT_ANY, + "label", + "%s", + rta_getattr_str(rta_tb[IFA_LABEL])); + + if (rta_tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + + print_nl(); + print_string(PRINT_FP, NULL, " valid_lft ", NULL); + + if (ci->ifa_valid == INFINITY_LIFE_TIME) { + print_uint(PRINT_JSON, + "valid_life_time", + NULL, INFINITY_LIFE_TIME); + print_string(PRINT_FP, NULL, "%s", "forever"); + } else { + print_uint(PRINT_ANY, + "valid_life_time", "%usec", ci->ifa_valid); + } + + print_string(PRINT_FP, NULL, " preferred_lft ", NULL); + if (ci->ifa_prefered == INFINITY_LIFE_TIME) { + print_uint(PRINT_JSON, + "preferred_life_time", + NULL, INFINITY_LIFE_TIME); + print_string(PRINT_FP, NULL, "%s", "forever"); + } else { + if (ifa_flags & IFA_F_DEPRECATED) + print_int(PRINT_ANY, + "preferred_life_time", + "%dsec", + ci->ifa_prefered); + else + print_uint(PRINT_ANY, + "preferred_life_time", + "%usec", + ci->ifa_prefered); + } + } + print_string(PRINT_FP, NULL, "%s", "\n"); +brief_exit: + fflush(fp); + return 0; +} + +static int print_selected_addrinfo(struct ifinfomsg *ifi, + struct nlmsg_list *ainfo, FILE *fp) +{ + open_json_array(PRINT_JSON, "addr_info"); + for ( ; ainfo ; ainfo = ainfo->next) { + struct nlmsghdr *n = &ainfo->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (n->nlmsg_type != RTM_NEWADDR) + continue; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifa))) + return -1; + + if (ifa->ifa_index != ifi->ifi_index || + (filter.family && filter.family != ifa->ifa_family)) + continue; + + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + continue; + + open_json_object(NULL); + print_addrinfo(n, fp); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); + + if (brief) { + print_string(PRINT_FP, NULL, "%s", "\n"); + fflush(fp); + } + return 0; +} + + +static int store_nlmsg(struct nlmsghdr *n, void *arg) +{ + struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg; + struct nlmsg_list *h; + + h = malloc(n->nlmsg_len+sizeof(void *)); + if (h == NULL) + return -1; + + memcpy(&h->h, n, n->nlmsg_len); + h->next = NULL; + + if (lchain->tail) + lchain->tail->next = h; + else + lchain->head = h; + lchain->tail = h; + + ll_remember_index(n, NULL); + return 0; +} + +static __u32 ipadd_dump_magic = 0x47361222; + +static int ipadd_save_prep(void) +{ + int ret; + + if (isatty(STDOUT_FILENO)) { + fprintf(stderr, "Not sending a binary stream to stdout\n"); + return -1; + } + + ret = write(STDOUT_FILENO, &ipadd_dump_magic, sizeof(ipadd_dump_magic)); + if (ret != sizeof(ipadd_dump_magic)) { + fprintf(stderr, "Can't write magic to dump file\n"); + return -1; + } + + return 0; +} + +static int ipadd_dump_check_magic(void) +{ + int ret; + __u32 magic = 0; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "Can't restore address dump from a terminal\n"); + return -1; + } + + ret = fread(&magic, sizeof(magic), 1, stdin); + if (magic != ipadd_dump_magic) { + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic); + return -1; + } + + return 0; +} + +static int save_nlmsg(struct nlmsghdr *n, void *arg) +{ + int ret; + + ret = write(STDOUT_FILENO, n, n->nlmsg_len); + if ((ret > 0) && (ret != n->nlmsg_len)) { + fprintf(stderr, "Short write while saving nlmsg\n"); + ret = -EIO; + } + + return ret == n->nlmsg_len ? 0 : ret; +} + +static int show_handler(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + open_json_object(NULL); + print_int(PRINT_ANY, "index", "if%d:", ifa->ifa_index); + print_nl(); + print_addrinfo(n, stdout); + close_json_object(); + return 0; +} + +static int ipaddr_showdump(void) +{ + int err; + + if (ipadd_dump_check_magic()) + exit(-1); + + new_json_obj(json); + open_json_object(NULL); + open_json_array(PRINT_JSON, "addr_info"); + + err = rtnl_from_file(stdin, &show_handler, NULL); + + close_json_array(PRINT_JSON, NULL); + close_json_object(); + delete_json_obj(); + + exit(err); +} + +static int restore_handler(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + int ret; + + n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + + ll_init_map(&rth); + + ret = rtnl_talk(&rth, n, NULL); + if ((ret < 0) && (errno == EEXIST)) + ret = 0; + + return ret; +} + +static int ipaddr_restore(void) +{ + if (ipadd_dump_check_magic()) + exit(-1); + + exit(rtnl_from_file(stdin, &restore_handler, NULL)); +} + +void free_nlmsg_chain(struct nlmsg_chain *info) +{ + struct nlmsg_list *l, *n; + + for (l = info->head; l; l = n) { + n = l->next; + free(l); + } +} + +static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo) +{ + struct nlmsg_list *l, **lp; + + lp = &linfo->head; + while ((l = *lp) != NULL) { + int ok = 0; + int missing_net_address = 1; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + struct nlmsg_list *a; + + for (a = ainfo->head; a; a = a->next) { + struct nlmsghdr *n = &a->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + struct rtattr *tb[IFA_MAX + 1]; + unsigned int ifa_flags; + + if (ifa->ifa_index != ifi->ifi_index) + continue; + missing_net_address = 0; + if (filter.family && filter.family != ifa->ifa_family) + continue; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + continue; + + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + ifa_flags = get_ifa_flags(ifa, tb[IFA_FLAGS]); + + if ((filter.flags ^ ifa_flags) & filter.flagmask) + continue; + + if (ifa_label_match_rta(ifa->ifa_index, tb[IFA_LABEL])) + continue; + + if (!tb[IFA_LOCAL]) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + if (inet_addr_match_rta(&filter.pfx, tb[IFA_LOCAL])) + continue; + + ok = 1; + break; + } + if (missing_net_address && + (filter.family == AF_UNSPEC || filter.family == AF_PACKET)) + ok = 1; + if (!ok) { + *lp = l->next; + free(l); + } else + lp = &l->next; + } +} + +static int ipaddr_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + struct ifaddrmsg *ifa = NLMSG_DATA(nlh); + + ifa->ifa_index = filter.ifindex; + + return 0; +} + +static int ipaddr_flush(void) +{ + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + + while ((max_flush_loops == 0) || (round < max_flush_loops)) { + if (rtnl_addrdump_req(&rth, filter.family, + ipaddr_dump_filter) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter_nc(&rth, print_addrinfo, + stdout, NLM_F_DUMP_INTR) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + flush_done: + if (show_stats) { + if (round == 0) + printf("Nothing to flush.\n"); + else + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); + } + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + return 1; + + if (show_stats) { + printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed); + fflush(stdout); + } + + /* If we are flushing, and specifying primary, then we + * want to flush only a single round. Otherwise, we'll + * start flushing secondaries that were promoted to + * primaries. + */ + if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY)) + goto flush_done; + } + fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops); + fflush(stderr); + return 1; +} + +static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + err = addattr32(nlh, reqlen, IFLA_EXT_MASK, RTEXT_FILTER_VF); + if (err) + return err; + + if (filter.master) { + err = addattr32(nlh, reqlen, IFLA_MASTER, filter.master); + if (err) + return err; + } + + if (filter.kind) { + struct rtattr *linkinfo; + + linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO); + + err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, filter.kind, + strlen(filter.kind)); + if (err) + return err; + + addattr_nest_end(nlh, linkinfo); + } + + return 0; +} + +static int ipaddr_link_get(int index, struct nlmsg_chain *linfo) +{ + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = filter.family, + .i.ifi_index = index, + }; + __u32 filt_mask = RTEXT_FILTER_VF; + struct nlmsghdr *answer; + + if (!show_stats) + filt_mask |= RTEXT_FILTER_SKIP_STATS; + + addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { + perror("Cannot send link request"); + return 1; + } + + if (store_nlmsg(answer, linfo) < 0) { + fprintf(stderr, "Failed to process link information\n"); + return 1; + } + + return 0; +} + +/* fills in linfo with link data and optionally ainfo with address info + * caller can walk lists as desired and must call free_nlmsg_chain for + * both when done + */ +int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo) +{ + if (rtnl_linkdump_req_filter_fn(&rth, preferred_family, + filter_fn) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, store_nlmsg, linfo) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +static int ip_addr_list(struct nlmsg_chain *ainfo) +{ + if (rtnl_addrdump_req(&rth, filter.family, ipaddr_dump_filter) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +static int ipaddr_list_flush_or_save(int argc, char **argv, int action) +{ + struct nlmsg_chain linfo = { NULL, NULL}; + struct nlmsg_chain _ainfo = { NULL, NULL}, *ainfo = &_ainfo; + struct nlmsg_list *l; + char *filter_dev = NULL; + int no_link = 0; + + ipaddr_reset_filter(oneline, 0); + filter.showqueue = 1; + filter.family = preferred_family; + + if (action == IPADD_FLUSH) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + + return -1; + } + if (filter.family == AF_PACKET) { + fprintf(stderr, "Cannot flush link addresses.\n"); + return -1; + } + } + + while (argc > 0) { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + if (get_prefix(&filter.pfx, *argv, filter.family)) + invarg("invalid \"to\"\n", *argv); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } else if (strcmp(*argv, "scope") == 0) { + unsigned int scope = 0; + + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (strcmp(*argv, "up") == 0) { + filter.up = 1; + } else if (get_filter(*argv) == 0) { + + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + filter.label = *argv; + } else if (strcmp(*argv, "group") == 0) { + NEXT_ARG(); + if (rtnl_group_a2n(&filter.group, *argv)) + invarg("Invalid \"group\" value\n", *argv); + } else if (strcmp(*argv, "master") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + filter.master = ifindex; + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + filter.master = ifindex; + } else if (strcmp(*argv, "type") == 0) { + int soff; + + NEXT_ARG(); + soff = strlen(*argv) - strlen("_slave"); + if (!strcmp(*argv + soff, "_slave")) { + (*argv)[soff] = '\0'; + filter.slave_kind = *argv; + } else { + filter.kind = *argv; + } + } else { + if (strcmp(*argv, "dev") == 0) + NEXT_ARG(); + else if (matches(*argv, "help") == 0) + usage(); + if (filter_dev) + duparg2("dev", *argv); + filter_dev = *argv; + } + argv++; argc--; + } + + if (filter_dev) { + filter.ifindex = ll_name_to_index(filter_dev); + if (filter.ifindex <= 0) { + fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev); + return -1; + } + } + + if (action == IPADD_FLUSH) + return ipaddr_flush(); + + if (action == IPADD_SAVE) { + if (ipadd_save_prep()) + exit(1); + + if (rtnl_addrdump_req(&rth, preferred_family, + ipaddr_dump_filter) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, save_nlmsg, stdout) < 0) { + fprintf(stderr, "Save terminated\n"); + exit(1); + } + + exit(0); + } + + /* + * Initialize a json_writer and open an array object + * if -json was specified. + */ + new_json_obj(json); + + /* + * If only filter_dev present and none of the other + * link filters are present, use RTM_GETLINK to get + * the link device + */ + if (filter_dev && filter.group == -1 && do_link == 1) { + if (iplink_get(filter_dev, RTEXT_FILTER_VF) < 0) { + perror("Cannot send link get request"); + delete_json_obj(); + exit(1); + } + delete_json_obj(); + goto out; + } + + if (filter.ifindex) { + if (ipaddr_link_get(filter.ifindex, &linfo) != 0) + goto out; + } else { + if (ip_link_list(iplink_filter_req, &linfo) != 0) + goto out; + } + + if (filter.family != AF_PACKET) { + if (filter.oneline) + no_link = 1; + + if (ip_addr_list(ainfo) != 0) + goto out; + + ipaddr_filter(&linfo, ainfo); + } + + for (l = linfo.head; l; l = l->next) { + struct nlmsghdr *n = &l->h; + struct ifinfomsg *ifi = NLMSG_DATA(n); + int res = 0; + + open_json_object(NULL); + if (brief || !no_link) + res = print_linkinfo(n, stdout); + if (res >= 0 && filter.family != AF_PACKET) + print_selected_addrinfo(ifi, ainfo->head, stdout); + if (res > 0 && !do_link && show_stats) + print_link_stats(stdout, n); + close_json_object(); + } + fflush(stdout); + +out: + free_nlmsg_chain(ainfo); + free_nlmsg_chain(&linfo); + delete_json_obj(); + return 0; +} + +static void +ipaddr_loop_each_vf(struct rtattr *tb[], int vfnum, int *min, int *max) +{ + struct rtattr *vflist = tb[IFLA_VFINFO_LIST]; + struct rtattr *i, *vf[IFLA_VF_MAX+1]; + struct ifla_vf_rate *vf_rate; + int rem; + + rem = RTA_PAYLOAD(vflist); + + for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + parse_rtattr_nested(vf, IFLA_VF_MAX, i); + + if (!vf[IFLA_VF_RATE]) { + fprintf(stderr, "VF min/max rate API not supported\n"); + exit(1); + } + + vf_rate = RTA_DATA(vf[IFLA_VF_RATE]); + if (vf_rate->vf == vfnum) { + *min = vf_rate->min_tx_rate; + *max = vf_rate->max_tx_rate; + return; + } + } + fprintf(stderr, "Cannot find VF %d\n", vfnum); + exit(1); +} + +void ipaddr_get_vf_rate(int vfnum, int *min, int *max, const char *dev) +{ + struct nlmsg_chain linfo = { NULL, NULL}; + struct rtattr *tb[IFLA_MAX+1]; + struct ifinfomsg *ifi; + struct nlmsg_list *l; + struct nlmsghdr *n; + int idx, len; + + idx = ll_name_to_index(dev); + if (idx == 0) { + fprintf(stderr, "Device %s does not exist\n", dev); + exit(1); + } + + if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) { + perror("Cannot send dump request"); + exit(1); + } + if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + for (l = linfo.head; l; l = l->next) { + n = &l->h; + ifi = NLMSG_DATA(n); + + len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0 || (idx && idx != ifi->ifi_index)) + continue; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if ((tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF])) { + ipaddr_loop_each_vf(tb, vfnum, min, max); + return; + } + } +} + +int ipaddr_list_link(int argc, char **argv) +{ + preferred_family = AF_PACKET; + do_link = 1; + return ipaddr_list_flush_or_save(argc, argv, IPADD_LIST); +} + +void ipaddr_reset_filter(int oneline, int ifindex) +{ + memset(&filter, 0, sizeof(filter)); + filter.oneline = oneline; + filter.ifindex = ifindex; + filter.group = -1; +} + +static int default_scope(inet_prefix *lcl) +{ + if (lcl->family == AF_INET) { + if (lcl->bytelen >= 1 && *(__u8 *)&lcl->data == 127) + return RT_SCOPE_HOST; + } + return 0; +} + +static bool ipaddr_is_multicast(inet_prefix *a) +{ + if (a->family == AF_INET) + return IN_MULTICAST(ntohl(a->data[0])); + else if (a->family == AF_INET6) + return IN6_IS_ADDR_MULTICAST(a->data); + else + return false; +} + +static bool is_valid_label(const char *dev, const char *label) +{ + size_t len = strlen(dev); + + if (strncmp(label, dev, len) != 0) + return false; + + return label[len] == '\0' || label[len] == ':'; +} + +static int ipaddr_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ifaddrmsg ifa; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ifa.ifa_family = preferred_family, + }; + char *d = NULL; + char *l = NULL; + char *lcl_arg = NULL; + char *valid_lftp = NULL; + char *preferred_lftp = NULL; + inet_prefix lcl = {}; + inet_prefix peer; + int local_len = 0; + int peer_len = 0; + int brd_len = 0; + int any_len = 0; + int scoped = 0; + __u32 preferred_lft = INFINITY_LIFE_TIME; + __u32 valid_lft = INFINITY_LIFE_TIME; + unsigned int ifa_flags = 0; + + while (argc > 0) { + if (strcmp(*argv, "peer") == 0 || + strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + + if (peer_len) + duparg("peer", *argv); + get_prefix(&peer, *argv, req.ifa.ifa_family); + peer_len = peer.bytelen; + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = peer.family; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); + req.ifa.ifa_prefixlen = peer.bitlen; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (brd_len) + duparg("broadcast", *argv); + if (strcmp(*argv, "+") == 0) + brd_len = -1; + else if (strcmp(*argv, "-") == 0) + brd_len = -2; + else { + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); + brd_len = addr.bytelen; + } + } else if (strcmp(*argv, "anycast") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (any_len) + duparg("anycast", *argv); + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); + any_len = addr.bytelen; + } else if (strcmp(*argv, "scope") == 0) { + unsigned int scope = 0; + + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid scope value.", *argv); + req.ifa.ifa_scope = scope; + scoped = 1; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + l = *argv; + addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + matches(*argv, "preference") == 0) { + __u32 metric; + + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), IFA_RT_PRIORITY, metric); + } else if (matches(*argv, "valid_lft") == 0) { + if (valid_lftp) + duparg("valid_lft", *argv); + NEXT_ARG(); + valid_lftp = *argv; + if (set_lifetime(&valid_lft, *argv)) + invarg("valid_lft value", *argv); + } else if (matches(*argv, "preferred_lft") == 0) { + if (preferred_lftp) + duparg("preferred_lft", *argv); + NEXT_ARG(); + preferred_lftp = *argv; + if (set_lifetime(&preferred_lft, *argv)) + invarg("preferred_lft value", *argv); + } else if (lookup_flag_data_by_name(*argv)) { + const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(*argv); + if (flag_data->readonly) { + fprintf(stderr, "Warning: %s option is not mutable from userspace\n", flag_data->name); + } else if (flag_data->v6only && req.ifa.ifa_family != AF_INET6) { + fprintf(stderr, "Warning: %s option can be set only for IPv6 addresses\n", flag_data->name); + } else { + ifa_flags |= flag_data->mask; + } + } else { + if (strcmp(*argv, "local") == 0) + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (local_len) + duparg2("local", *argv); + lcl_arg = *argv; + get_prefix(&lcl, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = lcl.family; + addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); + local_len = lcl.bytelen; + } + argc--; argv++; + } + if (ifa_flags <= 0xff) + req.ifa.ifa_flags = ifa_flags; + else + addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags); + + if (d == NULL) { + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); + return -1; + } + if (l && !is_valid_label(d, l)) { + fprintf(stderr, + "\"label\" (%s) must match \"dev\" (%s) or be prefixed by \"dev\" with a colon.\n", + l, d); + return -1; + } + + if (peer_len == 0 && local_len) { + if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) { + fprintf(stderr, + "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" + " Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" + " This special behaviour is likely to disappear in further releases,\n" + " fix your scripts!\n", lcl_arg, local_len*8); + } else { + peer = lcl; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); + } + } + if (req.ifa.ifa_prefixlen == 0) + req.ifa.ifa_prefixlen = lcl.bitlen; + + if (brd_len < 0 && cmd != RTM_DELADDR) { + inet_prefix brd; + int i; + + if (req.ifa.ifa_family != AF_INET) { + fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n"); + return -1; + } + brd = peer; + if (brd.bitlen <= 30) { + for (i = 31; i >= brd.bitlen; i--) { + if (brd_len == -1) + brd.data[0] |= htonl(1<<(31-i)); + else + brd.data[0] &= ~htonl(1<<(31-i)); + } + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); + brd_len = brd.bytelen; + } + } + if (!scoped && cmd != RTM_DELADDR) + req.ifa.ifa_scope = default_scope(&lcl); + + req.ifa.ifa_index = ll_name_to_index(d); + if (!req.ifa.ifa_index) + return nodev(d); + + if (valid_lftp || preferred_lftp) { + struct ifa_cacheinfo cinfo = {}; + + if (!valid_lft) { + fprintf(stderr, "valid_lft is zero\n"); + return -1; + } + if (valid_lft < preferred_lft) { + fprintf(stderr, "preferred_lft is greater than valid_lft\n"); + return -1; + } + + cinfo.ifa_prefered = preferred_lft; + cinfo.ifa_valid = valid_lft; + addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo, + sizeof(cinfo)); + } + + if ((ifa_flags & IFA_F_MCAUTOJOIN) && !ipaddr_is_multicast(&lcl)) { + fprintf(stderr, "autojoin needs multicast address\n"); + return -1; + } + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +int do_ipaddr(int argc, char **argv) +{ + if (argc < 1) + return ipaddr_list_flush_or_save(0, NULL, IPADD_LIST); + if (matches(*argv, "add") == 0) + return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "change") == 0 || + strcmp(*argv, "chg") == 0) + return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_LIST); + if (matches(*argv, "flush") == 0) + return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_FLUSH); + if (matches(*argv, "save") == 0) + return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_SAVE); + if (matches(*argv, "showdump") == 0) + return ipaddr_showdump(); + if (matches(*argv, "restore") == 0) + return ipaddr_restore(); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c new file mode 100644 index 0000000..beb08da --- /dev/null +++ b/ip/ipaddrlabel.c @@ -0,0 +1,273 @@ +/* + * ipaddrlabel.c "ip addrlabel" + * + * Copyright (C)2007 USAGI/WIDE Project + * + * 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, see . + * + * + * Based on iprule.c. + * + * Authors: YOSHIFUJI Hideaki + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +#define IFAL_RTA(r) ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)))) +#define IFAL_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct ifaddrlblmsg)) + +extern struct rtnl_handle rth; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip addrlabel { add | del } prefix PREFIX [ dev DEV ] [ label LABEL ]\n" + " ip addrlabel [ list | flush | help ]\n"); + exit(-1); +} + +int print_addrlabel(struct nlmsghdr *n, void *arg) +{ + struct ifaddrlblmsg *ifal = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[IFAL_MAX+1]; + + if (n->nlmsg_type != RTM_NEWADDRLABEL && n->nlmsg_type != RTM_DELADDRLABEL) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifal)); + if (len < 0) + return -1; + + parse_rtattr(tb, IFAL_MAX, IFAL_RTA(ifal), len); + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELADDRLABEL) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[IFAL_ADDRESS]) { + const char *host + = format_host_rta(ifal->ifal_family, + tb[IFAL_ADDRESS]); + + print_string(PRINT_FP, NULL, "prefix ", NULL); + print_color_string(PRINT_ANY, + ifa_family_color(ifal->ifal_family), + "address", "%s", host); + + print_uint(PRINT_ANY, "prefixlen", "/%u ", + ifal->ifal_prefixlen); + } + + if (ifal->ifal_index) { + print_string(PRINT_FP, NULL, "dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s ", + ll_index_to_name(ifal->ifal_index)); + } + + if (tb[IFAL_LABEL] && RTA_PAYLOAD(tb[IFAL_LABEL]) == sizeof(uint32_t)) { + uint32_t label = rta_getattr_u32(tb[IFAL_LABEL]); + + print_uint(PRINT_ANY, + "label", "label %u ", label); + } + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + + return 0; +} + +static int ipaddrlabel_list(int argc, char **argv) +{ + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET6; + + if (argc > 0) { + fprintf(stderr, "\"ip addrlabel show\" does not take any arguments.\n"); + return -1; + } + + if (rtnl_addrlbldump_req(&rth, af) < 0) { + perror("Cannot send dump request"); + return 1; + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_addrlabel, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + delete_json_obj(); + + return 0; +} + + +static int ipaddrlabel_modify(int cmd, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ifaddrlblmsg ifal; + char buf[1024]; + } req = { + .n.nlmsg_type = cmd, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .ifal.ifal_family = preferred_family, + }; + + inet_prefix prefix = {}; + uint32_t label = 0xffffffffUL; + char *p = NULL; + char *l = NULL; + + if (cmd == RTM_NEWADDRLABEL) { + req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; + } + + while (argc > 0) { + if (strcmp(*argv, "prefix") == 0) { + NEXT_ARG(); + p = *argv; + get_prefix(&prefix, *argv, preferred_family); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((req.ifal.ifal_index = ll_name_to_index(*argv)) == 0) + invarg("dev is invalid\n", *argv); + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + l = *argv; + if (get_u32(&label, *argv, 0) || label == 0xffffffffUL) + invarg("label is invalid\n", *argv); + } + argc--; + argv++; + } + if (p == NULL) { + fprintf(stderr, "Not enough information: \"prefix\" argument is required.\n"); + return -1; + } + if (l == NULL) { + fprintf(stderr, "Not enough information: \"label\" argument is required.\n"); + return -1; + } + addattr32(&req.n, sizeof(req), IFAL_LABEL, label); + addattr_l(&req.n, sizeof(req), IFAL_ADDRESS, &prefix.data, prefix.bytelen); + req.ifal.ifal_prefixlen = prefix.bitlen; + + if (req.ifal.ifal_family == AF_UNSPEC) + req.ifal.ifal_family = AF_INET6; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + + +static int flush_addrlabel(struct nlmsghdr *n, void *arg) +{ + struct rtnl_handle rth2; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[IFAL_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, IFAL_MAX, RTM_RTA(r), len); + + if (tb[IFAL_ADDRESS]) { + n->nlmsg_type = RTM_DELADDRLABEL; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth2, 0) < 0) + return -1; + + if (rtnl_talk(&rth2, n, NULL) < 0) + return -2; + + rtnl_close(&rth2); + } + + return 0; +} + +static int ipaddrlabel_flush(int argc, char **argv) +{ + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET6; + + if (argc > 0) { + fprintf(stderr, "\"ip addrlabel flush\" does not allow extra arguments\n"); + return -1; + } + + if (rtnl_addrlbldump_req(&rth, af) < 0) { + perror("Cannot send dump request"); + return -1; + } + + if (rtnl_dump_filter(&rth, flush_addrlabel, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + return -1; + } + + return 0; +} + +int do_ipaddrlabel(int argc, char **argv) +{ + if (argc < 1) { + return ipaddrlabel_list(0, NULL); + } else if (matches(argv[0], "list") == 0 || + matches(argv[0], "lst") == 0 || + matches(argv[0], "show") == 0) { + return ipaddrlabel_list(argc-1, argv+1); + } else if (matches(argv[0], "add") == 0) { + return ipaddrlabel_modify(RTM_NEWADDRLABEL, argc-1, argv+1); + } else if (matches(argv[0], "delete") == 0) { + return ipaddrlabel_modify(RTM_DELADDRLABEL, argc-1, argv+1); + } else if (matches(argv[0], "flush") == 0) { + return ipaddrlabel_flush(argc-1, argv+1); + } else if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip addrlabel help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipfou.c b/ip/ipfou.c new file mode 100644 index 0000000..9c69777 --- /dev/null +++ b/ip/ipfou.c @@ -0,0 +1,354 @@ +/* + * ipfou.c FOU (foo over UDP) support + * + * 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. + * + * Authors: Tom Herbert + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libgenl.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip fou add port PORT { ipproto PROTO | gue }\n" + " [ local IFADDR ] [ peer IFADDR ]\n" + " [ peer_port PORT ] [ dev IFNAME ]\n" + " ip fou del port PORT [ local IFADDR ]\n" + " [ peer IFADDR ] [ peer_port PORT ]\n" + " [ dev IFNAME ]\n" + " ip fou show\n" + "\n" + "Where: PROTO { ipproto-name | 1..255 }\n" + " PORT { 1..65535 }\n" + " IFADDR { addr }\n"); + + exit(-1); +} + +/* netlink socket */ +static struct rtnl_handle genl_rth = { .fd = -1 }; +static int genl_family = -1; + +#define FOU_REQUEST(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ + FOU_GENL_VERSION, _cmd, _flags) + +static int fou_parse_opt(int argc, char **argv, struct nlmsghdr *n, + bool adding) +{ + const char *local = NULL, *peer = NULL; + __u16 port, peer_port = 0; + __u8 family = preferred_family; + bool gue_set = false; + int ipproto_set = 0; + __u8 ipproto, type; + int port_set = 0; + int index = 0; + + if (preferred_family == AF_UNSPEC) { + family = AF_INET; + } + + while (argc > 0) { + if (!matches(*argv, "port")) { + NEXT_ARG(); + + if (get_be16(&port, *argv, 0) || port == 0) + invarg("invalid port", *argv); + port_set = 1; + } else if (!matches(*argv, "ipproto")) { + struct protoent *servptr; + + NEXT_ARG(); + + servptr = getprotobyname(*argv); + if (servptr) + ipproto = servptr->p_proto; + else if (get_u8(&ipproto, *argv, 0) || ipproto == 0) + invarg("invalid ipproto", *argv); + ipproto_set = 1; + } else if (!matches(*argv, "gue")) { + gue_set = true; + } else if (!matches(*argv, "-6")) { + family = AF_INET6; + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + + local = *argv; + } else if (!matches(*argv, "peer")) { + NEXT_ARG(); + + peer = *argv; + } else if (!matches(*argv, "peer_port")) { + NEXT_ARG(); + + if (get_be16(&peer_port, *argv, 0) || peer_port == 0) + invarg("invalid peer port", *argv); + } else if (!matches(*argv, "dev")) { + const char *ifname; + + NEXT_ARG(); + + ifname = *argv; + + if (check_ifname(ifname)) { + fprintf(stderr, "fou: invalid device name\n"); + exit(EXIT_FAILURE); + } + + index = ll_name_to_index(ifname); + + if (!index) { + fprintf(stderr, "fou: unknown device name\n"); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr + , "fou: unknown command \"%s\"?\n", *argv); + usage(); + return -1; + } + argc--, argv++; + } + + if (!port_set) { + fprintf(stderr, "fou: missing port\n"); + return -1; + } + + if (!ipproto_set && !gue_set && adding) { + fprintf(stderr, "fou: must set ipproto or gue\n"); + return -1; + } + + if (ipproto_set && gue_set) { + fprintf(stderr, "fou: cannot set ipproto and gue\n"); + return -1; + } + + if ((peer_port && !peer) || (peer && !peer_port)) { + fprintf(stderr, "fou: both peer and peer port must be set\n"); + return -1; + } + + type = gue_set ? FOU_ENCAP_GUE : FOU_ENCAP_DIRECT; + + addattr16(n, 1024, FOU_ATTR_PORT, port); + addattr8(n, 1024, FOU_ATTR_TYPE, type); + addattr8(n, 1024, FOU_ATTR_AF, family); + + if (ipproto_set) + addattr8(n, 1024, FOU_ATTR_IPPROTO, ipproto); + + if (local) { + inet_prefix local_addr; + __u8 attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 : + FOU_ATTR_LOCAL_V6; + + if (get_addr(&local_addr, local, family)) { + fprintf(stderr, "fou: parsing local address failed\n"); + exit(EXIT_FAILURE); + } + addattr_l(n, 1024, attr_type, &local_addr.data, + local_addr.bytelen); + } + + if (peer) { + inet_prefix peer_addr; + __u8 attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 : + FOU_ATTR_PEER_V6; + + if (get_addr(&peer_addr, peer, family)) { + fprintf(stderr, "fou: parsing peer address failed\n"); + exit(EXIT_FAILURE); + } + addattr_l(n, 1024, attr_type, &peer_addr.data, + peer_addr.bytelen); + + if (peer_port) + addattr16(n, 1024, FOU_ATTR_PEER_PORT, peer_port); + } + + if (index) + addattr32(n, 1024, FOU_ATTR_IFINDEX, index); + + return 0; +} + +static int do_add(int argc, char **argv) +{ + FOU_REQUEST(req, 1024, FOU_CMD_ADD, NLM_F_REQUEST); + + fou_parse_opt(argc, argv, &req.n, true); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int do_del(int argc, char **argv) +{ + FOU_REQUEST(req, 1024, FOU_CMD_DEL, NLM_F_REQUEST); + + fou_parse_opt(argc, argv, &req.n, false); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int print_fou_mapping(struct nlmsghdr *n, void *arg) +{ + __u8 family = AF_INET, local_attr_type, peer_attr_type, byte_len; + struct rtattr *tb[FOU_ATTR_MAX + 1]; + __u8 empty_buf[16] = {0}; + struct genlmsghdr *ghdr; + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return 0; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + parse_rtattr(tb, FOU_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + + open_json_object(NULL); + if (tb[FOU_ATTR_PORT]) + print_uint(PRINT_ANY, "port", "port %u", + ntohs(rta_getattr_u16(tb[FOU_ATTR_PORT]))); + + if (tb[FOU_ATTR_TYPE] && + rta_getattr_u8(tb[FOU_ATTR_TYPE]) == FOU_ENCAP_GUE) + print_null(PRINT_ANY, "gue", " gue", NULL); + else if (tb[FOU_ATTR_IPPROTO]) + print_uint(PRINT_ANY, "ipproto", + " ipproto %u", rta_getattr_u8(tb[FOU_ATTR_IPPROTO])); + + if (tb[FOU_ATTR_AF]) { + family = rta_getattr_u8(tb[FOU_ATTR_AF]); + + print_string(PRINT_JSON, "family", NULL, + family_name(family)); + + if (family == AF_INET6) + print_string(PRINT_FP, NULL, + " -6", NULL); + } + + local_attr_type = family == AF_INET ? FOU_ATTR_LOCAL_V4 : + FOU_ATTR_LOCAL_V6; + peer_attr_type = family == AF_INET ? FOU_ATTR_PEER_V4 : + FOU_ATTR_PEER_V6; + byte_len = af_bit_len(family) / 8; + + if (tb[local_attr_type] && memcmp(RTA_DATA(tb[local_attr_type]), + empty_buf, byte_len)) { + print_string(PRINT_ANY, "local", " local %s", + format_host_rta(family, tb[local_attr_type])); + } + + if (tb[peer_attr_type] && memcmp(RTA_DATA(tb[peer_attr_type]), + empty_buf, byte_len)) { + print_string(PRINT_ANY, "peer", " peer %s", + format_host_rta(family, tb[peer_attr_type])); + } + + if (tb[FOU_ATTR_PEER_PORT]) { + __u16 p_port = ntohs(rta_getattr_u16(tb[FOU_ATTR_PEER_PORT])); + + if (p_port) + print_uint(PRINT_ANY, "peer_port", " peer_port %u", + p_port); + + } + + if (tb[FOU_ATTR_IFINDEX]) { + int index = rta_getattr_s32(tb[FOU_ATTR_IFINDEX]); + + if (index) { + const char *ifname; + + ifname = ll_index_to_name(index); + + if (ifname) + print_string(PRINT_ANY, "dev", " dev %s", + ifname); + } + } + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); + + return 0; +} + +static int do_show(int argc, char **argv) +{ + FOU_REQUEST(req, 4096, FOU_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP); + + if (argc > 0) { + fprintf(stderr, + "\"ip fou show\" does not take any arguments.\n"); + return -1; + } + + if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) { + perror("Cannot send show request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&genl_rth, print_fou_mapping, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + delete_json_obj(); + fflush(stdout); + + return 0; +} + +int do_ipfou(int argc, char **argv) +{ + if (argc < 1) + usage(); + + if (matches(*argv, "help") == 0) + usage(); + + if (genl_init_handle(&genl_rth, FOU_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0) + return do_show(argc-1, argv+1); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip fou help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipila.c b/ip/ipila.c new file mode 100644 index 0000000..739ee4e --- /dev/null +++ b/ip/ipila.c @@ -0,0 +1,307 @@ +/* + * ipila.c ILA (Identifier Locator Addressing) support + * + * 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. + * + * Authors: Tom Herbert + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libgenl.h" +#include "utils.h" +#include "ip_common.h" +#include "ila_common.h" +#include "json_print.h" + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip ila add loc_match LOCATOR_MATCH loc LOCATOR [ dev DEV ] OPTIONS\n" + " ip ila del loc_match LOCATOR_MATCH [ loc LOCATOR ] [ dev DEV ]\n" + " ip ila list\n" + "OPTIONS := [ csum-mode { adj-transport | neutral-map | neutral-map-auto | no-action } ]\n" + " [ ident-type { luid | use-format } ]\n"); + + exit(-1); +} + +/* netlink socket */ +static struct rtnl_handle genl_rth = { .fd = -1 }; +static int genl_family = -1; + +#define ILA_REQUEST(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ + ILA_GENL_VERSION, _cmd, _flags) + +#define ILA_RTA(g) ((struct rtattr *)(((char *)(g)) + \ + NLMSG_ALIGN(sizeof(struct genlmsghdr)))) + +static void print_addr64(__u64 addr, char *buff, size_t len) +{ + __u16 *words = (__u16 *)&addr; + __u16 v; + int i, ret; + size_t written = 0; + char *sep = ":"; + + for (i = 0; i < 4; i++) { + v = ntohs(words[i]); + + if (i == 3) + sep = ""; + + ret = snprintf(&buff[written], len - written, "%x%s", v, sep); + written += ret; + } +} + +static void print_ila_locid(const char *tag, int attr, struct rtattr *tb[]) +{ + char abuf[256]; + + if (tb[attr]) + print_addr64(rta_getattr_u64(tb[attr]), + abuf, sizeof(abuf)); + else + snprintf(abuf, sizeof(abuf), "-"); + + /* 20 = sizeof("xxxx:xxxx:xxxx:xxxx") */ + print_string(PRINT_ANY, tag, "%-20s", abuf); +} + +static int print_ila_mapping(struct nlmsghdr *n, void *arg) +{ + struct genlmsghdr *ghdr; + struct rtattr *tb[ILA_ATTR_MAX + 1]; + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return 0; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + parse_rtattr(tb, ILA_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + + open_json_object(NULL); + print_ila_locid("locator_match", ILA_ATTR_LOCATOR_MATCH, tb); + print_ila_locid("locator", ILA_ATTR_LOCATOR, tb); + + if (tb[ILA_ATTR_IFINDEX]) { + __u32 ifindex + = rta_getattr_u32(tb[ILA_ATTR_IFINDEX]); + + print_color_string(PRINT_ANY, COLOR_IFNAME, + "interface", "%-16s", + ll_index_to_name(ifindex)); + } else { + print_string(PRINT_FP, NULL, "%-10s ", "-"); + } + + if (tb[ILA_ATTR_CSUM_MODE]) { + __u8 csum = rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]); + + print_string(PRINT_ANY, "csum_mode", "%s", + ila_csum_mode2name(csum)); + } else + print_string(PRINT_FP, NULL, "%-10s ", "-"); + + if (tb[ILA_ATTR_IDENT_TYPE]) + print_string(PRINT_ANY, "ident_type", "%s", + ila_ident_type2name(rta_getattr_u8( + tb[ILA_ATTR_IDENT_TYPE]))); + else + print_string(PRINT_FP, NULL, "%s", "-"); + + print_nl(); + close_json_object(); + + return 0; +} + +#define NLMSG_BUF_SIZE 4096 + +static int do_list(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_GET, NLM_F_REQUEST | NLM_F_DUMP); + + if (argc > 0) { + fprintf(stderr, "\"ip ila show\" does not take " + "any arguments.\n"); + return -1; + } + + if (rtnl_send(&genl_rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&genl_rth, print_ila_mapping, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int ila_parse_opt(int argc, char **argv, struct nlmsghdr *n, + bool adding) +{ + __u64 locator = 0; + __u64 locator_match = 0; + int ifindex = 0; + int csum_mode = 0; + int ident_type = 0; + bool loc_set = false; + bool loc_match_set = false; + bool ifindex_set = false; + bool csum_mode_set = false; + bool ident_type_set = false; + + while (argc > 0) { + if (!matches(*argv, "loc")) { + NEXT_ARG(); + + if (get_addr64(&locator, *argv) < 0) { + fprintf(stderr, "Bad locator: %s\n", *argv); + return -1; + } + loc_set = true; + } else if (!matches(*argv, "loc_match")) { + NEXT_ARG(); + + if (get_addr64(&locator_match, *argv) < 0) { + fprintf(stderr, "Bad locator to match: %s\n", + *argv); + return -1; + } + loc_match_set = true; + } else if (!matches(*argv, "csum-mode")) { + NEXT_ARG(); + + csum_mode = ila_csum_name2mode(*argv); + if (csum_mode < 0) { + fprintf(stderr, "Bad csum-mode: %s\n", + *argv); + return -1; + } + csum_mode_set = true; + } else if (!matches(*argv, "ident-type")) { + NEXT_ARG(); + + ident_type = ila_ident_name2type(*argv); + if (ident_type < 0) { + fprintf(stderr, "Bad ident-type: %s\n", + *argv); + return -1; + } + ident_type_set = true; + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) { + fprintf(stderr, "No such interface: %s\n", + *argv); + return -1; + } + ifindex_set = true; + } else { + usage(); + return -1; + } + argc--, argv++; + } + + if (adding) { + if (!loc_set) { + fprintf(stderr, "ila: missing locator\n"); + return -1; + } + if (!loc_match_set) { + fprintf(stderr, "ila: missing locator0match\n"); + return -1; + } + } + + if (loc_match_set) + addattr64(n, 1024, ILA_ATTR_LOCATOR_MATCH, locator_match); + + if (loc_set) + addattr64(n, 1024, ILA_ATTR_LOCATOR, locator); + + if (ifindex_set) + addattr32(n, 1024, ILA_ATTR_IFINDEX, ifindex); + + if (csum_mode_set) + addattr8(n, 1024, ILA_ATTR_CSUM_MODE, csum_mode); + + if (ident_type_set) + addattr8(n, 1024, ILA_ATTR_IDENT_TYPE, ident_type); + + return 0; +} + +static int do_add(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_ADD, NLM_F_REQUEST); + + ila_parse_opt(argc, argv, &req.n, true); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int do_del(int argc, char **argv) +{ + ILA_REQUEST(req, 1024, ILA_CMD_DEL, NLM_F_REQUEST); + + ila_parse_opt(argc, argv, &req.n, false); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +int do_ipila(int argc, char **argv) +{ + if (argc < 1) + usage(); + + if (matches(*argv, "help") == 0) + usage(); + + if (genl_init_handle(&genl_rth, ILA_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "list") == 0) + return do_list(argc-1, argv+1); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip ila help\".\n", + *argv); + exit(-1); +} diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c new file mode 100644 index 0000000..77bc324 --- /dev/null +++ b/ip/ipl2tp.c @@ -0,0 +1,850 @@ +/* + * ipl2tp.c "ip l2tp" + * + * 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. + * + * Original Author: James Chapman + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "libgenl.h" + +#include "utils.h" +#include "ip_common.h" + +enum { + L2TP_ADD, + L2TP_CHG, + L2TP_DEL, + L2TP_GET +}; + +struct l2tp_parm { + uint32_t tunnel_id; + uint32_t peer_tunnel_id; + uint32_t session_id; + uint32_t peer_session_id; + enum l2tp_encap_type encap; + uint16_t local_udp_port; + uint16_t peer_udp_port; + int cookie_len; + uint8_t cookie[8]; + int peer_cookie_len; + uint8_t peer_cookie[8]; + inet_prefix local_ip; + inet_prefix peer_ip; + + uint16_t pw_type; + unsigned int udp6_csum_tx:1; + unsigned int udp6_csum_rx:1; + unsigned int udp_csum:1; + unsigned int recv_seq:1; + unsigned int send_seq:1; + unsigned int tunnel:1; + unsigned int session:1; + int reorder_timeout; + const char *ifname; + uint8_t l2spec_type; + uint8_t l2spec_len; +}; + +struct l2tp_stats { + uint64_t data_rx_packets; + uint64_t data_rx_bytes; + uint64_t data_rx_errors; + uint64_t data_rx_oos_packets; + uint64_t data_rx_oos_discards; + uint64_t data_tx_packets; + uint64_t data_tx_bytes; + uint64_t data_tx_errors; +}; + +struct l2tp_data { + struct l2tp_parm config; + struct l2tp_stats stats; +}; + +/* netlink socket */ +static struct rtnl_handle genl_rth; +static int genl_family = -1; + +/***************************************************************************** + * Netlink actions + *****************************************************************************/ + +static int create_tunnel(struct l2tp_parm *p) +{ + uint32_t local_attr = L2TP_ATTR_IP_SADDR; + uint32_t peer_attr = L2TP_ATTR_IP_DADDR; + + GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_TUNNEL_CREATE, NLM_F_REQUEST | NLM_F_ACK); + + addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id); + addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id); + addattr8(&req.n, 1024, L2TP_ATTR_PROTO_VERSION, 3); + addattr16(&req.n, 1024, L2TP_ATTR_ENCAP_TYPE, p->encap); + + if (p->local_ip.family == AF_INET6) + local_attr = L2TP_ATTR_IP6_SADDR; + addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, + p->local_ip.bytelen); + + if (p->peer_ip.family == AF_INET6) + peer_attr = L2TP_ATTR_IP6_DADDR; + addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, + p->peer_ip.bytelen); + + if (p->encap == L2TP_ENCAPTYPE_UDP) { + addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port); + addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port); + if (p->udp_csum) + addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1); + if (!p->udp6_csum_tx) + addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX); + if (!p->udp6_csum_rx) + addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX); + } + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int delete_tunnel(struct l2tp_parm *p) +{ + GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_TUNNEL_DELETE, NLM_F_REQUEST | NLM_F_ACK); + + addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int create_session(struct l2tp_parm *p) +{ + GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_SESSION_CREATE, NLM_F_REQUEST | NLM_F_ACK); + + addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id); + addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id); + addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id); + addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id); + addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type); + addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type); + addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len); + + if (p->recv_seq) + addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1); + if (p->send_seq) + addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1); + if (p->reorder_timeout) + addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT, + p->reorder_timeout); + if (p->cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE, + p->cookie, p->cookie_len); + if (p->peer_cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE, + p->peer_cookie, p->peer_cookie_len); + if (p->ifname) + addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int delete_session(struct l2tp_parm *p) +{ + GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_SESSION_DELETE, NLM_F_REQUEST | NLM_F_ACK); + + addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id); + addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id); + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static void print_cookie(const char *name, const char *fmt, + const uint8_t *cookie, int len) +{ + char abuf[32]; + size_t n; + + n = snprintf(abuf, sizeof(abuf), + "%02x%02x%02x%02x", + cookie[0], cookie[1], cookie[2], cookie[3]); + if (len == 8) + snprintf(abuf + n, sizeof(abuf) - n, + "%02x%02x%02x%02x", + cookie[4], cookie[5], + cookie[6], cookie[7]); + + print_string(PRINT_ANY, name, fmt, abuf); +} + +static void print_tunnel(const struct l2tp_data *data) +{ + const struct l2tp_parm *p = &data->config; + char buf[INET6_ADDRSTRLEN]; + + open_json_object(NULL); + print_uint(PRINT_ANY, "tunnel_id", "Tunnel %u,", p->tunnel_id); + print_string(PRINT_ANY, "encap", " encap %s", + p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" : + p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??"); + print_nl(); + + print_string(PRINT_ANY, "local", " From %s ", + inet_ntop(p->local_ip.family, p->local_ip.data, + buf, sizeof(buf))); + print_string(PRINT_ANY, "peer", "to %s", + inet_ntop(p->peer_ip.family, p->peer_ip.data, + buf, sizeof(buf))); + print_nl(); + + print_uint(PRINT_ANY, "peer_tunnel", " Peer tunnel %u", + p->peer_tunnel_id); + print_nl(); + + if (p->encap == L2TP_ENCAPTYPE_UDP) { + print_string(PRINT_FP, NULL, + " UDP source / dest ports:", NULL); + + print_hu(PRINT_ANY, "local_port", " %hu", + p->local_udp_port); + print_hu(PRINT_ANY, "peer_port", "/%hu", + p->peer_udp_port); + print_nl(); + + switch (p->local_ip.family) { + case AF_INET: + print_bool(PRINT_JSON, "checksum", + NULL, p->udp_csum); + print_string(PRINT_FP, NULL, + " UDP checksum: %s\n", + p->udp_csum ? "enabled" : "disabled"); + break; + case AF_INET6: + if (is_json_context()) { + print_bool(PRINT_JSON, "checksum_tx", + NULL, p->udp6_csum_tx); + + print_bool(PRINT_JSON, "checksum_rx", + NULL, p->udp6_csum_tx); + } else { + printf(" UDP checksum: %s%s%s%s\n", + p->udp6_csum_tx && p->udp6_csum_rx + ? "enabled" : "", + p->udp6_csum_tx && !p->udp6_csum_rx + ? "tx" : "", + !p->udp6_csum_tx && p->udp6_csum_rx + ? "rx" : "", + !p->udp6_csum_tx && !p->udp6_csum_rx + ? "disabled" : ""); + } + break; + } + } + close_json_object(); +} + +static void print_session(struct l2tp_data *data) +{ + struct l2tp_parm *p = &data->config; + + open_json_object(NULL); + + print_uint(PRINT_ANY, "session_id", "Session %u", p->session_id); + print_uint(PRINT_ANY, "tunnel_id", " in tunnel %u", p->tunnel_id); + print_nl(); + + print_uint(PRINT_ANY, "peer_session_id", + " Peer session %u,", p->peer_session_id); + print_uint(PRINT_ANY, "peer_tunnel_id", + " tunnel %u", p->peer_tunnel_id); + print_nl(); + + if (p->ifname != NULL) { + print_color_string(PRINT_ANY, COLOR_IFNAME, + "interface", " interface name: %s" , p->ifname); + print_nl(); + } + + /* Show offsets only for plain console output (for legacy scripts) */ + print_uint(PRINT_FP, "offset", " offset %u,", 0); + print_uint(PRINT_FP, "peer_offset", " peer offset %u\n", 0); + + if (p->cookie_len > 0) + print_cookie("cookie", " cookie %s", + p->cookie, p->cookie_len); + + if (p->peer_cookie_len > 0) + print_cookie("peer_cookie", " peer cookie %s", + p->peer_cookie, p->peer_cookie_len); + + if (p->reorder_timeout != 0) + print_uint(PRINT_ANY, "reorder_timeout", + " reorder timeout: %u", p->reorder_timeout); + + + if (p->send_seq || p->recv_seq) { + print_string(PRINT_FP, NULL, "%s sequence numbering:", _SL_); + + if (p->send_seq) + print_null(PRINT_ANY, "send_seq", " send", NULL); + if (p->recv_seq) + print_null(PRINT_ANY, "recv_seq", " recv", NULL); + + } + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); +} + +static int get_response(struct nlmsghdr *n, void *arg) +{ + struct genlmsghdr *ghdr; + struct l2tp_data *data = arg; + struct l2tp_parm *p = &data->config; + struct rtattr *attrs[L2TP_ATTR_MAX + 1]; + struct rtattr *nla_stats, *rta; + int len; + + /* Validate message and parse attributes */ + if (n->nlmsg_type == NLMSG_ERROR) + return -EBADMSG; + + ghdr = NLMSG_DATA(n); + len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr)); + if (len < 0) + return -1; + + parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len); + + if (attrs[L2TP_ATTR_PW_TYPE]) + p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]); + if (attrs[L2TP_ATTR_ENCAP_TYPE]) + p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]); + if (attrs[L2TP_ATTR_CONN_ID]) + p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]); + if (attrs[L2TP_ATTR_PEER_CONN_ID]) + p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]); + if (attrs[L2TP_ATTR_SESSION_ID]) + p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]); + if (attrs[L2TP_ATTR_PEER_SESSION_ID]) + p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]); + if (attrs[L2TP_ATTR_L2SPEC_TYPE]) + p->l2spec_type = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_TYPE]); + if (attrs[L2TP_ATTR_L2SPEC_LEN]) + p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]); + + if (attrs[L2TP_ATTR_UDP_CSUM]) + p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]); + + p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]; + p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]; + + if (attrs[L2TP_ATTR_COOKIE]) + memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]), + p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE])); + + if (attrs[L2TP_ATTR_PEER_COOKIE]) + memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]), + p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE])); + + if (attrs[L2TP_ATTR_RECV_SEQ]) + p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]); + if (attrs[L2TP_ATTR_SEND_SEQ]) + p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]); + + if (attrs[L2TP_ATTR_RECV_TIMEOUT]) + p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]); + + rta = attrs[L2TP_ATTR_IP_SADDR]; + p->local_ip.family = AF_INET; + if (!rta) { + rta = attrs[L2TP_ATTR_IP6_SADDR]; + p->local_ip.family = AF_INET6; + } + if (rta && get_addr_rta(&p->local_ip, rta, p->local_ip.family)) + return -1; + + rta = attrs[L2TP_ATTR_IP_DADDR]; + p->peer_ip.family = AF_INET; + if (!rta) { + rta = attrs[L2TP_ATTR_IP6_DADDR]; + p->peer_ip.family = AF_INET6; + } + if (rta && get_addr_rta(&p->peer_ip, rta, p->peer_ip.family)) + return -1; + + if (attrs[L2TP_ATTR_UDP_SPORT]) + p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]); + if (attrs[L2TP_ATTR_UDP_DPORT]) + p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]); + if (attrs[L2TP_ATTR_IFNAME]) + p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]); + + nla_stats = attrs[L2TP_ATTR_STATS]; + if (nla_stats) { + struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1]; + + parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats); + + if (tb[L2TP_ATTR_TX_PACKETS]) + data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]); + if (tb[L2TP_ATTR_TX_BYTES]) + data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]); + if (tb[L2TP_ATTR_TX_ERRORS]) + data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]); + if (tb[L2TP_ATTR_RX_PACKETS]) + data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]); + if (tb[L2TP_ATTR_RX_BYTES]) + data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]); + if (tb[L2TP_ATTR_RX_ERRORS]) + data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]); + if (tb[L2TP_ATTR_RX_SEQ_DISCARDS]) + data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]); + if (tb[L2TP_ATTR_RX_OOS_PACKETS]) + data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]); + } + + return 0; +} + +static int session_nlmsg(struct nlmsghdr *n, void *arg) +{ + int ret = get_response(n, arg); + + if (ret == 0) + print_session(arg); + + return ret; +} + +static int get_session(struct l2tp_data *p) +{ + GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_SESSION_GET, + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST); + + req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq; + + if (p->config.tunnel_id && p->config.session_id) { + addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id); + addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, + p->config.session_id); + } + + if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) + return -2; + + new_json_obj(json); + if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int tunnel_nlmsg(struct nlmsghdr *n, void *arg) +{ + int ret = get_response(n, arg); + + if (ret == 0) + print_tunnel(arg); + + return ret; +} + +static int get_tunnel(struct l2tp_data *p) +{ + GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION, + L2TP_CMD_TUNNEL_GET, + NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST); + + req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq; + + if (p->config.tunnel_id) + addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id); + + if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) + return -2; + + new_json_obj(json); + if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + fflush(stdout); + + return 0; +} + +/***************************************************************************** + * Command parser + *****************************************************************************/ + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip l2tp add tunnel\n" + " remote ADDR local ADDR\n" + " tunnel_id ID peer_tunnel_id ID\n" + " [ encap { ip | udp } ]\n" + " [ udp_sport PORT ] [ udp_dport PORT ]\n" + " [ udp_csum { on | off } ]\n" + " [ udp6_csum_tx { on | off } ]\n" + " [ udp6_csum_rx { on | off } ]\n" + "Usage: ip l2tp add session [ name NAME ]\n" + " tunnel_id ID\n" + " session_id ID peer_session_id ID\n" + " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n" + " [ seq { none | send | recv | both } ]\n" + " [ l2spec_type L2SPEC ]\n" + " ip l2tp del tunnel tunnel_id ID\n" + " ip l2tp del session tunnel_id ID session_id ID\n" + " ip l2tp show tunnel [ tunnel_id ID ]\n" + " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n" + "\n" + "Where: NAME := STRING\n" + " ADDR := { IP_ADDRESS | any }\n" + " PORT := { 0..65535 }\n" + " ID := { 1..4294967295 }\n" + " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n" + " L2SPEC := { none | default }\n"); + + exit(-1); +} + +static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p) +{ + memset(p, 0, sizeof(*p)); + + if (argc == 0) + usage(); + + /* Defaults */ + p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT; + p->l2spec_len = 4; + p->udp6_csum_rx = 1; + p->udp6_csum_tx = 1; + + while (argc > 0) { + if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ip") == 0) { + p->encap = L2TP_ENCAPTYPE_IP; + } else if (strcmp(*argv, "udp") == 0) { + p->encap = L2TP_ENCAPTYPE_UDP; + } else { + fprintf(stderr, "Unknown tunnel encapsulation \"%s\"\n", *argv); + exit(-1); + } + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"name\" not a valid ifname", *argv); + p->ifname = *argv; + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + if (get_addr(&p->peer_ip, *argv, AF_UNSPEC)) + invarg("invalid remote address\n", *argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + if (get_addr(&p->local_ip, *argv, AF_UNSPEC)) + invarg("invalid local address\n", *argv); + } else if ((strcmp(*argv, "tunnel_id") == 0) || + (strcmp(*argv, "tid") == 0)) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0)) + invarg("invalid ID\n", *argv); + p->tunnel_id = uval; + } else if ((strcmp(*argv, "peer_tunnel_id") == 0) || + (strcmp(*argv, "ptid") == 0)) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0)) + invarg("invalid ID\n", *argv); + p->peer_tunnel_id = uval; + } else if ((strcmp(*argv, "session_id") == 0) || + (strcmp(*argv, "sid") == 0)) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0)) + invarg("invalid ID\n", *argv); + p->session_id = uval; + } else if ((strcmp(*argv, "peer_session_id") == 0) || + (strcmp(*argv, "psid") == 0)) { + __u32 uval; + + NEXT_ARG(); + if (get_u32(&uval, *argv, 0)) + invarg("invalid ID\n", *argv); + p->peer_session_id = uval; + } else if (strcmp(*argv, "udp_sport") == 0) { + __u16 uval; + + NEXT_ARG(); + if (get_u16(&uval, *argv, 0)) + invarg("invalid port\n", *argv); + p->local_udp_port = uval; + } else if (strcmp(*argv, "udp_dport") == 0) { + __u16 uval; + + NEXT_ARG(); + if (get_u16(&uval, *argv, 0)) + invarg("invalid port\n", *argv); + p->peer_udp_port = uval; + } else if (strcmp(*argv, "udp_csum") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp_csum = 1; + else if (strcmp(*argv, "off") == 0) + p->udp_csum = 0; + else + invarg("invalid option for udp_csum\n", *argv); + } else if (strcmp(*argv, "udp6_csum_rx") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp6_csum_rx = 1; + else if (strcmp(*argv, "off") == 0) + p->udp6_csum_rx = 0; + else + invarg("invalid option for udp6_csum_rx\n" + , *argv); + } else if (strcmp(*argv, "udp6_csum_tx") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + p->udp6_csum_tx = 1; + else if (strcmp(*argv, "off") == 0) + p->udp6_csum_tx = 0; + else + invarg("invalid option for udp6_csum_tx\n" + , *argv); + } else if (strcmp(*argv, "offset") == 0) { + fprintf(stderr, "Ignoring option \"offset\"\n"); + NEXT_ARG(); + } else if (strcmp(*argv, "peer_offset") == 0) { + fprintf(stderr, "Ignoring option \"peer_offset\"\n"); + NEXT_ARG(); + } else if (strcmp(*argv, "cookie") == 0) { + int slen; + + NEXT_ARG(); + slen = strlen(*argv); + if ((slen != 8) && (slen != 16)) + invarg("cookie must be either 8 or 16 hex digits\n", *argv); + + p->cookie_len = slen / 2; + if (hex2mem(*argv, p->cookie, p->cookie_len) < 0) + invarg("cookie must be a hex string\n", *argv); + } else if (strcmp(*argv, "peer_cookie") == 0) { + int slen; + + NEXT_ARG(); + slen = strlen(*argv); + if ((slen != 8) && (slen != 16)) + invarg("cookie must be either 8 or 16 hex digits\n", *argv); + + p->peer_cookie_len = slen / 2; + if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0) + invarg("cookie must be a hex string\n", *argv); + } else if (strcmp(*argv, "l2spec_type") == 0) { + NEXT_ARG(); + if (strcasecmp(*argv, "default") == 0) { + p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT; + p->l2spec_len = 4; + } else if (strcasecmp(*argv, "none") == 0) { + p->l2spec_type = L2TP_L2SPECTYPE_NONE; + p->l2spec_len = 0; + } else { + fprintf(stderr, + "Unknown layer2specific header type \"%s\"\n", + *argv); + exit(-1); + } + } else if (strcmp(*argv, "seq") == 0) { + NEXT_ARG(); + if (strcasecmp(*argv, "both") == 0) { + p->recv_seq = 1; + p->send_seq = 1; + } else if (strcasecmp(*argv, "recv") == 0) { + p->recv_seq = 1; + } else if (strcasecmp(*argv, "send") == 0) { + p->send_seq = 1; + } else if (strcasecmp(*argv, "none") == 0) { + p->recv_seq = 0; + p->send_seq = 0; + } else { + fprintf(stderr, + "Unknown seq value \"%s\"\n", *argv); + exit(-1); + } + } else if (strcmp(*argv, "tunnel") == 0) { + p->tunnel = 1; + } else if (strcmp(*argv, "session") == 0) { + p->session = 1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Unknown command: %s\n", *argv); + usage(); + } + + argc--; argv++; + } + + return 0; +} + + +static int do_add(int argc, char **argv) +{ + struct l2tp_parm p; + int ret = 0; + + if (parse_args(argc, argv, L2TP_ADD, &p) < 0) + return -1; + + if (!p.tunnel && !p.session) + missarg("tunnel or session"); + + if (p.tunnel_id == 0) + missarg("tunnel_id"); + + /* session_id and peer_session_id must be provided for sessions */ + if ((p.session) && (p.peer_session_id == 0)) + missarg("peer_session_id"); + if ((p.session) && (p.session_id == 0)) + missarg("session_id"); + + /* peer_tunnel_id is needed for tunnels */ + if ((p.tunnel) && (p.peer_tunnel_id == 0)) + missarg("peer_tunnel_id"); + + if (p.tunnel) { + if (p.local_ip.family == AF_UNSPEC) + missarg("local"); + + if (p.peer_ip.family == AF_UNSPEC) + missarg("remote"); + + if (p.encap == L2TP_ENCAPTYPE_UDP) { + if (p.local_udp_port == 0) + missarg("udp_sport"); + if (p.peer_udp_port == 0) + missarg("udp_dport"); + } + + ret = create_tunnel(&p); + } + + if (p.session) { + /* Only ethernet pseudowires supported */ + p.pw_type = L2TP_PWTYPE_ETH; + + ret = create_session(&p); + } + + return ret; +} + +static int do_del(int argc, char **argv) +{ + struct l2tp_parm p; + + if (parse_args(argc, argv, L2TP_DEL, &p) < 0) + return -1; + + if (!p.tunnel && !p.session) + missarg("tunnel or session"); + + if ((p.tunnel) && (p.tunnel_id == 0)) + missarg("tunnel_id"); + if ((p.session) && (p.session_id == 0)) + missarg("session_id"); + + if (p.session_id) + return delete_session(&p); + else + return delete_tunnel(&p); + + return -1; +} + +static int do_show(int argc, char **argv) +{ + struct l2tp_data data; + struct l2tp_parm *p = &data.config; + + if (parse_args(argc, argv, L2TP_GET, p) < 0) + return -1; + + if (!p->tunnel && !p->session) + missarg("tunnel or session"); + + if (p->session) + get_session(&data); + else + get_tunnel(&data); + + return 0; +} + +int do_ipl2tp(int argc, char **argv) +{ + if (argc < 1 || !matches(*argv, "help")) + usage(); + + if (genl_init_handle(&genl_rth, L2TP_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv); + exit(-1); +} diff --git a/ip/iplink.c b/ip/iplink.c new file mode 100644 index 0000000..5ec33a9 --- /dev/null +++ b/ip/iplink.c @@ -0,0 +1,1765 @@ +/* + * iplink.c "ip link". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "namespace.h" + +#define IPLINK_IOCTL_COMPAT 1 +#ifndef LIBDIR +#define LIBDIR "/usr/lib" +#endif + +#ifndef GSO_MAX_SIZE +#define GSO_MAX_SIZE 65536 +#endif +#ifndef GSO_MAX_SEGS +#define GSO_MAX_SEGS 65535 +#endif + + +static void usage(void) __attribute__((noreturn)); +static int iplink_have_newlink(void); + +void iplink_usage(void) +{ + if (iplink_have_newlink()) { + fprintf(stderr, + "Usage: ip link add [link DEV] [ name ] NAME\n" + " [ txqueuelen PACKETS ]\n" + " [ address LLADDR ]\n" + " [ broadcast LLADDR ]\n" + " [ mtu MTU ] [index IDX ]\n" + " [ numtxqueues QUEUE_COUNT ]\n" + " [ numrxqueues QUEUE_COUNT ]\n" + " type TYPE [ ARGS ]\n" + "\n" + " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n" + "\n" + " ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n" + " [ { up | down } ]\n" + " [ type TYPE ARGS ]\n"); + } else + fprintf(stderr, + "Usage: ip link set DEVICE [ { up | down } ]\n"); + + fprintf(stderr, + " [ arp { on | off } ]\n" + " [ dynamic { on | off } ]\n" + " [ multicast { on | off } ]\n" + " [ allmulticast { on | off } ]\n" + " [ promisc { on | off } ]\n" + " [ trailers { on | off } ]\n" + " [ carrier { on | off } ]\n" + " [ txqueuelen PACKETS ]\n" + " [ name NEWNAME ]\n" + " [ address LLADDR ]\n" + " [ broadcast LLADDR ]\n" + " [ mtu MTU ]\n" + " [ netns { PID | NAME } ]\n" + " [ link-netns NAME | link-netnsid ID ]\n" + " [ alias NAME ]\n" + " [ vf NUM [ mac LLADDR ]\n" + " [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n" + " [ rate TXRATE ]\n" + " [ max_tx_rate TXRATE ]\n" + " [ min_tx_rate TXRATE ]\n" + " [ spoofchk { on | off} ]\n" + " [ query_rss { on | off} ]\n" + " [ state { auto | enable | disable} ]\n" + " [ trust { on | off} ]\n" + " [ node_guid EUI64 ]\n" + " [ port_guid EUI64 ] ]\n" + " [ { xdp | xdpgeneric | xdpdrv | xdpoffload } { off |\n" + " object FILE [ section NAME ] [ verbose ] |\n" + " pinned FILE } ]\n" + " [ master DEVICE ][ vrf NAME ]\n" + " [ nomaster ]\n" + " [ addrgenmode { eui64 | none | stable_secret | random } ]\n" + " [ protodown { on | off } ]\n" + " [ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n" + "\n" + " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n" + "\n" + " ip link xstats type TYPE [ ARGS ]\n" + "\n" + " ip link afstats [ dev DEVICE ]\n" + " ip link property add dev DEVICE [ altname NAME .. ]\n" + " ip link property del dev DEVICE [ altname NAME .. ]\n"); + + if (iplink_have_newlink()) { + fprintf(stderr, + "\n" + " ip link help [ TYPE ]\n" + "\n" + "TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n" + " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" + " gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n" + " vti | nlmon | team_slave | bond_slave | bridge_slave |\n" + " ipvlan | ipvtap | geneve | bareudp | vrf | macsec | netdevsim | rmnet |\n" + " xfrm }\n"); + } + exit(-1); +} + +static void usage(void) +{ + iplink_usage(); +} + +static int on_off(const char *msg, const char *realval) +{ + fprintf(stderr, + "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", + msg, realval); + return -1; +} + +static void *BODY; /* cached dlopen(NULL) handle */ +static struct link_util *linkutil_list; + +struct link_util *get_link_kind(const char *id) +{ + void *dlh; + char buf[256]; + struct link_util *l; + + for (l = linkutil_list; l; l = l->next) + if (strcmp(l->id, id) == 0) + return l; + + snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + /* look in current binary, only open once */ + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + return NULL; + } + } + + snprintf(buf, sizeof(buf), "%s_link_util", id); + l = dlsym(dlh, buf); + if (l == NULL) + return NULL; + + l->next = linkutil_list; + linkutil_list = l; + return l; +} + +static int get_link_mode(const char *mode) +{ + if (strcasecmp(mode, "default") == 0) + return IF_LINK_MODE_DEFAULT; + if (strcasecmp(mode, "dormant") == 0) + return IF_LINK_MODE_DORMANT; + return -1; +} + +static int get_addr_gen_mode(const char *mode) +{ + if (strcasecmp(mode, "eui64") == 0) + return IN6_ADDR_GEN_MODE_EUI64; + if (strcasecmp(mode, "none") == 0) + return IN6_ADDR_GEN_MODE_NONE; + if (strcasecmp(mode, "stable_secret") == 0) + return IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + if (strcasecmp(mode, "random") == 0) + return IN6_ADDR_GEN_MODE_RANDOM; + return -1; +} + +#if IPLINK_IOCTL_COMPAT +static int have_rtnl_newlink = -1; + +static int accept_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); + + if (n->nlmsg_type == NLMSG_ERROR && + (err->error == -EOPNOTSUPP || err->error == -EINVAL)) + have_rtnl_newlink = 0; + else + have_rtnl_newlink = 1; + return -1; +} + +static int iplink_have_newlink(void) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = RTM_NEWLINK, + .i.ifi_family = AF_UNSPEC, + }; + + if (have_rtnl_newlink < 0) { + if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { + perror("request send failed"); + exit(1); + } + rtnl_listen(&rth, accept_msg, NULL); + } + return have_rtnl_newlink; +} +#else /* IPLINK_IOCTL_COMPAT */ +static int iplink_have_newlink(void) +{ + return 1; +} +#endif /* ! IPLINK_IOCTL_COMPAT */ + +static int nl_get_ll_addr_len(const char *ifname) +{ + int len; + int dev_index = ll_name_to_index(ifname); + struct iplink_req req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST + }, + .i = { + .ifi_family = preferred_family, + .ifi_index = dev_index, + } + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX+1]; + + if (dev_index == 0) + return -1; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -1; + + len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); + if (len < 0) { + free(answer); + return -1; + } + + parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), + len, NLA_F_NESTED); + if (!tb[IFLA_ADDRESS]) { + free(answer); + return -1; + } + + len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); + free(answer); + return len; +} + +static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp, + struct ifla_vf_vlan_info *ivvip) +{ + int argc = *argcp; + char **argv = *argvp; + unsigned int vci; + + NEXT_ARG(); + if (get_unsigned(&vci, *argv, 0) || vci > 4095) + invarg("Invalid \"vlan\" value\n", *argv); + + ivvip->vlan = vci; + ivvip->vf = vf; + ivvip->qos = 0; + ivvip->vlan_proto = htons(ETH_P_8021Q); + if (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "qos") == 0) { + NEXT_ARG(); + if (get_unsigned(&ivvip->qos, *argv, 0)) + invarg("Invalid \"qos\" value\n", *argv); + } else { + /* rewind arg */ + PREV_ARG(); + } + } + if (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "proto") == 0) { + NEXT_ARG(); + if (ll_proto_a2n(&ivvip->vlan_proto, *argv)) + invarg("protocol is invalid\n", *argv); + if (ivvip->vlan_proto != htons(ETH_P_8021AD) && + ivvip->vlan_proto != htons(ETH_P_8021Q)) { + SPRINT_BUF(b1); + SPRINT_BUF(b2); + char msg[64 + sizeof(b1) + sizeof(b2)]; + + sprintf(msg, + "Invalid \"vlan protocol\" value - supported %s, %s\n", + ll_proto_n2a(htons(ETH_P_8021Q), + b1, sizeof(b1)), + ll_proto_n2a(htons(ETH_P_8021AD), + b2, sizeof(b2))); + invarg(msg, *argv); + } + } else { + /* rewind arg */ + PREV_ARG(); + } + } + + *argcp = argc; + *argvp = argv; +} + +static int iplink_parse_vf(int vf, int *argcp, char ***argvp, + struct iplink_req *req, const char *dev) +{ + char new_rate_api = 0, count = 0, override_legacy_rate = 0; + struct ifla_vf_rate tivt; + int len, argc = *argcp; + char **argv = *argvp; + struct rtattr *vfinfo; + + tivt.min_tx_rate = -1; + tivt.max_tx_rate = -1; + + vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO); + + while (NEXT_ARG_OK()) { + NEXT_ARG(); + count++; + if (!matches(*argv, "max_tx_rate")) { + /* new API in use */ + new_rate_api = 1; + /* override legacy rate */ + override_legacy_rate = 1; + } else if (!matches(*argv, "min_tx_rate")) { + /* new API in use */ + new_rate_api = 1; + } + } + + while (count--) { + /* rewind arg */ + PREV_ARG(); + } + + while (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "mac") == 0) { + struct ifla_vf_mac ivm = { 0 }; + int halen = nl_get_ll_addr_len(dev); + + NEXT_ARG(); + ivm.vf = vf; + len = ll_addr_a2n((char *)ivm.mac, 32, *argv); + if (len < 0) + return -1; + if (halen > 0 && len != halen) { + fprintf(stderr, + "Invalid address length %d - must be %d bytes\n", + len, halen); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, + &ivm, sizeof(ivm)); + } else if (matches(*argv, "vlan") == 0) { + struct ifla_vf_vlan_info ivvi; + + iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi); + /* support the old interface in case of older kernel*/ + if (ivvi.vlan_proto == htons(ETH_P_8021Q)) { + struct ifla_vf_vlan ivv; + + ivv.vf = ivvi.vf; + ivv.vlan = ivvi.vlan; + ivv.qos = ivvi.qos; + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN, &ivv, sizeof(ivv)); + } else { + struct rtattr *vfvlanlist; + + vfvlanlist = addattr_nest(&req->n, sizeof(*req), + IFLA_VF_VLAN_LIST); + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN_INFO, &ivvi, + sizeof(ivvi)); + + while (NEXT_ARG_OK()) { + NEXT_ARG(); + if (matches(*argv, "vlan") != 0) { + PREV_ARG(); + break; + } + iplink_parse_vf_vlan_info(vf, &argc, + &argv, &ivvi); + addattr_l(&req->n, sizeof(*req), + IFLA_VF_VLAN_INFO, &ivvi, + sizeof(ivvi)); + } + addattr_nest_end(&req->n, vfvlanlist); + } + } else if (matches(*argv, "rate") == 0) { + struct ifla_vf_tx_rate ivt; + + NEXT_ARG(); + if (get_unsigned(&ivt.rate, *argv, 0)) + invarg("Invalid \"rate\" value\n", *argv); + + ivt.vf = vf; + if (!new_rate_api) + addattr_l(&req->n, sizeof(*req), + IFLA_VF_TX_RATE, &ivt, sizeof(ivt)); + else if (!override_legacy_rate) + tivt.max_tx_rate = ivt.rate; + + } else if (matches(*argv, "max_tx_rate") == 0) { + NEXT_ARG(); + if (get_unsigned(&tivt.max_tx_rate, *argv, 0)) + invarg("Invalid \"max tx rate\" value\n", + *argv); + tivt.vf = vf; + + } else if (matches(*argv, "min_tx_rate") == 0) { + NEXT_ARG(); + if (get_unsigned(&tivt.min_tx_rate, *argv, 0)) + invarg("Invalid \"min tx rate\" value\n", + *argv); + tivt.vf = vf; + + } else if (matches(*argv, "spoofchk") == 0) { + struct ifla_vf_spoofchk ivs; + + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivs.setting = 1; + else if (matches(*argv, "off") == 0) + ivs.setting = 0; + else + return on_off("spoofchk", *argv); + ivs.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, + &ivs, sizeof(ivs)); + + } else if (matches(*argv, "query_rss") == 0) { + struct ifla_vf_rss_query_en ivs; + + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivs.setting = 1; + else if (matches(*argv, "off") == 0) + ivs.setting = 0; + else + return on_off("query_rss", *argv); + ivs.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, + &ivs, sizeof(ivs)); + + } else if (matches(*argv, "trust") == 0) { + struct ifla_vf_trust ivt; + + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivt.setting = 1; + else if (matches(*argv, "off") == 0) + ivt.setting = 0; + else + invarg("Invalid \"trust\" value\n", *argv); + ivt.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST, + &ivt, sizeof(ivt)); + + } else if (matches(*argv, "state") == 0) { + struct ifla_vf_link_state ivl; + + NEXT_ARG(); + if (matches(*argv, "auto") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_AUTO; + else if (matches(*argv, "enable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; + else if (matches(*argv, "disable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; + else + invarg("Invalid \"state\" value\n", *argv); + ivl.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, + &ivl, sizeof(ivl)); + } else if (matches(*argv, "node_guid") == 0) { + struct ifla_vf_guid ivg; + + NEXT_ARG(); + ivg.vf = vf; + if (get_guid(&ivg.guid, *argv)) { + invarg("Invalid GUID format\n", *argv); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID, + &ivg, sizeof(ivg)); + } else if (matches(*argv, "port_guid") == 0) { + struct ifla_vf_guid ivg; + + NEXT_ARG(); + ivg.vf = vf; + if (get_guid(&ivg.guid, *argv)) { + invarg("Invalid GUID format\n", *argv); + return -1; + } + addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID, + &ivg, sizeof(ivg)); + } else { + /* rewind arg */ + PREV_ARG(); + break; + } + } + + if (new_rate_api) { + int tmin, tmax; + + if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) { + ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev); + if (tivt.min_tx_rate == -1) + tivt.min_tx_rate = tmin; + if (tivt.max_tx_rate == -1) + tivt.max_tx_rate = tmax; + } + + if (tivt.max_tx_rate && tivt.min_tx_rate > tivt.max_tx_rate) { + fprintf(stderr, + "Invalid min_tx_rate %d - must be <= max_tx_rate %d\n", + tivt.min_tx_rate, tivt.max_tx_rate); + return -1; + } + + addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt, + sizeof(tivt)); + } + + if (argc == *argcp) + incomplete_command(); + + addattr_nest_end(&req->n, vfinfo); + + *argcp = argc; + *argvp = argv; + return 0; +} + +int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type) +{ + char *name = NULL; + char *dev = NULL; + char *link = NULL; + int ret, len; + char abuf[32]; + int qlen = -1; + int mtu = -1; + int netns = -1; + int vf = -1; + int numtxqueues = -1; + int numrxqueues = -1; + int link_netnsid = -1; + int index = 0; + int group = -1; + int addr_len = 0; + + ret = argc; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + req->i.ifi_change |= IFF_UP; + req->i.ifi_flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + req->i.ifi_change |= IFF_UP; + req->i.ifi_flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + if (name) + duparg("name", *argv); + if (check_ifname(*argv)) + invarg("\"name\" not a valid ifname", *argv); + name = *argv; + if (!dev) + dev = name; + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (index) + duparg("index", *argv); + index = atoi(*argv); + if (index <= 0) + invarg("Invalid \"index\" value", *argv); + } else if (matches(*argv, "link") == 0) { + NEXT_ARG(); + link = *argv; + } else if (matches(*argv, "address") == 0) { + NEXT_ARG(); + addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv); + if (addr_len < 0) + return -1; + addattr_l(&req->n, sizeof(*req), + IFLA_ADDRESS, abuf, addr_len); + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + NEXT_ARG(); + len = ll_addr_a2n(abuf, sizeof(abuf), *argv); + if (len < 0) + return -1; + addattr_l(&req->n, sizeof(*req), + IFLA_BROADCAST, abuf, len); + } else if (matches(*argv, "txqueuelen") == 0 || + strcmp(*argv, "qlen") == 0 || + matches(*argv, "txqlen") == 0) { + NEXT_ARG(); + if (qlen != -1) + duparg("txqueuelen", *argv); + if (get_integer(&qlen, *argv, 0)) + invarg("Invalid \"txqueuelen\" value\n", *argv); + addattr_l(&req->n, sizeof(*req), + IFLA_TXQLEN, &qlen, 4); + } else if (strcmp(*argv, "mtu") == 0) { + NEXT_ARG(); + if (mtu != -1) + duparg("mtu", *argv); + if (get_integer(&mtu, *argv, 0)) + invarg("Invalid \"mtu\" value\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4); + } else if (strcmp(*argv, "xdpgeneric") == 0 || + strcmp(*argv, "xdpdrv") == 0 || + strcmp(*argv, "xdpoffload") == 0 || + strcmp(*argv, "xdp") == 0) { + bool generic = strcmp(*argv, "xdpgeneric") == 0; + bool drv = strcmp(*argv, "xdpdrv") == 0; + bool offload = strcmp(*argv, "xdpoffload") == 0; + + NEXT_ARG(); + if (xdp_parse(&argc, &argv, req, dev, + generic, drv, offload)) + exit(-1); + + if (offload && name == dev) + dev = NULL; + } else if (strcmp(*argv, "netns") == 0) { + NEXT_ARG(); + if (netns != -1) + duparg("netns", *argv); + netns = netns_get_fd(*argv); + if (netns >= 0) + addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, + &netns, 4); + else if (get_integer(&netns, *argv, 0) == 0) + addattr_l(&req->n, sizeof(*req), + IFLA_NET_NS_PID, &netns, 4); + else + invarg("Invalid \"netns\" value\n", *argv); + } else if (strcmp(*argv, "multicast") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_MULTICAST; + + if (strcmp(*argv, "on") == 0) + req->i.ifi_flags |= IFF_MULTICAST; + else if (strcmp(*argv, "off") == 0) + req->i.ifi_flags &= ~IFF_MULTICAST; + else + return on_off("multicast", *argv); + } else if (strcmp(*argv, "allmulticast") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_ALLMULTI; + + if (strcmp(*argv, "on") == 0) + req->i.ifi_flags |= IFF_ALLMULTI; + else if (strcmp(*argv, "off") == 0) + req->i.ifi_flags &= ~IFF_ALLMULTI; + else + return on_off("allmulticast", *argv); + } else if (strcmp(*argv, "promisc") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_PROMISC; + + if (strcmp(*argv, "on") == 0) + req->i.ifi_flags |= IFF_PROMISC; + else if (strcmp(*argv, "off") == 0) + req->i.ifi_flags &= ~IFF_PROMISC; + else + return on_off("promisc", *argv); + } else if (strcmp(*argv, "trailers") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_NOTRAILERS; + + if (strcmp(*argv, "off") == 0) + req->i.ifi_flags |= IFF_NOTRAILERS; + else if (strcmp(*argv, "on") == 0) + req->i.ifi_flags &= ~IFF_NOTRAILERS; + else + return on_off("trailers", *argv); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_NOARP; + + if (strcmp(*argv, "on") == 0) + req->i.ifi_flags &= ~IFF_NOARP; + else if (strcmp(*argv, "off") == 0) + req->i.ifi_flags |= IFF_NOARP; + else + return on_off("arp", *argv); + } else if (strcmp(*argv, "carrier") == 0) { + int carrier; + + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + carrier = 1; + else if (strcmp(*argv, "off") == 0) + carrier = 0; + else + return on_off("carrier", *argv); + + addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier); + } else if (strcmp(*argv, "vf") == 0) { + struct rtattr *vflist; + + NEXT_ARG(); + if (get_integer(&vf, *argv, 0)) + invarg("Invalid \"vf\" value\n", *argv); + + vflist = addattr_nest(&req->n, sizeof(*req), + IFLA_VFINFO_LIST); + if (!dev) + missarg("dev"); + + len = iplink_parse_vf(vf, &argc, &argv, req, dev); + if (len < 0) + return -1; + addattr_nest_end(&req->n, vflist); + + if (name == dev) + dev = NULL; + } else if (matches(*argv, "master") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, 4); + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, sizeof(ifindex)); + } else if (matches(*argv, "nomaster") == 0) { + int ifindex = 0; + + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, 4); + } else if (matches(*argv, "dynamic") == 0) { + NEXT_ARG(); + req->i.ifi_change |= IFF_DYNAMIC; + + if (strcmp(*argv, "on") == 0) + req->i.ifi_flags |= IFF_DYNAMIC; + else if (strcmp(*argv, "off") == 0) + req->i.ifi_flags &= ~IFF_DYNAMIC; + else + return on_off("dynamic", *argv); + } else if (matches(*argv, "type") == 0) { + NEXT_ARG(); + *type = *argv; + argc--; argv++; + break; + } else if (matches(*argv, "alias") == 0) { + NEXT_ARG(); + len = strlen(*argv); + if (len >= IFALIASZ) + invarg("alias too long\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS, + *argv, len); + } else if (strcmp(*argv, "group") == 0) { + NEXT_ARG(); + if (group != -1) + duparg("group", *argv); + if (rtnl_group_a2n(&group, *argv)) + invarg("Invalid \"group\" value\n", *argv); + addattr32(&req->n, sizeof(*req), IFLA_GROUP, group); + } else if (strcmp(*argv, "mode") == 0) { + int mode; + + NEXT_ARG(); + mode = get_link_mode(*argv); + if (mode < 0) + invarg("Invalid link mode\n", *argv); + addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode); + } else if (strcmp(*argv, "state") == 0) { + int state; + + NEXT_ARG(); + state = get_operstate(*argv); + if (state < 0) + invarg("Invalid operstate\n", *argv); + + addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state); + } else if (matches(*argv, "numtxqueues") == 0) { + NEXT_ARG(); + if (numtxqueues != -1) + duparg("numtxqueues", *argv); + if (get_integer(&numtxqueues, *argv, 0)) + invarg("Invalid \"numtxqueues\" value\n", + *argv); + addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES, + &numtxqueues, 4); + } else if (matches(*argv, "numrxqueues") == 0) { + NEXT_ARG(); + if (numrxqueues != -1) + duparg("numrxqueues", *argv); + if (get_integer(&numrxqueues, *argv, 0)) + invarg("Invalid \"numrxqueues\" value\n", + *argv); + addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, + &numrxqueues, 4); + } else if (matches(*argv, "addrgenmode") == 0) { + struct rtattr *afs, *afs6; + int mode; + + NEXT_ARG(); + mode = get_addr_gen_mode(*argv); + if (mode < 0) + invarg("Invalid address generation mode\n", + *argv); + afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC); + afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6); + addattr8(&req->n, sizeof(*req), + IFLA_INET6_ADDR_GEN_MODE, mode); + addattr_nest_end(&req->n, afs6); + addattr_nest_end(&req->n, afs); + } else if (matches(*argv, "link-netns") == 0) { + NEXT_ARG(); + if (link_netnsid != -1) + duparg("link-netns/link-netnsid", *argv); + link_netnsid = get_netnsid_from_name(*argv); + /* No nsid? Try to assign one. */ + if (link_netnsid < 0) + set_netnsid_from_name(*argv, -1); + link_netnsid = get_netnsid_from_name(*argv); + if (link_netnsid < 0) + invarg("Invalid \"link-netns\" value\n", + *argv); + addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, + link_netnsid); + } else if (matches(*argv, "link-netnsid") == 0) { + NEXT_ARG(); + if (link_netnsid != -1) + duparg("link-netns/link-netnsid", *argv); + if (get_integer(&link_netnsid, *argv, 0)) + invarg("Invalid \"link-netnsid\" value\n", + *argv); + addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, + link_netnsid); + } else if (strcmp(*argv, "protodown") == 0) { + unsigned int proto_down; + + NEXT_ARG(); + if (strcmp(*argv, "on") == 0) + proto_down = 1; + else if (strcmp(*argv, "off") == 0) + proto_down = 0; + else + return on_off("protodown", *argv); + addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN, + proto_down); + } else if (strcmp(*argv, "gso_max_size") == 0) { + unsigned int max_size; + + NEXT_ARG(); + if (get_unsigned(&max_size, *argv, 0) || + max_size > GSO_MAX_SIZE) + invarg("Invalid \"gso_max_size\" value\n", + *argv); + addattr32(&req->n, sizeof(*req), + IFLA_GSO_MAX_SIZE, max_size); + } else if (strcmp(*argv, "gso_max_segs") == 0) { + unsigned int max_segs; + + NEXT_ARG(); + if (get_unsigned(&max_segs, *argv, 0) || + max_segs > GSO_MAX_SEGS) + invarg("Invalid \"gso_max_segs\" value\n", + *argv); + addattr32(&req->n, sizeof(*req), + IFLA_GSO_MAX_SEGS, max_segs); + } else { + if (matches(*argv, "help") == 0) + usage(); + + if (strcmp(*argv, "dev") == 0) + NEXT_ARG(); + if (dev != name) + duparg2("dev", *argv); + if (check_altifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + dev = *argv; + } + argc--; argv++; + } + + ret -= argc; + + /* Allow "ip link add dev" and "ip link add name" */ + if (!name) + name = dev; + else if (!dev) + dev = name; + else if (!strcmp(name, dev)) + name = dev; + + if (dev && addr_len && + !(req->n.nlmsg_flags & NLM_F_CREATE)) { + int halen = nl_get_ll_addr_len(dev); + + if (halen >= 0 && halen != addr_len) { + fprintf(stderr, + "Invalid address length %d - must be %d bytes\n", + addr_len, halen); + return -1; + } + } + + if (!(req->n.nlmsg_flags & NLM_F_CREATE) && index) { + fprintf(stderr, + "index can be used only when creating devices.\n"); + exit(-1); + } + + if (group != -1) { + if (!dev) { + if (argc) { + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", + *argv); + exit(-1); + } + if (req->n.nlmsg_flags & NLM_F_CREATE) { + fprintf(stderr, + "group cannot be used when creating devices.\n"); + exit(-1); + } + + *type = NULL; + return ret; + } + } + + if (!(req->n.nlmsg_flags & NLM_F_CREATE)) { + if (!dev) { + fprintf(stderr, + "Not enough information: \"dev\" argument is required.\n"); + exit(-1); + } + + req->i.ifi_index = ll_name_to_index(dev); + if (!req->i.ifi_index) + return nodev(dev); + + /* Not renaming to the same name */ + if (name == dev) + name = NULL; + } else { + if (name != dev) { + fprintf(stderr, + "both \"name\" and \"dev\" cannot be used when creating devices.\n"); + exit(-1); + } + + if (link) { + int ifindex; + + ifindex = ll_name_to_index(link); + if (!ifindex) + return nodev(link); + addattr32(&req->n, sizeof(*req), IFLA_LINK, ifindex); + } + + req->i.ifi_index = index; + } + + if (name) { + addattr_l(&req->n, sizeof(*req), + IFLA_IFNAME, name, strlen(name) + 1); + } + + return ret; +} + +static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + char *type = NULL; + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .i.ifi_family = preferred_family, + }; + int ret; + + ret = iplink_parse(argc, argv, &req, &type); + if (ret < 0) + return ret; + + if (type) { + struct link_util *lu; + struct rtattr *linkinfo; + char *ulinep = strchr(type, '_'); + int iflatype; + + linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, + strlen(type)); + + lu = get_link_kind(type); + if (ulinep && !strcmp(ulinep, "_slave")) + iflatype = IFLA_INFO_SLAVE_DATA; + else + iflatype = IFLA_INFO_DATA; + + argc -= ret; + argv += ret; + + if (lu && argc) { + struct rtattr *data; + + data = addattr_nest(&req.n, sizeof(req), iflatype); + + if (lu->parse_opt && + lu->parse_opt(lu, argc, argv, &req.n)) + return -1; + + addattr_nest_end(&req.n, data); + } else if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, + "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", + *argv); + return -1; + } + addattr_nest_end(&req.n, linkinfo); + } else if (flags & NLM_F_CREATE) { + fprintf(stderr, + "Not enough information: \"type\" argument is required\n"); + return -1; + } + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + /* remove device from cache; next use can refresh with new data */ + ll_drop_by_index(req.i.ifi_index); + + return 0; +} + +int iplink_get(char *name, __u32 filt_mask) +{ + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + }; + struct nlmsghdr *answer; + + if (name) { + addattr_l(&req.n, sizeof(req), + !check_ifname(name) ? IFLA_IFNAME : IFLA_ALT_IFNAME, + name, strlen(name) + 1); + } + addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + open_json_object(NULL); + print_linkinfo(answer, stdout); + close_json_object(); + + free(answer); + return 0; +} + +#if IPLINK_IOCTL_COMPAT +static int get_ctl_fd(void) +{ + int s_errno; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + s_errno = errno; + fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + fd = socket(PF_INET6, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + errno = s_errno; + perror("Cannot create control socket"); + return -1; +} + +static int do_chflags(const char *dev, __u32 flags, __u32 mask) +{ + struct ifreq ifr; + int fd; + int err; + + strlcpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCGIFFLAGS, &ifr); + if (err) { + perror("SIOCGIFFLAGS"); + close(fd); + return -1; + } + if ((ifr.ifr_flags^flags)&mask) { + ifr.ifr_flags &= ~mask; + ifr.ifr_flags |= mask&flags; + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) + perror("SIOCSIFFLAGS"); + } + close(fd); + return err; +} + +static int do_changename(const char *dev, const char *newdev) +{ + struct ifreq ifr; + int fd; + int err; + + strlcpy(ifr.ifr_name, dev, IFNAMSIZ); + strlcpy(ifr.ifr_newname, newdev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCSIFNAME, &ifr); + if (err) { + perror("SIOCSIFNAME"); + close(fd); + return -1; + } + close(fd); + return err; +} + +static int set_qlen(const char *dev, int qlen) +{ + struct ifreq ifr = { .ifr_qlen = qlen }; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + strlcpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { + perror("SIOCSIFXQLEN"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int set_mtu(const char *dev, int mtu) +{ + struct ifreq ifr = { .ifr_mtu = mtu }; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + strlcpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { + perror("SIOCSIFMTU"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int get_address(const char *dev, int *htype) +{ + struct ifreq ifr = {}; + struct sockaddr_ll me = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_LOOP), + }; + socklen_t alen; + int s; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_PACKET)"); + return -1; + } + + strlcpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + close(s); + return -1; + } + + me.sll_ifindex = ifr.ifr_ifindex; + if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) { + perror("bind"); + close(s); + return -1; + } + + alen = sizeof(me); + if (getsockname(s, (struct sockaddr *)&me, &alen) == -1) { + perror("getsockname"); + close(s); + return -1; + } + close(s); + *htype = me.sll_hatype; + return me.sll_halen; +} + +static int parse_address(const char *dev, int hatype, int halen, + char *lla, struct ifreq *ifr) +{ + int alen; + + memset(ifr, 0, sizeof(*ifr)); + strlcpy(ifr->ifr_name, dev, IFNAMSIZ); + ifr->ifr_hwaddr.sa_family = hatype; + alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); + if (alen < 0) + return -1; + if (alen != halen) { + fprintf(stderr, + "Wrong address (%s) length: expected %d bytes\n", + lla, halen); + return -1; + } + return 0; +} + +static int set_address(struct ifreq *ifr, int brd) +{ + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { + perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); + close(s); + return -1; + } + close(s); + return 0; +} + +static int do_set(int argc, char **argv) +{ + char *dev = NULL; + __u32 mask = 0; + __u32 flags = 0; + int qlen = -1; + int mtu = -1; + char *newaddr = NULL; + char *newbrd = NULL; + struct ifreq ifr0, ifr1; + char *newname = NULL; + int htype, halen; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + mask |= IFF_UP; + flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + mask |= IFF_UP; + flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"name\" not a valid ifname", *argv); + newname = *argv; + } else if (matches(*argv, "address") == 0) { + NEXT_ARG(); + newaddr = *argv; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + NEXT_ARG(); + newbrd = *argv; + } else if (matches(*argv, "txqueuelen") == 0 || + strcmp(*argv, "qlen") == 0 || + matches(*argv, "txqlen") == 0) { + NEXT_ARG(); + if (qlen != -1) + duparg("txqueuelen", *argv); + if (get_integer(&qlen, *argv, 0)) + invarg("Invalid \"txqueuelen\" value\n", *argv); + } else if (strcmp(*argv, "mtu") == 0) { + NEXT_ARG(); + if (mtu != -1) + duparg("mtu", *argv); + if (get_integer(&mtu, *argv, 0)) + invarg("Invalid \"mtu\" value\n", *argv); + } else if (strcmp(*argv, "multicast") == 0) { + NEXT_ARG(); + mask |= IFF_MULTICAST; + + if (strcmp(*argv, "on") == 0) + flags |= IFF_MULTICAST; + else if (strcmp(*argv, "off") == 0) + flags &= ~IFF_MULTICAST; + else + return on_off("multicast", *argv); + } else if (strcmp(*argv, "allmulticast") == 0) { + NEXT_ARG(); + mask |= IFF_ALLMULTI; + + if (strcmp(*argv, "on") == 0) + flags |= IFF_ALLMULTI; + else if (strcmp(*argv, "off") == 0) + flags &= ~IFF_ALLMULTI; + else + return on_off("allmulticast", *argv); + } else if (strcmp(*argv, "promisc") == 0) { + NEXT_ARG(); + mask |= IFF_PROMISC; + + if (strcmp(*argv, "on") == 0) + flags |= IFF_PROMISC; + else if (strcmp(*argv, "off") == 0) + flags &= ~IFF_PROMISC; + else + return on_off("promisc", *argv); + } else if (strcmp(*argv, "trailers") == 0) { + NEXT_ARG(); + mask |= IFF_NOTRAILERS; + + if (strcmp(*argv, "off") == 0) + flags |= IFF_NOTRAILERS; + else if (strcmp(*argv, "on") == 0) + flags &= ~IFF_NOTRAILERS; + else + return on_off("trailers", *argv); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + mask |= IFF_NOARP; + + if (strcmp(*argv, "on") == 0) + flags &= ~IFF_NOARP; + else if (strcmp(*argv, "off") == 0) + flags |= IFF_NOARP; + else + return on_off("arp", *argv); + } else if (matches(*argv, "dynamic") == 0) { + NEXT_ARG(); + mask |= IFF_DYNAMIC; + + if (strcmp(*argv, "on") == 0) + flags |= IFF_DYNAMIC; + else if (strcmp(*argv, "off") == 0) + flags &= ~IFF_DYNAMIC; + else + return on_off("dynamic", *argv); + } else { + if (strcmp(*argv, "dev") == 0) + NEXT_ARG(); + else if (matches(*argv, "help") == 0) + usage(); + + if (dev) + duparg2("dev", *argv); + if (check_ifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + dev = *argv; + } + argc--; argv++; + } + + if (!dev) { + fprintf(stderr, + "Not enough of information: \"dev\" argument is required.\n"); + exit(-1); + } + + if (newaddr || newbrd) { + halen = get_address(dev, &htype); + if (halen < 0) + return -1; + if (newaddr) { + if (parse_address(dev, htype, halen, + newaddr, &ifr0) < 0) + return -1; + } + if (newbrd) { + if (parse_address(dev, htype, halen, + newbrd, &ifr1) < 0) + return -1; + } + } + + if (newname && strcmp(dev, newname)) { + if (do_changename(dev, newname) < 0) + return -1; + dev = newname; + } + if (qlen != -1) { + if (set_qlen(dev, qlen) < 0) + return -1; + } + if (mtu != -1) { + if (set_mtu(dev, mtu) < 0) + return -1; + } + if (newaddr || newbrd) { + if (newbrd) { + if (set_address(&ifr1, 1) < 0) + return -1; + } + if (newaddr) { + if (set_address(&ifr0, 0) < 0) + return -1; + } + } + if (mask) + return do_chflags(dev, flags, mask); + return 0; +} +#endif /* IPLINK_IOCTL_COMPAT */ + +static void print_mpls_stats(FILE *fp, struct rtattr *attr) +{ + struct rtattr *mrtb[MPLS_STATS_MAX+1]; + struct mpls_link_stats *stats; + + parse_rtattr(mrtb, MPLS_STATS_MAX, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + if (!mrtb[MPLS_STATS_LINK]) + return; + + stats = RTA_DATA(mrtb[MPLS_STATS_LINK]); + + fprintf(fp, " mpls:\n"); + fprintf(fp, " RX: bytes packets errors dropped noroute\n"); + fprintf(fp, " "); + print_num(fp, 10, stats->rx_bytes); + print_num(fp, 8, stats->rx_packets); + print_num(fp, 7, stats->rx_errors); + print_num(fp, 8, stats->rx_dropped); + print_num(fp, 7, stats->rx_noroute); + fprintf(fp, "\n"); + fprintf(fp, " TX: bytes packets errors dropped\n"); + fprintf(fp, " "); + print_num(fp, 10, stats->tx_bytes); + print_num(fp, 8, stats->tx_packets); + print_num(fp, 7, stats->tx_errors); + print_num(fp, 7, stats->tx_dropped); + fprintf(fp, "\n"); +} + +static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr) +{ + bool if_printed = false; + struct rtattr *i; + int rem; + + rem = RTA_PAYLOAD(attr); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + if (preferred_family != AF_UNSPEC && + i->rta_type != preferred_family) + continue; + + if (!if_printed) { + fprintf(fp, "%u: %s\n", ifindex, + ll_index_to_name(ifindex)); + if_printed = true; + } + + switch (i->rta_type) { + case AF_MPLS: + print_mpls_stats(fp, i); + break; + default: + fprintf(fp, " unknown af(%d)\n", i->rta_type); + break; + } + } +} + +struct af_stats_ctx { + FILE *fp; + int ifindex; +}; + +static int print_af_stats(struct nlmsghdr *n, void *arg) +{ + struct if_stats_msg *ifsm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_STATS_MAX+1]; + int len = n->nlmsg_len; + struct af_stats_ctx *ctx = arg; + FILE *fp = ctx->fp; + + len -= NLMSG_LENGTH(sizeof(*ifsm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (ctx->ifindex && ifsm->ifindex != ctx->ifindex) + return 0; + + parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); + + if (tb[IFLA_STATS_AF_SPEC]) + print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]); + + fflush(fp); + return 0; +} + +static int iplink_afstats(int argc, char **argv) +{ + __u32 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_AF_SPEC); + const char *filter_dev = NULL; + struct af_stats_ctx ctx = { + .fp = stdout, + .ifindex = 0, + }; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg2("dev", *argv); + filter_dev = *argv; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip link help\".\n", + *argv); + exit(-1); + } + + argv++; argc--; + } + + if (filter_dev) { + ctx.ifindex = ll_name_to_index(filter_dev); + if (ctx.ifindex <= 0) { + fprintf(stderr, + "Device \"%s\" does not exist.\n", + filter_dev); + return -1; + } + } + + if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { + perror("Cannont send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_af_stats, &ctx) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +static int iplink_prop_mod(int argc, char **argv, struct iplink_req *req) +{ + struct rtattr *proplist; + char *dev = NULL; + char *name; + + proplist = addattr_nest(&req->n, sizeof(*req), + IFLA_PROP_LIST | NLA_F_NESTED); + + while (argc > 0) { + if (matches(*argv, "altname") == 0) { + NEXT_ARG(); + if (check_altifname(*argv)) + invarg("not a valid altname", *argv); + name = *argv; + addattr_l(&req->n, sizeof(*req), IFLA_ALT_IFNAME, + name, strlen(name) + 1); + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + if (strcmp(*argv, "dev") == 0) + NEXT_ARG(); + if (dev) + duparg2("dev", *argv); + if (check_altifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + dev = *argv; + } + argv++; argc--; + } + addattr_nest_end(&req->n, proplist); + + if (!dev) { + fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); + exit(-1); + } + + req->i.ifi_index = ll_name_to_index(dev); + if (!req->i.ifi_index) + return nodev(dev); + + if (rtnl_talk(&rth, &req->n, NULL) < 0) + return -2; + + return 0; +} + +static int iplink_prop(int argc, char **argv) +{ + struct iplink_req req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .i.ifi_family = preferred_family, + }; + + if (argc <= 0) { + usage(); + exit(-1); + } + + if (matches(*argv, "add") == 0) { + req.n.nlmsg_flags |= NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND; + req.n.nlmsg_type = RTM_NEWLINKPROP; + } else if (matches(*argv, "del") == 0) { + req.n.nlmsg_type = RTM_DELLINKPROP; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Operator required\n"); + exit(-1); + } + return iplink_prop_mod(argc - 1, argv + 1, &req); +} + +static void do_help(int argc, char **argv) +{ + struct link_util *lu = NULL; + + if (argc <= 0) { + usage(); + return; + } + + lu = get_link_kind(*argv); + if (lu && lu->print_help) + lu->print_help(lu, argc-1, argv+1, stdout); + else + usage(); +} + +int do_iplink(int argc, char **argv) +{ + if (argc < 1) + return ipaddr_list_link(0, NULL); + + if (iplink_have_newlink()) { + if (matches(*argv, "add") == 0) + return iplink_modify(RTM_NEWLINK, + NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "set") == 0 || + matches(*argv, "change") == 0) + return iplink_modify(RTM_NEWLINK, 0, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iplink_modify(RTM_NEWLINK, + NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iplink_modify(RTM_DELLINK, 0, + argc-1, argv+1); + } else { +#if IPLINK_IOCTL_COMPAT + if (matches(*argv, "set") == 0) + return do_set(argc-1, argv+1); +#endif + } + + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipaddr_list_link(argc-1, argv+1); + + if (matches(*argv, "xstats") == 0) + return iplink_ifla_xstats(argc-1, argv+1); + + if (matches(*argv, "afstats") == 0) { + iplink_afstats(argc-1, argv+1); + return 0; + } + + if (matches(*argv, "property") == 0) + return iplink_prop(argc-1, argv+1); + + if (matches(*argv, "help") == 0) { + do_help(argc-1, argv+1); + return 0; + } + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", + *argv); + exit(-1); +} diff --git a/ip/iplink_bareudp.c b/ip/iplink_bareudp.c new file mode 100644 index 0000000..860ec69 --- /dev/null +++ b/ip/iplink_bareudp.c @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include +#include + +#include "libnetlink.h" +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +#define BAREUDP_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0) + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... bareudp dstport PORT\n" + " ethertype PROTO\n" + " [ srcportmin PORT ]\n" + " [ [no]multiproto ]\n" + "\n" + "Where: PORT := 0-65535\n" + " PROTO := NUMBER | ip | mpls\n" + " SRCPORTMIN := 0-65535\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static void check_duparg(__u64 *attrs, int type, const char *key, + const char *argv) +{ + if (!BAREUDP_ATTRSET(*attrs, type)) { + *attrs |= (1L << type); + return; + } + duparg2(key, argv); +} + +static int bareudp_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + bool multiproto = false; + __u16 srcportmin = 0; + __be16 ethertype = 0; + __be16 dstport = 0; + __u64 attrs = 0; + + while (argc > 0) { + if (matches(*argv, "dstport") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_PORT, "dstport", + *argv); + if (get_be16(&dstport, *argv, 0)) + invarg("dstport", *argv); + } else if (matches(*argv, "ethertype") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_ETHERTYPE, + "ethertype", *argv); + if (ll_proto_a2n(ðertype, *argv)) + invarg("ethertype", *argv); + } else if (matches(*argv, "srcportmin") == 0) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_BAREUDP_SRCPORT_MIN, + "srcportmin", *argv); + if (get_u16(&srcportmin, *argv, 0)) + invarg("srcportmin", *argv); + } else if (matches(*argv, "multiproto") == 0) { + check_duparg(&attrs, IFLA_BAREUDP_MULTIPROTO_MODE, + *argv, *argv); + multiproto = true; + } else if (matches(*argv, "nomultiproto") == 0) { + check_duparg(&attrs, IFLA_BAREUDP_MULTIPROTO_MODE, + *argv, *argv); + multiproto = false; + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "bareudp: unknown command \"%s\"?\n", + *argv); + explain(); + return -1; + } + argc--, argv++; + } + + if (!BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_PORT)) + missarg("dstport"); + if (!BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_ETHERTYPE)) + missarg("ethertype"); + + addattr16(n, 1024, IFLA_BAREUDP_PORT, dstport); + addattr16(n, 1024, IFLA_BAREUDP_ETHERTYPE, ethertype); + if (BAREUDP_ATTRSET(attrs, IFLA_BAREUDP_SRCPORT_MIN)) + addattr16(n, 1024, IFLA_BAREUDP_SRCPORT_MIN, srcportmin); + if (multiproto) + addattr(n, 1024, IFLA_BAREUDP_MULTIPROTO_MODE); + + return 0; +} + +static void bareudp_print_opt(struct link_util *lu, FILE *f, + struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_BAREUDP_PORT]) + print_uint(PRINT_ANY, "dstport", "dstport %u ", + rta_getattr_be16(tb[IFLA_BAREUDP_PORT])); + + if (tb[IFLA_BAREUDP_ETHERTYPE]) { + struct rtattr *attr = tb[IFLA_BAREUDP_ETHERTYPE]; + SPRINT_BUF(ethertype); + + print_string(PRINT_ANY, "ethertype", "ethertype %s ", + ll_proto_n2a(rta_getattr_u16(attr), + ethertype, sizeof(ethertype))); + } + + if (tb[IFLA_BAREUDP_SRCPORT_MIN]) + print_uint(PRINT_ANY, "srcportmin", "srcportmin %u ", + rta_getattr_u16(tb[IFLA_BAREUDP_SRCPORT_MIN])); + + if (tb[IFLA_BAREUDP_MULTIPROTO_MODE]) + print_bool(PRINT_ANY, "multiproto", "multiproto ", true); + else + print_bool(PRINT_ANY, "multiproto", "nomultiproto ", false); +} + +static void bareudp_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util bareudp_link_util = { + .id = "bareudp", + .maxattr = IFLA_BAREUDP_MAX, + .parse_opt = bareudp_parse_opt, + .print_opt = bareudp_print_opt, + .print_help = bareudp_print_help, +}; diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c new file mode 100644 index 0000000..585b6be --- /dev/null +++ b/ip/iplink_bond.c @@ -0,0 +1,831 @@ +/* + * iplink_bond.c Bonding device support + * + * 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. + * + * Authors: Jiri Pirko + * Scott Feldman + */ + +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +#define BOND_MAX_ARP_TARGETS 16 + +static unsigned int xstats_print_attr; +static int filter_index; + +static const char *mode_tbl[] = { + "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", + NULL, +}; + +static const char *arp_validate_tbl[] = { + "none", + "active", + "backup", + "all", + NULL, +}; + +static const char *arp_all_targets_tbl[] = { + "any", + "all", + NULL, +}; + +static const char *primary_reselect_tbl[] = { + "always", + "better", + "failure", + NULL, +}; + +static const char *fail_over_mac_tbl[] = { + "none", + "active", + "follow", + NULL, +}; + +static const char *xmit_hash_policy_tbl[] = { + "layer2", + "layer3+4", + "layer2+3", + "encap2+3", + "encap3+4", + NULL, +}; + +static const char *lacp_rate_tbl[] = { + "slow", + "fast", + NULL, +}; + +static const char *ad_select_tbl[] = { + "stable", + "bandwidth", + "count", + NULL, +}; + +static const char *get_name(const char **tbl, int index) +{ + int i; + + for (i = 0; tbl[i]; i++) + if (i == index) + return tbl[i]; + + return "UNKNOWN"; +} + +static int get_index(const char **tbl, char *name) +{ + int i, index; + + /* check for integer index passed in instead of name */ + if (get_integer(&index, name, 10) == 0) + for (i = 0; tbl[i]; i++) + if (i == index) + return i; + + for (i = 0; tbl[i]; i++) + if (strcmp(tbl[i], name) == 0) + return i; + + return -1; +} + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... bond [ mode BONDMODE ] [ active_slave SLAVE_DEV ]\n" + " [ clear_active_slave ] [ miimon MIIMON ]\n" + " [ updelay UPDELAY ] [ downdelay DOWNDELAY ]\n" + " [ peer_notify_delay DELAY ]\n" + " [ use_carrier USE_CARRIER ]\n" + " [ arp_interval ARP_INTERVAL ]\n" + " [ arp_validate ARP_VALIDATE ]\n" + " [ arp_all_targets ARP_ALL_TARGETS ]\n" + " [ arp_ip_target [ ARP_IP_TARGET, ... ] ]\n" + " [ primary SLAVE_DEV ]\n" + " [ primary_reselect PRIMARY_RESELECT ]\n" + " [ fail_over_mac FAIL_OVER_MAC ]\n" + " [ xmit_hash_policy XMIT_HASH_POLICY ]\n" + " [ resend_igmp RESEND_IGMP ]\n" + " [ num_grat_arp|num_unsol_na NUM_GRAT_ARP|NUM_UNSOL_NA ]\n" + " [ all_slaves_active ALL_SLAVES_ACTIVE ]\n" + " [ min_links MIN_LINKS ]\n" + " [ lp_interval LP_INTERVAL ]\n" + " [ packets_per_slave PACKETS_PER_SLAVE ]\n" + " [ tlb_dynamic_lb TLB_DYNAMIC_LB ]\n" + " [ lacp_rate LACP_RATE ]\n" + " [ ad_select AD_SELECT ]\n" + " [ ad_user_port_key PORTKEY ]\n" + " [ ad_actor_sys_prio SYSPRIO ]\n" + " [ ad_actor_system LLADDR ]\n" + "\n" + "BONDMODE := balance-rr|active-backup|balance-xor|broadcast|802.3ad|balance-tlb|balance-alb\n" + "ARP_VALIDATE := none|active|backup|all\n" + "ARP_ALL_TARGETS := any|all\n" + "PRIMARY_RESELECT := always|better|failure\n" + "FAIL_OVER_MAC := none|active|follow\n" + "XMIT_HASH_POLICY := layer2|layer2+3|layer3+4|encap2+3|encap3+4\n" + "LACP_RATE := slow|fast\n" + "AD_SELECT := stable|bandwidth|count\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static int bond_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u8 mode, use_carrier, primary_reselect, fail_over_mac; + __u8 xmit_hash_policy, num_peer_notif, all_slaves_active; + __u8 lacp_rate, ad_select, tlb_dynamic_lb; + __u16 ad_user_port_key, ad_actor_sys_prio; + __u32 miimon, updelay, downdelay, peer_notify_delay, arp_interval, arp_validate; + __u32 arp_all_targets, resend_igmp, min_links, lp_interval; + __u32 packets_per_slave; + unsigned int ifindex; + + while (argc > 0) { + if (matches(*argv, "mode") == 0) { + NEXT_ARG(); + if (get_index(mode_tbl, *argv) < 0) + invarg("invalid mode", *argv); + mode = get_index(mode_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_MODE, mode); + } else if (matches(*argv, "active_slave") == 0) { + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + return nodev(*argv); + addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, ifindex); + } else if (matches(*argv, "clear_active_slave") == 0) { + addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, 0); + } else if (matches(*argv, "miimon") == 0) { + NEXT_ARG(); + if (get_u32(&miimon, *argv, 0)) + invarg("invalid miimon", *argv); + addattr32(n, 1024, IFLA_BOND_MIIMON, miimon); + } else if (matches(*argv, "updelay") == 0) { + NEXT_ARG(); + if (get_u32(&updelay, *argv, 0)) + invarg("invalid updelay", *argv); + addattr32(n, 1024, IFLA_BOND_UPDELAY, updelay); + } else if (matches(*argv, "downdelay") == 0) { + NEXT_ARG(); + if (get_u32(&downdelay, *argv, 0)) + invarg("invalid downdelay", *argv); + addattr32(n, 1024, IFLA_BOND_DOWNDELAY, downdelay); + } else if (matches(*argv, "peer_notify_delay") == 0) { + NEXT_ARG(); + if (get_u32(&peer_notify_delay, *argv, 0)) + invarg("invalid peer_notify_delay", *argv); + addattr32(n, 1024, IFLA_BOND_PEER_NOTIF_DELAY, peer_notify_delay); + } else if (matches(*argv, "use_carrier") == 0) { + NEXT_ARG(); + if (get_u8(&use_carrier, *argv, 0)) + invarg("invalid use_carrier", *argv); + addattr8(n, 1024, IFLA_BOND_USE_CARRIER, use_carrier); + } else if (matches(*argv, "arp_interval") == 0) { + NEXT_ARG(); + if (get_u32(&arp_interval, *argv, 0)) + invarg("invalid arp_interval", *argv); + addattr32(n, 1024, IFLA_BOND_ARP_INTERVAL, arp_interval); + } else if (matches(*argv, "arp_ip_target") == 0) { + struct rtattr *nest = addattr_nest(n, 1024, + IFLA_BOND_ARP_IP_TARGET); + if (NEXT_ARG_OK()) { + NEXT_ARG(); + char *targets = strdupa(*argv); + char *target = strtok(targets, ","); + int i; + + for (i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) { + __u32 addr = get_addr32(target); + + addattr32(n, 1024, i, addr); + target = strtok(NULL, ","); + } + addattr_nest_end(n, nest); + } + addattr_nest_end(n, nest); + } else if (matches(*argv, "arp_validate") == 0) { + NEXT_ARG(); + if (get_index(arp_validate_tbl, *argv) < 0) + invarg("invalid arp_validate", *argv); + arp_validate = get_index(arp_validate_tbl, *argv); + addattr32(n, 1024, IFLA_BOND_ARP_VALIDATE, arp_validate); + } else if (matches(*argv, "arp_all_targets") == 0) { + NEXT_ARG(); + if (get_index(arp_all_targets_tbl, *argv) < 0) + invarg("invalid arp_all_targets", *argv); + arp_all_targets = get_index(arp_all_targets_tbl, *argv); + addattr32(n, 1024, IFLA_BOND_ARP_ALL_TARGETS, arp_all_targets); + } else if (matches(*argv, "primary") == 0) { + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + return nodev(*argv); + addattr32(n, 1024, IFLA_BOND_PRIMARY, ifindex); + } else if (matches(*argv, "primary_reselect") == 0) { + NEXT_ARG(); + if (get_index(primary_reselect_tbl, *argv) < 0) + invarg("invalid primary_reselect", *argv); + primary_reselect = get_index(primary_reselect_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_PRIMARY_RESELECT, + primary_reselect); + } else if (matches(*argv, "fail_over_mac") == 0) { + NEXT_ARG(); + if (get_index(fail_over_mac_tbl, *argv) < 0) + invarg("invalid fail_over_mac", *argv); + fail_over_mac = get_index(fail_over_mac_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_FAIL_OVER_MAC, + fail_over_mac); + } else if (matches(*argv, "xmit_hash_policy") == 0) { + NEXT_ARG(); + if (get_index(xmit_hash_policy_tbl, *argv) < 0) + invarg("invalid xmit_hash_policy", *argv); + + xmit_hash_policy = get_index(xmit_hash_policy_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_XMIT_HASH_POLICY, + xmit_hash_policy); + } else if (matches(*argv, "resend_igmp") == 0) { + NEXT_ARG(); + if (get_u32(&resend_igmp, *argv, 0)) + invarg("invalid resend_igmp", *argv); + + addattr32(n, 1024, IFLA_BOND_RESEND_IGMP, resend_igmp); + } else if (matches(*argv, "num_grat_arp") == 0 || + matches(*argv, "num_unsol_na") == 0) { + NEXT_ARG(); + if (get_u8(&num_peer_notif, *argv, 0)) + invarg("invalid num_grat_arp|num_unsol_na", + *argv); + + addattr8(n, 1024, IFLA_BOND_NUM_PEER_NOTIF, + num_peer_notif); + } else if (matches(*argv, "all_slaves_active") == 0) { + NEXT_ARG(); + if (get_u8(&all_slaves_active, *argv, 0)) + invarg("invalid all_slaves_active", *argv); + + addattr8(n, 1024, IFLA_BOND_ALL_SLAVES_ACTIVE, + all_slaves_active); + } else if (matches(*argv, "min_links") == 0) { + NEXT_ARG(); + if (get_u32(&min_links, *argv, 0)) + invarg("invalid min_links", *argv); + + addattr32(n, 1024, IFLA_BOND_MIN_LINKS, min_links); + } else if (matches(*argv, "lp_interval") == 0) { + NEXT_ARG(); + if (get_u32(&lp_interval, *argv, 0)) + invarg("invalid lp_interval", *argv); + + addattr32(n, 1024, IFLA_BOND_LP_INTERVAL, lp_interval); + } else if (matches(*argv, "packets_per_slave") == 0) { + NEXT_ARG(); + if (get_u32(&packets_per_slave, *argv, 0)) + invarg("invalid packets_per_slave", *argv); + + addattr32(n, 1024, IFLA_BOND_PACKETS_PER_SLAVE, + packets_per_slave); + } else if (matches(*argv, "lacp_rate") == 0) { + NEXT_ARG(); + if (get_index(lacp_rate_tbl, *argv) < 0) + invarg("invalid lacp_rate", *argv); + + lacp_rate = get_index(lacp_rate_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_AD_LACP_RATE, lacp_rate); + } else if (matches(*argv, "ad_select") == 0) { + NEXT_ARG(); + if (get_index(ad_select_tbl, *argv) < 0) + invarg("invalid ad_select", *argv); + + ad_select = get_index(ad_select_tbl, *argv); + addattr8(n, 1024, IFLA_BOND_AD_SELECT, ad_select); + } else if (matches(*argv, "ad_user_port_key") == 0) { + NEXT_ARG(); + if (get_u16(&ad_user_port_key, *argv, 0)) + invarg("invalid ad_user_port_key", *argv); + + addattr16(n, 1024, IFLA_BOND_AD_USER_PORT_KEY, + ad_user_port_key); + } else if (matches(*argv, "ad_actor_sys_prio") == 0) { + NEXT_ARG(); + if (get_u16(&ad_actor_sys_prio, *argv, 0)) + invarg("invalid ad_actor_sys_prio", *argv); + + addattr16(n, 1024, IFLA_BOND_AD_ACTOR_SYS_PRIO, + ad_actor_sys_prio); + } else if (matches(*argv, "ad_actor_system") == 0) { + int len; + char abuf[32]; + + NEXT_ARG(); + len = ll_addr_a2n(abuf, sizeof(abuf), *argv); + if (len < 0) + return -1; + addattr_l(n, 1024, IFLA_BOND_AD_ACTOR_SYSTEM, + abuf, len); + } else if (matches(*argv, "tlb_dynamic_lb") == 0) { + NEXT_ARG(); + if (get_u8(&tlb_dynamic_lb, *argv, 0)) { + invarg("invalid tlb_dynamic_lb", *argv); + return -1; + } + addattr8(n, 1024, IFLA_BOND_TLB_DYNAMIC_LB, + tlb_dynamic_lb); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "bond: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_BOND_MODE]) { + const char *mode = get_name(mode_tbl, + rta_getattr_u8(tb[IFLA_BOND_MODE])); + print_string(PRINT_ANY, "mode", "mode %s ", mode); + } + + if (tb[IFLA_BOND_ACTIVE_SLAVE]) { + unsigned int ifindex = + rta_getattr_u32(tb[IFLA_BOND_ACTIVE_SLAVE]); + + if (ifindex) { + print_string(PRINT_ANY, + "active_slave", + "active_slave %s ", + ll_index_to_name(ifindex)); + } + } + + if (tb[IFLA_BOND_MIIMON]) + print_uint(PRINT_ANY, + "miimon", + "miimon %u ", + rta_getattr_u32(tb[IFLA_BOND_MIIMON])); + + if (tb[IFLA_BOND_UPDELAY]) + print_uint(PRINT_ANY, + "updelay", + "updelay %u ", + rta_getattr_u32(tb[IFLA_BOND_UPDELAY])); + + if (tb[IFLA_BOND_DOWNDELAY]) + print_uint(PRINT_ANY, + "downdelay", + "downdelay %u ", + rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY])); + + if (tb[IFLA_BOND_PEER_NOTIF_DELAY]) + print_uint(PRINT_ANY, + "peer_notify_delay", + "peer_notify_delay %u ", + rta_getattr_u32(tb[IFLA_BOND_PEER_NOTIF_DELAY])); + + if (tb[IFLA_BOND_USE_CARRIER]) + print_uint(PRINT_ANY, + "use_carrier", + "use_carrier %u ", + rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER])); + + if (tb[IFLA_BOND_ARP_INTERVAL]) + print_uint(PRINT_ANY, + "arp_interval", + "arp_interval %u ", + rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL])); + + if (tb[IFLA_BOND_ARP_IP_TARGET]) { + struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1]; + int i; + + parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS, + tb[IFLA_BOND_ARP_IP_TARGET]); + + if (iptb[0]) { + open_json_array(PRINT_JSON, "arp_ip_target"); + print_string(PRINT_FP, NULL, "arp_ip_target ", NULL); + } + + for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { + if (iptb[i]) + print_string(PRINT_ANY, + NULL, + "%s", + rt_addr_n2a_rta(AF_INET, iptb[i])); + if (!is_json_context() + && i < BOND_MAX_ARP_TARGETS-1 + && iptb[i+1]) + fprintf(f, ","); + } + + if (iptb[0]) { + print_string(PRINT_FP, NULL, " ", NULL); + close_json_array(PRINT_JSON, NULL); + } + } + + if (tb[IFLA_BOND_ARP_VALIDATE]) { + __u32 arp_v = rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]); + const char *arp_validate = get_name(arp_validate_tbl, arp_v); + + if (!arp_v && is_json_context()) + print_null(PRINT_JSON, "arp_validate", NULL, NULL); + else + print_string(PRINT_ANY, + "arp_validate", + "arp_validate %s ", + arp_validate); + } + + if (tb[IFLA_BOND_ARP_ALL_TARGETS]) { + const char *arp_all_targets = get_name(arp_all_targets_tbl, + rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS])); + print_string(PRINT_ANY, + "arp_all_targets", + "arp_all_targets %s ", + arp_all_targets); + } + + if (tb[IFLA_BOND_PRIMARY]) { + unsigned int ifindex = rta_getattr_u32(tb[IFLA_BOND_PRIMARY]); + + if (ifindex) { + print_string(PRINT_ANY, + "primary", + "primary %s ", + ll_index_to_name(ifindex)); + } + } + + if (tb[IFLA_BOND_PRIMARY_RESELECT]) { + const char *primary_reselect = get_name(primary_reselect_tbl, + rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT])); + print_string(PRINT_ANY, + "primary_reselect", + "primary_reselect %s ", + primary_reselect); + } + + if (tb[IFLA_BOND_FAIL_OVER_MAC]) { + const char *fail_over_mac = get_name(fail_over_mac_tbl, + rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC])); + print_string(PRINT_ANY, + "fail_over_mac", + "fail_over_mac %s ", + fail_over_mac); + } + + if (tb[IFLA_BOND_XMIT_HASH_POLICY]) { + const char *xmit_hash_policy = get_name(xmit_hash_policy_tbl, + rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY])); + print_string(PRINT_ANY, + "xmit_hash_policy", + "xmit_hash_policy %s ", + xmit_hash_policy); + } + + if (tb[IFLA_BOND_RESEND_IGMP]) + print_uint(PRINT_ANY, + "resend_igmp", + "resend_igmp %u ", + rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP])); + + if (tb[IFLA_BOND_NUM_PEER_NOTIF]) + print_uint(PRINT_ANY, + "num_peer_notif", + "num_grat_arp %u ", + rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF])); + + if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE]) + print_uint(PRINT_ANY, + "all_slaves_active", + "all_slaves_active %u ", + rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE])); + + if (tb[IFLA_BOND_MIN_LINKS]) + print_uint(PRINT_ANY, + "min_links", + "min_links %u ", + rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS])); + + if (tb[IFLA_BOND_LP_INTERVAL]) + print_uint(PRINT_ANY, + "lp_interval", + "lp_interval %u ", + rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL])); + + if (tb[IFLA_BOND_PACKETS_PER_SLAVE]) + print_uint(PRINT_ANY, + "packets_per_slave", + "packets_per_slave %u ", + rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE])); + + if (tb[IFLA_BOND_AD_LACP_RATE]) { + const char *lacp_rate = get_name(lacp_rate_tbl, + rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE])); + print_string(PRINT_ANY, + "ad_lacp_rate", + "lacp_rate %s ", + lacp_rate); + } + + if (tb[IFLA_BOND_AD_SELECT]) { + const char *ad_select = get_name(ad_select_tbl, + rta_getattr_u8(tb[IFLA_BOND_AD_SELECT])); + print_string(PRINT_ANY, + "ad_select", + "ad_select %s ", + ad_select); + } + + if (tb[IFLA_BOND_AD_INFO]) { + struct rtattr *adtb[IFLA_BOND_AD_INFO_MAX + 1]; + + parse_rtattr_nested(adtb, IFLA_BOND_AD_INFO_MAX, + tb[IFLA_BOND_AD_INFO]); + + open_json_object("ad_info"); + + if (adtb[IFLA_BOND_AD_INFO_AGGREGATOR]) + print_int(PRINT_ANY, + "aggregator", + "ad_aggregator %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR])); + + if (adtb[IFLA_BOND_AD_INFO_NUM_PORTS]) + print_int(PRINT_ANY, + "num_ports", + "ad_num_ports %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS])); + + if (adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]) + print_int(PRINT_ANY, + "actor_key", + "ad_actor_key %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY])); + + if (adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]) + print_int(PRINT_ANY, + "partner_key", + "ad_partner_key %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY])); + + if (adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]) { + unsigned char *p = + RTA_DATA(adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]); + SPRINT_BUF(b); + print_string(PRINT_ANY, + "partner_mac", + "ad_partner_mac %s ", + ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b))); + } + + close_json_object(); + } + + if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]) { + print_uint(PRINT_ANY, + "ad_actor_sys_prio", + "ad_actor_sys_prio %u ", + rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO])); + } + + if (tb[IFLA_BOND_AD_USER_PORT_KEY]) { + print_uint(PRINT_ANY, + "ad_user_port_key", + "ad_user_port_key %u ", + rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY])); + } + + if (tb[IFLA_BOND_AD_ACTOR_SYSTEM]) { + /* We assume the l2 address is an Ethernet MAC address */ + SPRINT_BUF(b1); + + print_string(PRINT_ANY, + "ad_actor_system", + "ad_actor_system %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), + RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), + 1 /*ARPHDR_ETHER*/, b1, sizeof(b1))); + } + + if (tb[IFLA_BOND_TLB_DYNAMIC_LB]) { + print_uint(PRINT_ANY, + "tlb_dynamic_lb", + "tlb_dynamic_lb %u ", + rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB])); + } +} + +static void bond_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +static void bond_print_xstats_help(struct link_util *lu, FILE *f) +{ + fprintf(f, "Usage: ... %s [ 802.3ad ] [ dev DEVICE ]\n", lu->id); +} + +static void bond_print_3ad_stats(struct rtattr *lacpattr) +{ + struct rtattr *lacptb[BOND_3AD_STAT_MAX+1]; + __u64 val; + + parse_rtattr(lacptb, BOND_3AD_STAT_MAX, RTA_DATA(lacpattr), + RTA_PAYLOAD(lacpattr)); + open_json_object("802.3ad"); + if (lacptb[BOND_3AD_STAT_LACPDU_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "lacpdu_rx", "LACPDU Rx %llu\n", + rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_RX])); + } + if (lacptb[BOND_3AD_STAT_LACPDU_TX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "lacpdu_tx", "LACPDU Tx %llu\n", + rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_TX])); + } + if (lacptb[BOND_3AD_STAT_LACPDU_UNKNOWN_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + val = rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_UNKNOWN_RX]); + print_u64(PRINT_ANY, + "lacpdu_unknown_rx", + "LACPDU Unknown type Rx %llu\n", + val); + } + if (lacptb[BOND_3AD_STAT_LACPDU_ILLEGAL_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + val = rta_getattr_u64(lacptb[BOND_3AD_STAT_LACPDU_ILLEGAL_RX]); + print_u64(PRINT_ANY, + "lacpdu_illegal_rx", + "LACPDU Illegal Rx %llu\n", + val); + } + if (lacptb[BOND_3AD_STAT_MARKER_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "marker_rx", "Marker Rx %llu\n", + rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RX])); + } + if (lacptb[BOND_3AD_STAT_MARKER_TX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "marker_tx", "Marker Tx %llu\n", + rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_TX])); + } + if (lacptb[BOND_3AD_STAT_MARKER_RESP_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RESP_RX]); + print_u64(PRINT_ANY, + "marker_response_rx", + "Marker response Rx %llu\n", + val); + } + if (lacptb[BOND_3AD_STAT_MARKER_RESP_TX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_RESP_TX]); + print_u64(PRINT_ANY, + "marker_response_tx", + "Marker response Tx %llu\n", + val); + } + if (lacptb[BOND_3AD_STAT_MARKER_UNKNOWN_RX]) { + print_string(PRINT_FP, NULL, "%-16s ", ""); + val = rta_getattr_u64(lacptb[BOND_3AD_STAT_MARKER_UNKNOWN_RX]); + print_u64(PRINT_ANY, + "marker_unknown_rx", + "Marker unknown type Rx %llu\n", + val); + } + close_json_object(); +} + +static void bond_print_stats_attr(struct rtattr *attr, int ifindex) +{ + struct rtattr *bondtb[LINK_XSTATS_TYPE_MAX+1]; + struct rtattr *i, *list; + const char *ifname = ""; + int rem; + + parse_rtattr(bondtb, LINK_XSTATS_TYPE_MAX+1, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + if (!bondtb[LINK_XSTATS_TYPE_BOND]) + return; + + list = bondtb[LINK_XSTATS_TYPE_BOND]; + rem = RTA_PAYLOAD(list); + open_json_object(NULL); + ifname = ll_index_to_name(ifindex); + print_string(PRINT_ANY, "ifname", "%-16s\n", ifname); + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + if (xstats_print_attr && i->rta_type != xstats_print_attr) + continue; + + switch (i->rta_type) { + case BOND_XSTATS_3AD: + bond_print_3ad_stats(i); + break; + } + break; + } + close_json_object(); +} + +int bond_print_xstats(struct nlmsghdr *n, void *arg) +{ + struct if_stats_msg *ifsm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_STATS_MAX+1]; + int len = n->nlmsg_len; + + len -= NLMSG_LENGTH(sizeof(*ifsm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + if (filter_index && filter_index != ifsm->ifindex) + return 0; + + parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); + if (tb[IFLA_STATS_LINK_XSTATS]) + bond_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS], + ifsm->ifindex); + + if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) + bond_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE], + ifsm->ifindex); + + return 0; +} + +int bond_parse_xstats(struct link_util *lu, int argc, char **argv) +{ + while (argc > 0) { + if (strcmp(*argv, "lacp") == 0 || + strcmp(*argv, "802.3ad") == 0) { + xstats_print_attr = BOND_XSTATS_3AD; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + filter_index = ll_name_to_index(*argv); + if (!filter_index) + return nodev(*argv); + } else if (strcmp(*argv, "help") == 0) { + bond_print_xstats_help(lu, stdout); + exit(0); + } else { + invarg("unknown attribute", *argv); + } + argc--; argv++; + } + + return 0; +} + + +struct link_util bond_link_util = { + .id = "bond", + .maxattr = IFLA_BOND_MAX, + .parse_opt = bond_parse_opt, + .print_opt = bond_print_opt, + .print_help = bond_print_help, + .parse_ifla_xstats = bond_parse_xstats, + .print_ifla_xstats = bond_print_xstats, +}; diff --git a/ip/iplink_bond_slave.c b/ip/iplink_bond_slave.c new file mode 100644 index 0000000..d488aaa --- /dev/null +++ b/ip/iplink_bond_slave.c @@ -0,0 +1,189 @@ +/* + * iplink_bond_slave.c Bonding slave device support + * + * 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. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, "Usage: ... bond_slave [ queue_id ID ]\n"); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static const char *slave_states[] = { + [BOND_STATE_ACTIVE] = "ACTIVE", + [BOND_STATE_BACKUP] = "BACKUP", +}; + +static void print_slave_state(FILE *f, struct rtattr *tb) +{ + unsigned int state = rta_getattr_u8(tb); + + if (state >= ARRAY_SIZE(slave_states)) + print_int(PRINT_ANY, "state_index", "state %d ", state); + else + print_string(PRINT_ANY, + "state", + "state %s ", + slave_states[state]); +} + +static const char *slave_mii_status[] = { + [BOND_LINK_UP] = "UP", + [BOND_LINK_FAIL] = "GOING_DOWN", + [BOND_LINK_DOWN] = "DOWN", + [BOND_LINK_BACK] = "GOING_BACK", +}; + +static void print_slave_mii_status(FILE *f, struct rtattr *tb) +{ + unsigned int status = rta_getattr_u8(tb); + + if (status >= ARRAY_SIZE(slave_mii_status)) + print_int(PRINT_ANY, + "mii_status_index", + "mii_status %d ", + status); + else + print_string(PRINT_ANY, + "mii_status", + "mii_status %s ", + slave_mii_status[status]); +} + +static void print_slave_oper_state(FILE *fp, const char *name, __u16 state) +{ + open_json_array(PRINT_ANY, name); + print_string(PRINT_FP, NULL, " <", NULL); +#define _PF(s, str) if (state & LACP_STATE_##s) { \ + state &= ~LACP_STATE_##s; \ + print_string(PRINT_ANY, NULL, \ + state ? "%s," : "%s", str); } + _PF(LACP_ACTIVITY, "active"); + _PF(LACP_TIMEOUT, "short_timeout"); + _PF(AGGREGATION, "aggregating"); + _PF(SYNCHRONIZATION, "in_sync"); + _PF(COLLECTING, "collecting"); + _PF(DISTRIBUTING, "distributing"); + _PF(DEFAULTED, "defaulted"); + _PF(EXPIRED, "expired"); +#undef _PF + close_json_array(PRINT_ANY, "> "); +} + +static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + SPRINT_BUF(b1); + if (!tb) + return; + + if (tb[IFLA_BOND_SLAVE_STATE]) + print_slave_state(f, tb[IFLA_BOND_SLAVE_STATE]); + + if (tb[IFLA_BOND_SLAVE_MII_STATUS]) + print_slave_mii_status(f, tb[IFLA_BOND_SLAVE_MII_STATUS]); + + if (tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]) + print_int(PRINT_ANY, + "link_failure_count", + "link_failure_count %d ", + rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT])); + + if (tb[IFLA_BOND_SLAVE_PERM_HWADDR]) + print_string(PRINT_ANY, + "perm_hwaddr", + "perm_hwaddr %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), + RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), + 0, b1, sizeof(b1))); + + if (tb[IFLA_BOND_SLAVE_QUEUE_ID]) + print_int(PRINT_ANY, + "queue_id", + "queue_id %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID])); + + if (tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]) + print_int(PRINT_ANY, + "ad_aggregator_id", + "ad_aggregator_id %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])); + + if (tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]) { + __u8 state = rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]); + + print_int(PRINT_ANY, + "ad_actor_oper_port_state", + "ad_actor_oper_port_state %d ", + state); + print_slave_oper_state(f, "ad_actor_oper_port_state_str", state); + } + + if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]) { + __u16 state = rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]); + + print_int(PRINT_ANY, + "ad_partner_oper_port_state", + "ad_partner_oper_port_state %d ", + state); + print_slave_oper_state(f, "ad_partner_oper_port_state_str", state); + } +} + +static int bond_slave_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u16 queue_id; + + while (argc > 0) { + if (matches(*argv, "queue_id") == 0) { + NEXT_ARG(); + if (get_u16(&queue_id, *argv, 0)) + invarg("queue_id is invalid", *argv); + addattr16(n, 1024, IFLA_BOND_SLAVE_QUEUE_ID, queue_id); + } else { + if (matches(*argv, "help") != 0) + fprintf(stderr, + "bond_slave: unknown option \"%s\"?\n", + *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void bond_slave_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util bond_slave_link_util = { + .id = "bond_slave", + .maxattr = IFLA_BOND_SLAVE_MAX, + .print_opt = bond_slave_print_opt, + .parse_opt = bond_slave_parse_opt, + .print_help = bond_slave_print_help, + .parse_ifla_xstats = bond_parse_xstats, + .print_ifla_xstats = bond_print_xstats, +}; diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c new file mode 100644 index 0000000..3e81aa0 --- /dev/null +++ b/ip/iplink_bridge.c @@ -0,0 +1,897 @@ +/* + * iplink_bridge.c Bridge device support + * + * 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. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static unsigned int xstats_print_attr; +static int filter_index; + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... bridge [ fdb_flush ]\n" + " [ forward_delay FORWARD_DELAY ]\n" + " [ hello_time HELLO_TIME ]\n" + " [ max_age MAX_AGE ]\n" + " [ ageing_time AGEING_TIME ]\n" + " [ stp_state STP_STATE ]\n" + " [ priority PRIORITY ]\n" + " [ group_fwd_mask MASK ]\n" + " [ group_address ADDRESS ]\n" + " [ vlan_filtering VLAN_FILTERING ]\n" + " [ vlan_protocol VLAN_PROTOCOL ]\n" + " [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n" + " [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n" + " [ vlan_stats_per_port VLAN_STATS_PER_PORT ]\n" + " [ mcast_snooping MULTICAST_SNOOPING ]\n" + " [ mcast_router MULTICAST_ROUTER ]\n" + " [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n" + " [ mcast_querier MULTICAST_QUERIER ]\n" + " [ mcast_hash_elasticity HASH_ELASTICITY ]\n" + " [ mcast_hash_max HASH_MAX ]\n" + " [ mcast_last_member_count LAST_MEMBER_COUNT ]\n" + " [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n" + " [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n" + " [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n" + " [ mcast_querier_interval QUERIER_INTERVAL ]\n" + " [ mcast_query_interval QUERY_INTERVAL ]\n" + " [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n" + " [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n" + " [ mcast_stats_enabled MCAST_STATS_ENABLED ]\n" + " [ mcast_igmp_version IGMP_VERSION ]\n" + " [ mcast_mld_version MLD_VERSION ]\n" + " [ nf_call_iptables NF_CALL_IPTABLES ]\n" + " [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n" + " [ nf_call_arptables NF_CALL_ARPTABLES ]\n" + "\n" + "Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len) +{ + char eaddr[32]; + + ether_ntoa_r((const struct ether_addr *)id->addr, eaddr); + snprintf(buf, len, "%.2x%.2x.%s", id->prio[0], id->prio[1], eaddr); +} + +static int bridge_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u32 val; + + while (argc > 0) { + if (matches(*argv, "forward_delay") == 0) { + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid forward_delay", *argv); + + addattr32(n, 1024, IFLA_BR_FORWARD_DELAY, val); + } else if (matches(*argv, "hello_time") == 0) { + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid hello_time", *argv); + + addattr32(n, 1024, IFLA_BR_HELLO_TIME, val); + } else if (matches(*argv, "max_age") == 0) { + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid max_age", *argv); + + addattr32(n, 1024, IFLA_BR_MAX_AGE, val); + } else if (matches(*argv, "ageing_time") == 0) { + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid ageing_time", *argv); + + addattr32(n, 1024, IFLA_BR_AGEING_TIME, val); + } else if (matches(*argv, "stp_state") == 0) { + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid stp_state", *argv); + + addattr32(n, 1024, IFLA_BR_STP_STATE, val); + } else if (matches(*argv, "priority") == 0) { + __u16 prio; + + NEXT_ARG(); + if (get_u16(&prio, *argv, 0)) + invarg("invalid priority", *argv); + + addattr16(n, 1024, IFLA_BR_PRIORITY, prio); + } else if (matches(*argv, "vlan_filtering") == 0) { + __u8 vlan_filter; + + NEXT_ARG(); + if (get_u8(&vlan_filter, *argv, 0)) + invarg("invalid vlan_filtering", *argv); + + addattr8(n, 1024, IFLA_BR_VLAN_FILTERING, vlan_filter); + } else if (matches(*argv, "vlan_protocol") == 0) { + __u16 vlan_proto; + + NEXT_ARG(); + if (ll_proto_a2n(&vlan_proto, *argv)) + invarg("invalid vlan_protocol", *argv); + + addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto); + } else if (matches(*argv, "group_fwd_mask") == 0) { + __u16 fwd_mask; + + NEXT_ARG(); + if (get_u16(&fwd_mask, *argv, 0)) + invarg("invalid group_fwd_mask", *argv); + + addattr16(n, 1024, IFLA_BR_GROUP_FWD_MASK, fwd_mask); + } else if (matches(*argv, "group_address") == 0) { + char llabuf[32]; + int len; + + NEXT_ARG(); + len = ll_addr_a2n(llabuf, sizeof(llabuf), *argv); + if (len < 0) + return -1; + addattr_l(n, 1024, IFLA_BR_GROUP_ADDR, llabuf, len); + } else if (matches(*argv, "fdb_flush") == 0) { + addattr(n, 1024, IFLA_BR_FDB_FLUSH); + } else if (matches(*argv, "vlan_default_pvid") == 0) { + __u16 default_pvid; + + NEXT_ARG(); + if (get_u16(&default_pvid, *argv, 0)) + invarg("invalid vlan_default_pvid", *argv); + + addattr16(n, 1024, IFLA_BR_VLAN_DEFAULT_PVID, + default_pvid); + } else if (matches(*argv, "vlan_stats_enabled") == 0) { + __u8 vlan_stats_enabled; + + NEXT_ARG(); + if (get_u8(&vlan_stats_enabled, *argv, 0)) + invarg("invalid vlan_stats_enabled", *argv); + addattr8(n, 1024, IFLA_BR_VLAN_STATS_ENABLED, + vlan_stats_enabled); + } else if (matches(*argv, "vlan_stats_per_port") == 0) { + __u8 vlan_stats_per_port; + + NEXT_ARG(); + if (get_u8(&vlan_stats_per_port, *argv, 0)) + invarg("invalid vlan_stats_per_port", *argv); + addattr8(n, 1024, IFLA_BR_VLAN_STATS_PER_PORT, + vlan_stats_per_port); + } else if (matches(*argv, "mcast_router") == 0) { + __u8 mcast_router; + + NEXT_ARG(); + if (get_u8(&mcast_router, *argv, 0)) + invarg("invalid mcast_router", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_ROUTER, mcast_router); + } else if (matches(*argv, "mcast_snooping") == 0) { + __u8 mcast_snoop; + + NEXT_ARG(); + if (get_u8(&mcast_snoop, *argv, 0)) + invarg("invalid mcast_snooping", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop); + } else if (matches(*argv, "mcast_query_use_ifaddr") == 0) { + __u8 mcast_qui; + + NEXT_ARG(); + if (get_u8(&mcast_qui, *argv, 0)) + invarg("invalid mcast_query_use_ifaddr", + *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_QUERY_USE_IFADDR, + mcast_qui); + } else if (matches(*argv, "mcast_querier") == 0) { + __u8 mcast_querier; + + NEXT_ARG(); + if (get_u8(&mcast_querier, *argv, 0)) + invarg("invalid mcast_querier", *argv); + + addattr8(n, 1024, IFLA_BR_MCAST_QUERIER, mcast_querier); + } else if (matches(*argv, "mcast_hash_elasticity") == 0) { + __u32 mcast_hash_el; + + NEXT_ARG(); + if (get_u32(&mcast_hash_el, *argv, 0)) + invarg("invalid mcast_hash_elasticity", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_HASH_ELASTICITY, + mcast_hash_el); + } else if (matches(*argv, "mcast_hash_max") == 0) { + __u32 mcast_hash_max; + + NEXT_ARG(); + if (get_u32(&mcast_hash_max, *argv, 0)) + invarg("invalid mcast_hash_max", *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_HASH_MAX, + mcast_hash_max); + } else if (matches(*argv, "mcast_last_member_count") == 0) { + __u32 mcast_lmc; + + NEXT_ARG(); + if (get_u32(&mcast_lmc, *argv, 0)) + invarg("invalid mcast_last_member_count", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_CNT, + mcast_lmc); + } else if (matches(*argv, "mcast_startup_query_count") == 0) { + __u32 mcast_sqc; + + NEXT_ARG(); + if (get_u32(&mcast_sqc, *argv, 0)) + invarg("invalid mcast_startup_query_count", + *argv); + + addattr32(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_CNT, + mcast_sqc); + } else if (matches(*argv, "mcast_last_member_interval") == 0) { + __u64 mcast_last_member_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_last_member_intvl, *argv, 0)) + invarg("invalid mcast_last_member_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_INTVL, + mcast_last_member_intvl); + } else if (matches(*argv, "mcast_membership_interval") == 0) { + __u64 mcast_membership_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_membership_intvl, *argv, 0)) + invarg("invalid mcast_membership_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_MEMBERSHIP_INTVL, + mcast_membership_intvl); + } else if (matches(*argv, "mcast_querier_interval") == 0) { + __u64 mcast_querier_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_querier_intvl, *argv, 0)) + invarg("invalid mcast_querier_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERIER_INTVL, + mcast_querier_intvl); + } else if (matches(*argv, "mcast_query_interval") == 0) { + __u64 mcast_query_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_query_intvl, *argv, 0)) + invarg("invalid mcast_query_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERY_INTVL, + mcast_query_intvl); + } else if (!matches(*argv, "mcast_query_response_interval")) { + __u64 mcast_query_resp_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_query_resp_intvl, *argv, 0)) + invarg("invalid mcast_query_response_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + mcast_query_resp_intvl); + } else if (!matches(*argv, "mcast_startup_query_interval")) { + __u64 mcast_startup_query_intvl; + + NEXT_ARG(); + if (get_u64(&mcast_startup_query_intvl, *argv, 0)) + invarg("invalid mcast_startup_query_interval", + *argv); + + addattr64(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + mcast_startup_query_intvl); + } else if (matches(*argv, "mcast_stats_enabled") == 0) { + __u8 mcast_stats_enabled; + + NEXT_ARG(); + if (get_u8(&mcast_stats_enabled, *argv, 0)) + invarg("invalid mcast_stats_enabled", *argv); + addattr8(n, 1024, IFLA_BR_MCAST_STATS_ENABLED, + mcast_stats_enabled); + } else if (matches(*argv, "mcast_igmp_version") == 0) { + __u8 igmp_version; + + NEXT_ARG(); + if (get_u8(&igmp_version, *argv, 0)) + invarg("invalid mcast_igmp_version", *argv); + addattr8(n, 1024, IFLA_BR_MCAST_IGMP_VERSION, + igmp_version); + } else if (matches(*argv, "mcast_mld_version") == 0) { + __u8 mld_version; + + NEXT_ARG(); + if (get_u8(&mld_version, *argv, 0)) + invarg("invalid mcast_mld_version", *argv); + addattr8(n, 1024, IFLA_BR_MCAST_MLD_VERSION, + mld_version); + } else if (matches(*argv, "nf_call_iptables") == 0) { + __u8 nf_call_ipt; + + NEXT_ARG(); + if (get_u8(&nf_call_ipt, *argv, 0)) + invarg("invalid nf_call_iptables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_IPTABLES, + nf_call_ipt); + } else if (matches(*argv, "nf_call_ip6tables") == 0) { + __u8 nf_call_ip6t; + + NEXT_ARG(); + if (get_u8(&nf_call_ip6t, *argv, 0)) + invarg("invalid nf_call_ip6tables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_IP6TABLES, + nf_call_ip6t); + } else if (matches(*argv, "nf_call_arptables") == 0) { + __u8 nf_call_arpt; + + NEXT_ARG(); + if (get_u8(&nf_call_arpt, *argv, 0)) + invarg("invalid nf_call_arptables", *argv); + + addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES, + nf_call_arpt); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "bridge: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void _bridge_print_timer(FILE *f, + const char *attr, + struct rtattr *timer) +{ + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(timer)); + if (is_json_context()) { + json_writer_t *jw = get_json_writer(); + + jsonw_name(jw, attr); + jsonw_printf(jw, "%i.%.2i", + (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } else { + fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } +} + +static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_BR_FORWARD_DELAY]) + print_uint(PRINT_ANY, + "forward_delay", + "forward_delay %u ", + rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY])); + + if (tb[IFLA_BR_HELLO_TIME]) + print_uint(PRINT_ANY, + "hello_time", + "hello_time %u ", + rta_getattr_u32(tb[IFLA_BR_HELLO_TIME])); + + if (tb[IFLA_BR_MAX_AGE]) + print_uint(PRINT_ANY, + "max_age", + "max_age %u ", + rta_getattr_u32(tb[IFLA_BR_MAX_AGE])); + + if (tb[IFLA_BR_AGEING_TIME]) + print_uint(PRINT_ANY, + "ageing_time", + "ageing_time %u ", + rta_getattr_u32(tb[IFLA_BR_AGEING_TIME])); + + if (tb[IFLA_BR_STP_STATE]) + print_uint(PRINT_ANY, + "stp_state", + "stp_state %u ", + rta_getattr_u32(tb[IFLA_BR_STP_STATE])); + + if (tb[IFLA_BR_PRIORITY]) + print_uint(PRINT_ANY, + "priority", + "priority %u ", + rta_getattr_u16(tb[IFLA_BR_PRIORITY])); + + if (tb[IFLA_BR_VLAN_FILTERING]) + print_uint(PRINT_ANY, + "vlan_filtering", + "vlan_filtering %u ", + rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING])); + + if (tb[IFLA_BR_VLAN_PROTOCOL]) { + SPRINT_BUF(b1); + + print_string(PRINT_ANY, + "vlan_protocol", + "vlan_protocol %s ", + ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), + b1, sizeof(b1))); + } + + if (tb[IFLA_BR_BRIDGE_ID]) { + char bridge_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id, + sizeof(bridge_id)); + print_string(PRINT_ANY, + "bridge_id", + "bridge_id %s ", + bridge_id); + } + + if (tb[IFLA_BR_ROOT_ID]) { + char root_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id, + sizeof(root_id)); + print_string(PRINT_ANY, + "root_id", + "designated_root %s ", + root_id); + } + + if (tb[IFLA_BR_ROOT_PORT]) + print_uint(PRINT_ANY, + "root_port", + "root_port %u ", + rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); + + if (tb[IFLA_BR_ROOT_PATH_COST]) + print_uint(PRINT_ANY, + "root_path_cost", + "root_path_cost %u ", + rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); + + if (tb[IFLA_BR_TOPOLOGY_CHANGE]) + print_uint(PRINT_ANY, + "topology_change", + "topology_change %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); + + if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]) + print_uint(PRINT_ANY, + "topology_change_detected", + "topology_change_detected %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); + + if (tb[IFLA_BR_HELLO_TIMER]) + _bridge_print_timer(f, "hello_timer", tb[IFLA_BR_HELLO_TIMER]); + + if (tb[IFLA_BR_TCN_TIMER]) + _bridge_print_timer(f, "tcn_timer", tb[IFLA_BR_TCN_TIMER]); + + if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) + _bridge_print_timer(f, "topology_change_timer", + tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); + + if (tb[IFLA_BR_GC_TIMER]) + _bridge_print_timer(f, "gc_timer", tb[IFLA_BR_GC_TIMER]); + + if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) + print_uint(PRINT_ANY, + "vlan_default_pvid", + "vlan_default_pvid %u ", + rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); + + if (tb[IFLA_BR_VLAN_STATS_ENABLED]) + print_uint(PRINT_ANY, + "vlan_stats_enabled", + "vlan_stats_enabled %u ", + rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED])); + + if (tb[IFLA_BR_VLAN_STATS_PER_PORT]) + print_uint(PRINT_ANY, + "vlan_stats_per_port", + "vlan_stats_per_port %u ", + rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_PER_PORT])); + + if (tb[IFLA_BR_GROUP_FWD_MASK]) + print_0xhex(PRINT_ANY, + "group_fwd_mask", + "group_fwd_mask %#llx ", + rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); + + if (tb[IFLA_BR_GROUP_ADDR]) { + SPRINT_BUF(mac); + + print_string(PRINT_ANY, + "group_addr", + "group_address %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), + RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), + 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); + } + + if (tb[IFLA_BR_MCAST_SNOOPING]) + print_uint(PRINT_ANY, + "mcast_snooping", + "mcast_snooping %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); + + if (tb[IFLA_BR_MCAST_ROUTER]) + print_uint(PRINT_ANY, + "mcast_router", + "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); + + if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) + print_uint(PRINT_ANY, + "mcast_query_use_ifaddr", + "mcast_query_use_ifaddr %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); + + if (tb[IFLA_BR_MCAST_QUERIER]) + print_uint(PRINT_ANY, + "mcast_querier", + "mcast_querier %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); + + if (tb[IFLA_BR_MCAST_HASH_ELASTICITY]) + print_uint(PRINT_ANY, + "mcast_hash_elasticity", + "mcast_hash_elasticity %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); + + if (tb[IFLA_BR_MCAST_HASH_MAX]) + print_uint(PRINT_ANY, + "mcast_hash_max", + "mcast_hash_max %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); + + if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) + print_uint(PRINT_ANY, + "mcast_last_member_cnt", + "mcast_last_member_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); + + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) + print_uint(PRINT_ANY, + "mcast_startup_query_cnt", + "mcast_startup_query_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); + + if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) + print_lluint(PRINT_ANY, + "mcast_last_member_intvl", + "mcast_last_member_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); + + if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) + print_lluint(PRINT_ANY, + "mcast_membership_intvl", + "mcast_membership_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) + print_lluint(PRINT_ANY, + "mcast_querier_intvl", + "mcast_querier_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERY_INTVL]) + print_lluint(PRINT_ANY, + "mcast_query_intvl", + "mcast_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); + + if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) + print_lluint(PRINT_ANY, + "mcast_query_response_intvl", + "mcast_query_response_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); + + if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) + print_lluint(PRINT_ANY, + "mcast_startup_query_intvl", + "mcast_startup_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); + + if (tb[IFLA_BR_MCAST_STATS_ENABLED]) + print_uint(PRINT_ANY, + "mcast_stats_enabled", + "mcast_stats_enabled %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED])); + + if (tb[IFLA_BR_MCAST_IGMP_VERSION]) + print_uint(PRINT_ANY, + "mcast_igmp_version", + "mcast_igmp_version %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION])); + + if (tb[IFLA_BR_MCAST_MLD_VERSION]) + print_uint(PRINT_ANY, + "mcast_mld_version", + "mcast_mld_version %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION])); + + if (tb[IFLA_BR_NF_CALL_IPTABLES]) + print_uint(PRINT_ANY, + "nf_call_iptables", + "nf_call_iptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); + + if (tb[IFLA_BR_NF_CALL_IP6TABLES]) + print_uint(PRINT_ANY, + "nf_call_ip6tables", + "nf_call_ip6tables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); + + if (tb[IFLA_BR_NF_CALL_ARPTABLES]) + print_uint(PRINT_ANY, + "nf_call_arptables", + "nf_call_arptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); +} + +static void bridge_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +static void bridge_print_xstats_help(struct link_util *lu, FILE *f) +{ + fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id); +} + +static void bridge_print_stats_attr(struct rtattr *attr, int ifindex) +{ + struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; + struct bridge_stp_xstats *sstats; + struct br_mcast_stats *mstats; + struct rtattr *i, *list; + const char *ifname = ""; + int rem; + + parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) + return; + + list = brtb[LINK_XSTATS_TYPE_BRIDGE]; + rem = RTA_PAYLOAD(list); + open_json_object(NULL); + ifname = ll_index_to_name(ifindex); + print_string(PRINT_ANY, "ifname", "%-16s\n", ifname); + for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + if (xstats_print_attr && i->rta_type != xstats_print_attr) + continue; + switch (i->rta_type) { + case BRIDGE_XSTATS_MCAST: + mstats = RTA_DATA(i); + open_json_object("multicast"); + open_json_object("igmp_queries"); + print_string(PRINT_FP, NULL, + "%-16s IGMP queries:\n", ""); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", + mstats->igmp_v1queries[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v2", "v2 %llu ", + mstats->igmp_v2queries[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n", + mstats->igmp_v3queries[BR_MCAST_DIR_RX]); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", + mstats->igmp_v1queries[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v2", "v2 %llu ", + mstats->igmp_v2queries[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n", + mstats->igmp_v3queries[BR_MCAST_DIR_TX]); + close_json_object(); + + open_json_object("igmp_reports"); + print_string(PRINT_FP, NULL, + "%-16s IGMP reports:\n", ""); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", + mstats->igmp_v1reports[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v2", "v2 %llu ", + mstats->igmp_v2reports[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n", + mstats->igmp_v3reports[BR_MCAST_DIR_RX]); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", + mstats->igmp_v1reports[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v2", "v2 %llu ", + mstats->igmp_v2reports[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n", + mstats->igmp_v3reports[BR_MCAST_DIR_TX]); + close_json_object(); + + open_json_object("igmp_leaves"); + print_string(PRINT_FP, NULL, + "%-16s IGMP leaves: ", ""); + print_u64(PRINT_ANY, "rx", "RX: %llu ", + mstats->igmp_leaves[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "tx", "TX: %llu\n", + mstats->igmp_leaves[BR_MCAST_DIR_TX]); + close_json_object(); + + print_string(PRINT_FP, NULL, + "%-16s IGMP parse errors: ", ""); + print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n", + mstats->igmp_parse_errors); + + open_json_object("mld_queries"); + print_string(PRINT_FP, NULL, + "%-16s MLD queries:\n", ""); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", + mstats->mld_v1queries[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n", + mstats->mld_v2queries[BR_MCAST_DIR_RX]); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", + mstats->mld_v1queries[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n", + mstats->mld_v2queries[BR_MCAST_DIR_TX]); + close_json_object(); + + open_json_object("mld_reports"); + print_string(PRINT_FP, NULL, + "%-16s MLD reports:\n", ""); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", + mstats->mld_v1reports[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n", + mstats->mld_v2reports[BR_MCAST_DIR_RX]); + print_string(PRINT_FP, NULL, "%-16s ", ""); + print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", + mstats->mld_v1reports[BR_MCAST_DIR_TX]); + print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n", + mstats->mld_v2reports[BR_MCAST_DIR_TX]); + close_json_object(); + + open_json_object("mld_leaves"); + print_string(PRINT_FP, NULL, + "%-16s MLD leaves: ", ""); + print_u64(PRINT_ANY, "rx", "RX: %llu ", + mstats->mld_leaves[BR_MCAST_DIR_RX]); + print_u64(PRINT_ANY, "tx", "TX: %llu\n", + mstats->mld_leaves[BR_MCAST_DIR_TX]); + close_json_object(); + + print_string(PRINT_FP, NULL, + "%-16s MLD parse errors: ", ""); + print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n", + mstats->mld_parse_errors); + close_json_object(); + break; + case BRIDGE_XSTATS_STP: + sstats = RTA_DATA(i); + open_json_object("stp"); + print_string(PRINT_FP, NULL, + "%-16s STP BPDU: ", ""); + print_u64(PRINT_ANY, "rx_bpdu", "RX: %llu ", + sstats->rx_bpdu); + print_u64(PRINT_ANY, "tx_bpdu", "TX: %llu\n", + sstats->tx_bpdu); + print_string(PRINT_FP, NULL, + "%-16s STP TCN: ", ""); + print_u64(PRINT_ANY, "rx_tcn", "RX: %llu ", + sstats->rx_tcn); + print_u64(PRINT_ANY, "tx_tcn", "TX: %llu\n", + sstats->tx_tcn); + print_string(PRINT_FP, NULL, + "%-16s STP Transitions: ", ""); + print_u64(PRINT_ANY, "transition_blk", "Blocked: %llu ", + sstats->transition_blk); + print_u64(PRINT_ANY, "transition_fwd", "Forwarding: %llu\n", + sstats->transition_fwd); + close_json_object(); + break; + } + } + close_json_object(); +} + +int bridge_print_xstats(struct nlmsghdr *n, void *arg) +{ + struct if_stats_msg *ifsm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_STATS_MAX+1]; + int len = n->nlmsg_len; + + len -= NLMSG_LENGTH(sizeof(*ifsm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + if (filter_index && filter_index != ifsm->ifindex) + return 0; + + parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); + if (tb[IFLA_STATS_LINK_XSTATS]) + bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS], + ifsm->ifindex); + + if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) + bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE], + ifsm->ifindex); + + return 0; +} + +int bridge_parse_xstats(struct link_util *lu, int argc, char **argv) +{ + while (argc > 0) { + if (strcmp(*argv, "igmp") == 0 || strcmp(*argv, "mcast") == 0) { + xstats_print_attr = BRIDGE_XSTATS_MCAST; + } else if (strcmp(*argv, "stp") == 0) { + xstats_print_attr = BRIDGE_XSTATS_STP; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + filter_index = ll_name_to_index(*argv); + if (!filter_index) + return nodev(*argv); + } else if (strcmp(*argv, "help") == 0) { + bridge_print_xstats_help(lu, stdout); + exit(0); + } else { + invarg("unknown attribute", *argv); + } + argc--; argv++; + } + + return 0; +} + +struct link_util bridge_link_util = { + .id = "bridge", + .maxattr = IFLA_BR_MAX, + .parse_opt = bridge_parse_opt, + .print_opt = bridge_print_opt, + .print_help = bridge_print_help, + .parse_ifla_xstats = bridge_parse_xstats, + .print_ifla_xstats = bridge_print_xstats, +}; diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c new file mode 100644 index 0000000..79a1d2f --- /dev/null +++ b/ip/iplink_bridge_slave.c @@ -0,0 +1,447 @@ +/* + * iplink_bridge_slave.c Bridge slave device support + * + * 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. + * + * Authors: Jiri Pirko + */ + +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... bridge_slave [ fdb_flush ]\n" + " [ state STATE ]\n" + " [ priority PRIO ]\n" + " [ cost COST ]\n" + " [ guard {on | off} ]\n" + " [ hairpin {on | off} ]\n" + " [ fastleave {on | off} ]\n" + " [ root_block {on | off} ]\n" + " [ learning {on | off} ]\n" + " [ flood {on | off} ]\n" + " [ proxy_arp {on | off} ]\n" + " [ proxy_arp_wifi {on | off} ]\n" + " [ mcast_router MULTICAST_ROUTER ]\n" + " [ mcast_fast_leave {on | off} ]\n" + " [ mcast_flood {on | off} ]\n" + " [ mcast_to_unicast {on | off} ]\n" + " [ group_fwd_mask MASK ]\n" + " [ neigh_suppress {on | off} ]\n" + " [ vlan_tunnel {on | off} ]\n" + " [ isolated {on | off} ]\n" + " [ backup_port DEVICE ] [ nobackup_port ]\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static const char *port_states[] = { + [BR_STATE_DISABLED] = "disabled", + [BR_STATE_LISTENING] = "listening", + [BR_STATE_LEARNING] = "learning", + [BR_STATE_FORWARDING] = "forwarding", + [BR_STATE_BLOCKING] = "blocking", +}; + +static const char *fwd_mask_tbl[16] = { + [0] = "stp", + [2] = "lacp", + [14] = "lldp" +}; + +static void print_portstate(FILE *f, __u8 state) +{ + if (state <= BR_STATE_BLOCKING) + print_string(PRINT_ANY, + "state", + "state %s ", + port_states[state]); + else + print_int(PRINT_ANY, "state_index", "state (%d) ", state); +} + +static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val) +{ + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, val); + else + fprintf(f, "%s %s ", flag, val ? "on" : "off"); +} + +static void _print_timer(FILE *f, const char *attr, struct rtattr *timer) +{ + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(timer)); + if (is_json_context()) { + json_writer_t *jw = get_json_writer(); + + jsonw_name(jw, attr); + jsonw_printf(jw, "%i.%.2i", + (int)tv.tv_sec, (int)tv.tv_usec / 10000); + } else { + fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } +} + +static void _bitmask2str(__u16 bitmask, char *dst, size_t dst_size, + const char **tbl) +{ + int len, i; + + for (i = 0, len = 0; bitmask; i++, bitmask >>= 1) { + if (bitmask & 0x1) { + if (tbl[i]) + len += snprintf(dst + len, dst_size - len, "%s,", + tbl[i]); + else + len += snprintf(dst + len, dst_size - len, "0x%x,", + (1 << i)); + } + } + + if (!len) + snprintf(dst, dst_size, "0x0"); + else + dst[len - 1] = 0; +} + +static void bridge_slave_print_opt(struct link_util *lu, FILE *f, + struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_BRPORT_STATE]) + print_portstate(f, rta_getattr_u8(tb[IFLA_BRPORT_STATE])); + + if (tb[IFLA_BRPORT_PRIORITY]) + print_int(PRINT_ANY, + "priority", + "priority %d ", + rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY])); + + if (tb[IFLA_BRPORT_COST]) + print_int(PRINT_ANY, + "cost", + "cost %d ", + rta_getattr_u32(tb[IFLA_BRPORT_COST])); + + if (tb[IFLA_BRPORT_MODE]) + _print_onoff(f, "mode", "hairpin", + rta_getattr_u8(tb[IFLA_BRPORT_MODE])); + + if (tb[IFLA_BRPORT_GUARD]) + _print_onoff(f, "guard", "guard", + rta_getattr_u8(tb[IFLA_BRPORT_GUARD])); + + if (tb[IFLA_BRPORT_PROTECT]) + _print_onoff(f, "protect", "root_block", + rta_getattr_u8(tb[IFLA_BRPORT_PROTECT])); + + if (tb[IFLA_BRPORT_FAST_LEAVE]) + _print_onoff(f, "fast_leave", "fastleave", + rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE])); + + if (tb[IFLA_BRPORT_LEARNING]) + _print_onoff(f, "learning", "learning", + rta_getattr_u8(tb[IFLA_BRPORT_LEARNING])); + + if (tb[IFLA_BRPORT_UNICAST_FLOOD]) + _print_onoff(f, "unicast_flood", "flood", + rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD])); + + if (tb[IFLA_BRPORT_ID]) + print_0xhex(PRINT_ANY, "id", "port_id %#llx ", + rta_getattr_u16(tb[IFLA_BRPORT_ID])); + + if (tb[IFLA_BRPORT_NO]) + print_0xhex(PRINT_ANY, "no", "port_no %#llx ", + rta_getattr_u16(tb[IFLA_BRPORT_NO])); + + if (tb[IFLA_BRPORT_DESIGNATED_PORT]) + print_uint(PRINT_ANY, + "designated_port", + "designated_port %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT])); + + if (tb[IFLA_BRPORT_DESIGNATED_COST]) + print_uint(PRINT_ANY, + "designated_cost", + "designated_cost %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST])); + + if (tb[IFLA_BRPORT_BRIDGE_ID]) { + char bridge_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]), + bridge_id, sizeof(bridge_id)); + print_string(PRINT_ANY, + "bridge_id", + "designated_bridge %s ", + bridge_id); + } + + if (tb[IFLA_BRPORT_ROOT_ID]) { + char root_id[32]; + + br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]), + root_id, sizeof(root_id)); + print_string(PRINT_ANY, + "root_id", + "designated_root %s ", root_id); + } + + if (tb[IFLA_BRPORT_HOLD_TIMER]) + _print_timer(f, "hold_timer", tb[IFLA_BRPORT_HOLD_TIMER]); + + if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]) + _print_timer(f, "message_age_timer", + tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]); + + if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]) + _print_timer(f, "forward_delay_timer", + tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]); + + if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]) + print_uint(PRINT_ANY, + "topology_change_ack", + "topology_change_ack %u ", + rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])); + + if (tb[IFLA_BRPORT_CONFIG_PENDING]) + print_uint(PRINT_ANY, + "config_pending", + "config_pending %u ", + rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING])); + + if (tb[IFLA_BRPORT_PROXYARP]) + _print_onoff(f, "proxyarp", "proxy_arp", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP])); + + if (tb[IFLA_BRPORT_PROXYARP_WIFI]) + _print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI])); + + if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) + print_uint(PRINT_ANY, + "multicast_router", + "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER])); + + if (tb[IFLA_BRPORT_FAST_LEAVE]) + // not printing any json here because + // we already printed fast_leave before + print_string(PRINT_FP, + NULL, + "mcast_fast_leave %s ", + rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off"); + + if (tb[IFLA_BRPORT_MCAST_FLOOD]) + _print_onoff(f, "mcast_flood", "mcast_flood", + rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD])); + + if (tb[IFLA_BRPORT_MCAST_TO_UCAST]) + _print_onoff(f, "mcast_to_unicast", "mcast_to_unicast", + rta_getattr_u8(tb[IFLA_BRPORT_MCAST_TO_UCAST])); + + if (tb[IFLA_BRPORT_NEIGH_SUPPRESS]) + _print_onoff(f, "neigh_suppress", "neigh_suppress", + rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS])); + + if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) { + char convbuf[256]; + __u16 fwd_mask; + + fwd_mask = rta_getattr_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]); + print_0xhex(PRINT_ANY, "group_fwd_mask", + "group_fwd_mask %#llx ", fwd_mask); + _bitmask2str(fwd_mask, convbuf, sizeof(convbuf), fwd_mask_tbl); + print_string(PRINT_ANY, "group_fwd_mask_str", + "group_fwd_mask_str %s ", convbuf); + } + + if (tb[IFLA_BRPORT_VLAN_TUNNEL]) + _print_onoff(f, "vlan_tunnel", "vlan_tunnel", + rta_getattr_u8(tb[IFLA_BRPORT_VLAN_TUNNEL])); + + if (tb[IFLA_BRPORT_ISOLATED]) + _print_onoff(f, "isolated", "isolated", + rta_getattr_u8(tb[IFLA_BRPORT_ISOLATED])); + + if (tb[IFLA_BRPORT_BACKUP_PORT]) { + int backup_p = rta_getattr_u32(tb[IFLA_BRPORT_BACKUP_PORT]); + + print_string(PRINT_ANY, "backup_port", "backup_port %s ", + ll_index_to_name(backup_p)); + } +} + +static void bridge_slave_parse_on_off(char *arg_name, char *arg_val, + struct nlmsghdr *n, int type) +{ + __u8 val; + + if (strcmp(arg_val, "on") == 0) + val = 1; + else if (strcmp(arg_val, "off") == 0) + val = 0; + else + invarg("should be \"on\" or \"off\"", arg_name); + + addattr8(n, 1024, type, val); +} + +static int bridge_slave_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u8 state; + __u16 priority; + __u32 cost; + + while (argc > 0) { + if (matches(*argv, "fdb_flush") == 0) { + addattr(n, 1024, IFLA_BRPORT_FLUSH); + } else if (matches(*argv, "state") == 0) { + NEXT_ARG(); + if (get_u8(&state, *argv, 0)) + invarg("state is invalid", *argv); + addattr8(n, 1024, IFLA_BRPORT_STATE, state); + } else if (matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u16(&priority, *argv, 0)) + invarg("priority is invalid", *argv); + addattr16(n, 1024, IFLA_BRPORT_PRIORITY, priority); + } else if (matches(*argv, "cost") == 0) { + NEXT_ARG(); + if (get_u32(&cost, *argv, 0)) + invarg("cost is invalid", *argv); + addattr32(n, 1024, IFLA_BRPORT_COST, cost); + } else if (matches(*argv, "hairpin") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("hairpin", *argv, n, + IFLA_BRPORT_MODE); + } else if (matches(*argv, "guard") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("guard", *argv, n, + IFLA_BRPORT_GUARD); + } else if (matches(*argv, "root_block") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("root_block", *argv, n, + IFLA_BRPORT_PROTECT); + } else if (matches(*argv, "fastleave") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("fastleave", *argv, n, + IFLA_BRPORT_FAST_LEAVE); + } else if (matches(*argv, "learning") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("learning", *argv, n, + IFLA_BRPORT_LEARNING); + } else if (matches(*argv, "flood") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("flood", *argv, n, + IFLA_BRPORT_UNICAST_FLOOD); + } else if (matches(*argv, "mcast_flood") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("mcast_flood", *argv, n, + IFLA_BRPORT_MCAST_FLOOD); + } else if (matches(*argv, "mcast_to_unicast") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("mcast_to_unicast", *argv, n, + IFLA_BRPORT_MCAST_TO_UCAST); + } else if (matches(*argv, "proxy_arp") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("proxy_arp", *argv, n, + IFLA_BRPORT_PROXYARP); + } else if (matches(*argv, "proxy_arp_wifi") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("proxy_arp_wifi", *argv, n, + IFLA_BRPORT_PROXYARP_WIFI); + } else if (matches(*argv, "mcast_router") == 0) { + __u8 mcast_router; + + NEXT_ARG(); + if (get_u8(&mcast_router, *argv, 0)) + invarg("invalid mcast_router", *argv); + addattr8(n, 1024, IFLA_BRPORT_MULTICAST_ROUTER, + mcast_router); + } else if (matches(*argv, "mcast_fast_leave") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("mcast_fast_leave", *argv, n, + IFLA_BRPORT_FAST_LEAVE); + } else if (matches(*argv, "neigh_suppress") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("neigh_suppress", *argv, n, + IFLA_BRPORT_NEIGH_SUPPRESS); + } else if (matches(*argv, "group_fwd_mask") == 0) { + __u16 mask; + + NEXT_ARG(); + if (get_u16(&mask, *argv, 0)) + invarg("invalid group_fwd_mask", *argv); + addattr16(n, 1024, IFLA_BRPORT_GROUP_FWD_MASK, mask); + } else if (matches(*argv, "vlan_tunnel") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("vlan_tunnel", *argv, n, + IFLA_BRPORT_VLAN_TUNNEL); + } else if (matches(*argv, "isolated") == 0) { + NEXT_ARG(); + bridge_slave_parse_on_off("isolated", *argv, n, + IFLA_BRPORT_ISOLATED); + } else if (matches(*argv, "backup_port") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr32(n, 1024, IFLA_BRPORT_BACKUP_PORT, ifindex); + } else if (matches(*argv, "nobackup_port") == 0) { + addattr32(n, 1024, IFLA_BRPORT_BACKUP_PORT, 0); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "bridge_slave: unknown option \"%s\"?\n", + *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void bridge_slave_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util bridge_slave_link_util = { + .id = "bridge_slave", + .maxattr = IFLA_BRPORT_MAX, + .print_opt = bridge_slave_print_opt, + .parse_opt = bridge_slave_parse_opt, + .print_help = bridge_slave_print_help, + .parse_ifla_xstats = bridge_parse_xstats, + .print_ifla_xstats = bridge_print_xstats, +}; diff --git a/ip/iplink_can.c b/ip/iplink_can.c new file mode 100644 index 0000000..735ab94 --- /dev/null +++ b/ip/iplink_can.c @@ -0,0 +1,598 @@ +/* + * iplink_can.c CAN device support + * + * 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. + * + * Authors: Wolfgang Grandegger + */ + +#include +#include +#include + +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_usage(FILE *f) +{ + fprintf(f, + "Usage: ip link set DEVICE type can\n" + "\t[ bitrate BITRATE [ sample-point SAMPLE-POINT] ] |\n" + "\t[ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n \t phase-seg2 PHASE-SEG2 [ sjw SJW ] ]\n" + "\n" + "\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n" + "\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n" + "\n" + "\t[ loopback { on | off } ]\n" + "\t[ listen-only { on | off } ]\n" + "\t[ triple-sampling { on | off } ]\n" + "\t[ one-shot { on | off } ]\n" + "\t[ berr-reporting { on | off } ]\n" + "\t[ fd { on | off } ]\n" + "\t[ fd-non-iso { on | off } ]\n" + "\t[ presume-ack { on | off } ]\n" + "\n" + "\t[ restart-ms TIME-MS ]\n" + "\t[ restart ]\n" + "\n" + "\t[ termination { 0..65535 } ]\n" + "\n" + "\tWhere: BITRATE := { 1..1000000 }\n" + "\t SAMPLE-POINT := { 0.000..0.999 }\n" + "\t TQ := { NUMBER }\n" + "\t PROP-SEG := { 1..8 }\n" + "\t PHASE-SEG1 := { 1..8 }\n" + "\t PHASE-SEG2 := { 1..8 }\n" + "\t SJW := { 1..4 }\n" + "\t RESTART-MS := { 0 | NUMBER }\n" + ); +} + +static void usage(void) +{ + print_usage(stderr); +} + +static int get_float(float *val, const char *arg) +{ + float res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtof(arg, &ptr); + if (!ptr || ptr == arg || *ptr) + return -1; + *val = res; + return 0; +} + +static void set_ctrlmode(char *name, char *arg, + struct can_ctrlmode *cm, __u32 flags) +{ + if (strcmp(arg, "on") == 0) { + cm->flags |= flags; + } else if (strcmp(arg, "off") != 0) { + fprintf(stderr, + "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", + name, arg); + exit(-1); + } + cm->mask |= flags; +} + +static void print_ctrlmode(FILE *f, __u32 cm) +{ + open_json_array(PRINT_ANY, is_json_context() ? "ctrlmode" : "<"); +#define _PF(cmflag, cmname) \ + if (cm & cmflag) { \ + cm &= ~cmflag; \ + print_string(PRINT_ANY, NULL, cm ? "%s," : "%s", cmname); \ + } + _PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK"); + _PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY"); + _PF(CAN_CTRLMODE_3_SAMPLES, "TRIPLE-SAMPLING"); + _PF(CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT"); + _PF(CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING"); + _PF(CAN_CTRLMODE_FD, "FD"); + _PF(CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO"); + _PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK"); +#undef _PF + if (cm) + print_hex(PRINT_ANY, NULL, "%x", cm); + close_json_array(PRINT_ANY, "> "); +} + +static int can_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct can_bittiming bt = {}, dbt = {}; + struct can_ctrlmode cm = {0, 0}; + + while (argc > 0) { + if (matches(*argv, "bitrate") == 0) { + NEXT_ARG(); + if (get_u32(&bt.bitrate, *argv, 0)) + invarg("invalid \"bitrate\" value\n", *argv); + } else if (matches(*argv, "sample-point") == 0) { + float sp; + + NEXT_ARG(); + if (get_float(&sp, *argv)) + invarg("invalid \"sample-point\" value\n", + *argv); + bt.sample_point = (__u32)(sp * 1000); + } else if (matches(*argv, "tq") == 0) { + NEXT_ARG(); + if (get_u32(&bt.tq, *argv, 0)) + invarg("invalid \"tq\" value\n", *argv); + } else if (matches(*argv, "prop-seg") == 0) { + NEXT_ARG(); + if (get_u32(&bt.prop_seg, *argv, 0)) + invarg("invalid \"prop-seg\" value\n", *argv); + } else if (matches(*argv, "phase-seg1") == 0) { + NEXT_ARG(); + if (get_u32(&bt.phase_seg1, *argv, 0)) + invarg("invalid \"phase-seg1\" value\n", *argv); + } else if (matches(*argv, "phase-seg2") == 0) { + NEXT_ARG(); + if (get_u32(&bt.phase_seg2, *argv, 0)) + invarg("invalid \"phase-seg2\" value\n", *argv); + } else if (matches(*argv, "sjw") == 0) { + NEXT_ARG(); + if (get_u32(&bt.sjw, *argv, 0)) + invarg("invalid \"sjw\" value\n", *argv); + } else if (matches(*argv, "dbitrate") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.bitrate, *argv, 0)) + invarg("invalid \"dbitrate\" value\n", *argv); + } else if (matches(*argv, "dsample-point") == 0) { + float sp; + + NEXT_ARG(); + if (get_float(&sp, *argv)) + invarg("invalid \"dsample-point\" value\n", *argv); + dbt.sample_point = (__u32)(sp * 1000); + } else if (matches(*argv, "dtq") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.tq, *argv, 0)) + invarg("invalid \"dtq\" value\n", *argv); + } else if (matches(*argv, "dprop-seg") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.prop_seg, *argv, 0)) + invarg("invalid \"dprop-seg\" value\n", *argv); + } else if (matches(*argv, "dphase-seg1") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.phase_seg1, *argv, 0)) + invarg("invalid \"dphase-seg1\" value\n", *argv); + } else if (matches(*argv, "dphase-seg2") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.phase_seg2, *argv, 0)) + invarg("invalid \"dphase-seg2\" value\n", *argv); + } else if (matches(*argv, "dsjw") == 0) { + NEXT_ARG(); + if (get_u32(&dbt.sjw, *argv, 0)) + invarg("invalid \"dsjw\" value\n", *argv); + } else if (matches(*argv, "loopback") == 0) { + NEXT_ARG(); + set_ctrlmode("loopback", *argv, &cm, + CAN_CTRLMODE_LOOPBACK); + } else if (matches(*argv, "listen-only") == 0) { + NEXT_ARG(); + set_ctrlmode("listen-only", *argv, &cm, + CAN_CTRLMODE_LISTENONLY); + } else if (matches(*argv, "triple-sampling") == 0) { + NEXT_ARG(); + set_ctrlmode("triple-sampling", *argv, &cm, + CAN_CTRLMODE_3_SAMPLES); + } else if (matches(*argv, "one-shot") == 0) { + NEXT_ARG(); + set_ctrlmode("one-shot", *argv, &cm, + CAN_CTRLMODE_ONE_SHOT); + } else if (matches(*argv, "berr-reporting") == 0) { + NEXT_ARG(); + set_ctrlmode("berr-reporting", *argv, &cm, + CAN_CTRLMODE_BERR_REPORTING); + } else if (matches(*argv, "fd") == 0) { + NEXT_ARG(); + set_ctrlmode("fd", *argv, &cm, + CAN_CTRLMODE_FD); + } else if (matches(*argv, "fd-non-iso") == 0) { + NEXT_ARG(); + set_ctrlmode("fd-non-iso", *argv, &cm, + CAN_CTRLMODE_FD_NON_ISO); + } else if (matches(*argv, "presume-ack") == 0) { + NEXT_ARG(); + set_ctrlmode("presume-ack", *argv, &cm, + CAN_CTRLMODE_PRESUME_ACK); + } else if (matches(*argv, "restart") == 0) { + __u32 val = 1; + + addattr32(n, 1024, IFLA_CAN_RESTART, val); + } else if (matches(*argv, "restart-ms") == 0) { + __u32 val; + + NEXT_ARG(); + if (get_u32(&val, *argv, 0)) + invarg("invalid \"restart-ms\" value\n", *argv); + addattr32(n, 1024, IFLA_CAN_RESTART_MS, val); + } else if (matches(*argv, "termination") == 0) { + __u16 val; + + NEXT_ARG(); + if (get_u16(&val, *argv, 0)) + invarg("invalid \"termination\" value\n", + *argv); + addattr16(n, 1024, IFLA_CAN_TERMINATION, val); + } else if (matches(*argv, "help") == 0) { + usage(); + return -1; + } else { + fprintf(stderr, "can: unknown option \"%s\"\n", *argv); + usage(); + return -1; + } + argc--, argv++; + } + + if (bt.bitrate || bt.tq) + addattr_l(n, 1024, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); + if (dbt.bitrate || dbt.tq) + addattr_l(n, 1024, IFLA_CAN_DATA_BITTIMING, &dbt, sizeof(dbt)); + if (cm.mask) + addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); + + return 0; +} + +static const char *can_state_names[CAN_STATE_MAX] = { + [CAN_STATE_ERROR_ACTIVE] = "ERROR-ACTIVE", + [CAN_STATE_ERROR_WARNING] = "ERROR-WARNING", + [CAN_STATE_ERROR_PASSIVE] = "ERROR-PASSIVE", + [CAN_STATE_BUS_OFF] = "BUS-OFF", + [CAN_STATE_STOPPED] = "STOPPED", + [CAN_STATE_SLEEPING] = "SLEEPING" +}; + +static void can_print_json_timing_min_max(const char *attr, int min, int max) +{ + open_json_object(attr); + print_int(PRINT_JSON, "min", NULL, min); + print_int(PRINT_JSON, "max", NULL, max); + close_json_object(); +} + +static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm = RTA_DATA(tb[IFLA_CAN_CTRLMODE]); + + if (cm->flags) + print_ctrlmode(f, cm->flags); + } + + if (tb[IFLA_CAN_STATE]) { + uint32_t state = rta_getattr_u32(tb[IFLA_CAN_STATE]); + + print_string(PRINT_ANY, "state", "state %s ", state < CAN_STATE_MAX ? + can_state_names[state] : "UNKNOWN"); + } + + if (tb[IFLA_CAN_BERR_COUNTER]) { + struct can_berr_counter *bc = + RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]); + + if (is_json_context()) { + open_json_object("berr_counter"); + print_int(PRINT_JSON, "tx", NULL, bc->txerr); + print_int(PRINT_JSON, "rx", NULL, bc->rxerr); + close_json_object(); + } else { + fprintf(f, "(berr-counter tx %d rx %d) ", + bc->txerr, bc->rxerr); + } + } + + if (tb[IFLA_CAN_RESTART_MS]) { + __u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]); + + print_int(PRINT_ANY, + "restart_ms", + "restart-ms %d ", + *restart_ms); + } + + /* bittiming is irrelevant if fixed bitrate is defined */ + if (tb[IFLA_CAN_BITTIMING] && !tb[IFLA_CAN_BITRATE_CONST]) { + struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]); + + if (is_json_context()) { + json_writer_t *jw; + + open_json_object("bittiming"); + print_int(PRINT_ANY, "bitrate", NULL, bt->bitrate); + jw = get_json_writer(); + jsonw_name(jw, "sample_point"); + jsonw_printf(jw, "%.3f", + (float) bt->sample_point / 1000); + print_int(PRINT_ANY, "tq", NULL, bt->tq); + print_int(PRINT_ANY, "prop_seg", NULL, bt->prop_seg); + print_int(PRINT_ANY, "phase_seg1", + NULL, bt->phase_seg1); + print_int(PRINT_ANY, "phase_seg2", + NULL, bt->phase_seg2); + print_int(PRINT_ANY, "sjw", NULL, bt->sjw); + close_json_object(); + } else { + fprintf(f, "\n bitrate %d sample-point %.3f ", + bt->bitrate, (float) bt->sample_point / 1000.); + fprintf(f, + "\n tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d", + bt->tq, bt->prop_seg, + bt->phase_seg1, bt->phase_seg2, + bt->sjw); + } + } + + /* bittiming const is irrelevant if fixed bitrate is defined */ + if (tb[IFLA_CAN_BITTIMING_CONST] && !tb[IFLA_CAN_BITRATE_CONST]) { + struct can_bittiming_const *btc = + RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]); + + if (is_json_context()) { + open_json_object("bittiming_const"); + print_string(PRINT_JSON, "name", NULL, btc->name); + can_print_json_timing_min_max("tseg1", + btc->tseg1_min, + btc->tseg1_max); + can_print_json_timing_min_max("tseg2", + btc->tseg2_min, + btc->tseg2_max); + can_print_json_timing_min_max("sjw", 1, btc->sjw_max); + can_print_json_timing_min_max("brp", + btc->brp_min, + btc->brp_max); + print_int(PRINT_JSON, "brp_inc", NULL, btc->brp_inc); + close_json_object(); + } else { + fprintf(f, "\n %s: tseg1 %d..%d tseg2 %d..%d " + "sjw 1..%d brp %d..%d brp-inc %d", + btc->name, btc->tseg1_min, btc->tseg1_max, + btc->tseg2_min, btc->tseg2_max, btc->sjw_max, + btc->brp_min, btc->brp_max, btc->brp_inc); + } + } + + if (tb[IFLA_CAN_BITRATE_CONST]) { + __u32 *bitrate_const = RTA_DATA(tb[IFLA_CAN_BITRATE_CONST]); + int bitrate_cnt = RTA_PAYLOAD(tb[IFLA_CAN_BITRATE_CONST]) / + sizeof(*bitrate_const); + int i; + __u32 bitrate = 0; + + if (tb[IFLA_CAN_BITTIMING]) { + struct can_bittiming *bt = + RTA_DATA(tb[IFLA_CAN_BITTIMING]); + bitrate = bt->bitrate; + } + + if (is_json_context()) { + print_uint(PRINT_JSON, + "bittiming_bitrate", + NULL, bitrate); + open_json_array(PRINT_JSON, "bitrate_const"); + for (i = 0; i < bitrate_cnt; ++i) + print_uint(PRINT_JSON, NULL, NULL, + bitrate_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n bitrate %u", bitrate); + fprintf(f, "\n ["); + + for (i = 0; i < bitrate_cnt - 1; ++i) { + /* This will keep lines below 80 signs */ + if (!(i % 6) && i) + fprintf(f, "\n "); + + fprintf(f, "%8u, ", bitrate_const[i]); + } + + if (!(i % 6) && i) + fprintf(f, "\n "); + fprintf(f, "%8u ]", bitrate_const[i]); + } + } + + /* data bittiming is irrelevant if fixed bitrate is defined */ + if (tb[IFLA_CAN_DATA_BITTIMING] && !tb[IFLA_CAN_DATA_BITRATE_CONST]) { + struct can_bittiming *dbt = + RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); + + if (is_json_context()) { + json_writer_t *jw; + + open_json_object("data_bittiming"); + print_int(PRINT_JSON, "bitrate", NULL, dbt->bitrate); + jw = get_json_writer(); + jsonw_name(jw, "sample_point"); + jsonw_printf(jw, "%.3f", + (float) dbt->sample_point / 1000.); + print_int(PRINT_JSON, "tq", NULL, dbt->tq); + print_int(PRINT_JSON, "prop_seg", NULL, dbt->prop_seg); + print_int(PRINT_JSON, "phase_seg1", + NULL, dbt->phase_seg1); + print_int(PRINT_JSON, "phase_seg2", + NULL, dbt->phase_seg2); + print_int(PRINT_JSON, "sjw", NULL, dbt->sjw); + close_json_object(); + } else { + fprintf(f, "\n dbitrate %d dsample-point %.3f ", + dbt->bitrate, + (float) dbt->sample_point / 1000.); + fprintf(f, "\n dtq %d dprop-seg %d dphase-seg1 %d " + "dphase-seg2 %d dsjw %d", + dbt->tq, dbt->prop_seg, dbt->phase_seg1, + dbt->phase_seg2, dbt->sjw); + } + } + + /* data bittiming const is irrelevant if fixed bitrate is defined */ + if (tb[IFLA_CAN_DATA_BITTIMING_CONST] && + !tb[IFLA_CAN_DATA_BITRATE_CONST]) { + struct can_bittiming_const *dbtc = + RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]); + + if (is_json_context()) { + open_json_object("data_bittiming_const"); + print_string(PRINT_JSON, "name", NULL, dbtc->name); + can_print_json_timing_min_max("tseg1", + dbtc->tseg1_min, + dbtc->tseg1_max); + can_print_json_timing_min_max("tseg2", + dbtc->tseg2_min, + dbtc->tseg2_max); + can_print_json_timing_min_max("sjw", 1, dbtc->sjw_max); + can_print_json_timing_min_max("brp", + dbtc->brp_min, + dbtc->brp_max); + + print_int(PRINT_JSON, "brp_inc", NULL, dbtc->brp_inc); + close_json_object(); + } else { + fprintf(f, "\n %s: dtseg1 %d..%d dtseg2 %d..%d " + "dsjw 1..%d dbrp %d..%d dbrp-inc %d", + dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max, + dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max, + dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc); + } + } + + if (tb[IFLA_CAN_DATA_BITRATE_CONST]) { + __u32 *dbitrate_const = + RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]); + int dbitrate_cnt = + RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) / + sizeof(*dbitrate_const); + int i; + __u32 dbitrate = 0; + + if (tb[IFLA_CAN_DATA_BITTIMING]) { + struct can_bittiming *dbt = + RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); + dbitrate = dbt->bitrate; + } + + if (is_json_context()) { + print_uint(PRINT_JSON, "data_bittiming_bitrate", + NULL, dbitrate); + open_json_array(PRINT_JSON, "data_bitrate_const"); + for (i = 0; i < dbitrate_cnt; ++i) + print_uint(PRINT_JSON, NULL, NULL, + dbitrate_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n dbitrate %u", dbitrate); + fprintf(f, "\n ["); + + for (i = 0; i < dbitrate_cnt - 1; ++i) { + /* This will keep lines below 80 signs */ + if (!(i % 6) && i) + fprintf(f, "\n "); + + fprintf(f, "%8u, ", dbitrate_const[i]); + } + + if (!(i % 6) && i) + fprintf(f, "\n "); + fprintf(f, "%8u ]", dbitrate_const[i]); + } + } + + if (tb[IFLA_CAN_TERMINATION_CONST] && tb[IFLA_CAN_TERMINATION]) { + __u16 *trm = RTA_DATA(tb[IFLA_CAN_TERMINATION]); + __u16 *trm_const = RTA_DATA(tb[IFLA_CAN_TERMINATION_CONST]); + int trm_cnt = RTA_PAYLOAD(tb[IFLA_CAN_TERMINATION_CONST]) / + sizeof(*trm_const); + int i; + + if (is_json_context()) { + print_hu(PRINT_JSON, "termination", NULL, *trm); + open_json_array(PRINT_JSON, "termination_const"); + for (i = 0; i < trm_cnt; ++i) + print_hu(PRINT_JSON, NULL, NULL, trm_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n termination %hu [ ", *trm); + + for (i = 0; i < trm_cnt - 1; ++i) + fprintf(f, "%hu, ", trm_const[i]); + + fprintf(f, "%hu ]", trm_const[i]); + } + } + + if (tb[IFLA_CAN_CLOCK]) { + struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]); + + print_int(PRINT_ANY, + "clock", + "\n clock %d ", + clock->freq); + } + +} + +static void can_print_xstats(struct link_util *lu, + FILE *f, struct rtattr *xstats) +{ + struct can_device_stats *stats; + + if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) { + stats = RTA_DATA(xstats); + + if (is_json_context()) { + print_int(PRINT_JSON, "restarts", + NULL, stats->restarts); + print_int(PRINT_JSON, "bus_error", + NULL, stats->bus_error); + print_int(PRINT_JSON, "arbitration_lost", + NULL, stats->arbitration_lost); + print_int(PRINT_JSON, "error_warning", + NULL, stats->error_warning); + print_int(PRINT_JSON, "error_passive", + NULL, stats->error_passive); + print_int(PRINT_JSON, "bus_off", NULL, stats->bus_off); + } else { + fprintf(f, "\n re-started bus-errors arbit-lost " + "error-warn error-pass bus-off"); + fprintf(f, "\n %-10d %-10d %-10d %-10d %-10d %-10d", + stats->restarts, stats->bus_error, + stats->arbitration_lost, stats->error_warning, + stats->error_passive, stats->bus_off); + } + } +} + +static void can_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_usage(f); +} + +struct link_util can_link_util = { + .id = "can", + .maxattr = IFLA_CAN_MAX, + .parse_opt = can_parse_opt, + .print_opt = can_print_opt, + .print_xstats = can_print_xstats, + .print_help = can_print_help, +}; diff --git a/ip/iplink_dummy.c b/ip/iplink_dummy.c new file mode 100644 index 0000000..cba2295 --- /dev/null +++ b/ip/iplink_dummy.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void dummy_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... dummy\n"); +} + +struct link_util dummy_link_util = { + .id = "dummy", + .print_help = dummy_print_help, +}; diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c new file mode 100644 index 0000000..9299236 --- /dev/null +++ b/ip/iplink_geneve.c @@ -0,0 +1,386 @@ +/* + * iplink_geneve.c GENEVE device support + * + * 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. + * + * Authors: John W. Linville + */ + +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define GENEVE_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0) + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... geneve id VNI\n" + " remote ADDR\n" + " [ ttl TTL ]\n" + " [ tos TOS ]\n" + " [ df DF ]\n" + " [ flowlabel LABEL ]\n" + " [ dstport PORT ]\n" + " [ [no]external ]\n" + " [ [no]udpcsum ]\n" + " [ [no]udp6zerocsumtx ]\n" + " [ [no]udp6zerocsumrx ]\n" + "\n" + "Where: VNI := 0-16777215\n" + " ADDR := IP_ADDRESS\n" + " TOS := { NUMBER | inherit }\n" + " TTL := { 1..255 | auto | inherit }\n" + " DF := { unset | set | inherit }\n" + " LABEL := 0-1048575\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static void check_duparg(__u64 *attrs, int type, const char *key, + const char *argv) +{ + if (!GENEVE_ATTRSET(*attrs, type)) { + *attrs |= (1L << type); + return; + } + duparg2(key, argv); +} + +static int geneve_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + inet_prefix daddr; + __u32 vni = 0; + __u32 label = 0; + __u8 ttl = 0; + __u8 tos = 0; + __u16 dstport = 0; + bool metadata = 0; + __u8 udpcsum = 0; + __u8 udp6zerocsumtx = 0; + __u8 udp6zerocsumrx = 0; + __u64 attrs = 0; + bool set_op = (n->nlmsg_type == RTM_NEWLINK && + !(n->nlmsg_flags & NLM_F_CREATE)); + + inet_prefix_reset(&daddr); + + while (argc > 0) { + if (!matches(*argv, "id") || + !matches(*argv, "vni")) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv); + if (get_u32(&vni, *argv, 0) || + vni >= 1u << 24) + invarg("invalid id", *argv); + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote", + *argv); + get_addr(&daddr, *argv, AF_UNSPEC); + if (!is_addrtype_inet_not_multi(&daddr)) + invarg("invalid remote address", *argv); + } else if (!matches(*argv, "ttl") || + !matches(*argv, "hoplimit")) { + unsigned int uval; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_TTL, "ttl", *argv); + if (strcmp(*argv, "inherit") == 0) { + addattr8(n, 1024, IFLA_GENEVE_TTL_INHERIT, 1); + } else if (strcmp(*argv, "auto") != 0) { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL", *argv); + if (uval > 255) + invarg("TTL must be <= 255", *argv); + ttl = uval; + } + } else if (!matches(*argv, "tos") || + !matches(*argv, "dsfield")) { + __u32 uval; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_TOS, "tos", *argv); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + tos = uval; + } else + tos = 1; + } else if (!matches(*argv, "df")) { + enum ifla_geneve_df df; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_DF, "df", *argv); + if (strcmp(*argv, "unset") == 0) + df = GENEVE_DF_UNSET; + else if (strcmp(*argv, "set") == 0) + df = GENEVE_DF_SET; + else if (strcmp(*argv, "inherit") == 0) + df = GENEVE_DF_INHERIT; + else + invarg("DF must be 'unset', 'set' or 'inherit'", + *argv); + + addattr8(n, 1024, IFLA_GENEVE_DF, df); + } else if (!matches(*argv, "label") || + !matches(*argv, "flowlabel")) { + __u32 uval; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_LABEL, "flowlabel", + *argv); + if (get_u32(&uval, *argv, 0) || + (uval & ~LABEL_MAX_MASK)) + invarg("invalid flowlabel", *argv); + label = htonl(uval); + } else if (!matches(*argv, "dstport")) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_GENEVE_PORT, "dstport", + *argv); + if (get_u16(&dstport, *argv, 0)) + invarg("dstport", *argv); + } else if (!matches(*argv, "external")) { + check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA, + *argv, *argv); + metadata = true; + } else if (!matches(*argv, "noexternal")) { + check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA, + *argv, *argv); + metadata = false; + } else if (!matches(*argv, "udpcsum")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv, + *argv); + udpcsum = 1; + } else if (!matches(*argv, "noudpcsum")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv, + *argv); + udpcsum = 0; + } else if (!matches(*argv, "udp6zerocsumtx")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + *argv, *argv); + udp6zerocsumtx = 1; + } else if (!matches(*argv, "noudp6zerocsumtx")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + *argv, *argv); + udp6zerocsumtx = 0; + } else if (!matches(*argv, "udp6zerocsumrx")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + *argv, *argv); + udp6zerocsumrx = 1; + } else if (!matches(*argv, "noudp6zerocsumrx")) { + check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + *argv, *argv); + udp6zerocsumrx = 0; + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "geneve: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + if (metadata && GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) { + fprintf(stderr, "geneve: both 'external' and vni cannot be specified\n"); + return -1; + } + + if (!metadata) { + /* parameter checking make sense only for full geneve tunnels */ + if (!GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) { + fprintf(stderr, "geneve: missing virtual network identifier\n"); + return -1; + } + + /* If we are modifying the geneve device, then we only need the + * ID (VNI) to identify the geneve device, and we do not need + * the remote IP. + */ + if (!set_op && !is_addrtype_inet(&daddr)) { + fprintf(stderr, "geneve: remote link partner not specified\n"); + return -1; + } + } + + addattr32(n, 1024, IFLA_GENEVE_ID, vni); + if (is_addrtype_inet(&daddr)) { + int type = (daddr.family == AF_INET) ? IFLA_GENEVE_REMOTE : + IFLA_GENEVE_REMOTE6; + addattr_l(n, 1024, type, daddr.data, daddr.bytelen); + } + if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL)) + addattr32(n, 1024, IFLA_GENEVE_LABEL, label); + if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL)) + addattr8(n, 1024, IFLA_GENEVE_TTL, ttl); + if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS)) + addattr8(n, 1024, IFLA_GENEVE_TOS, tos); + if (dstport) + addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport)); + if (metadata) + addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA); + if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM)) + addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum); + if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX)) + addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); + if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX)) + addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); + + return 0; +} + +static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + __u32 vni; + __u8 ttl = 0; + __u8 tos = 0; + + if (!tb) + return; + + if (tb[IFLA_GENEVE_COLLECT_METADATA]) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + if (!tb[IFLA_GENEVE_ID] || + RTA_PAYLOAD(tb[IFLA_GENEVE_ID]) < sizeof(__u32)) + return; + + vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]); + print_uint(PRINT_ANY, "id", "id %u ", vni); + + if (tb[IFLA_GENEVE_REMOTE]) { + __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); + + if (addr) + print_string(PRINT_ANY, + "remote", + "remote %s ", + format_host(AF_INET, 4, &addr)); + } else if (tb[IFLA_GENEVE_REMOTE6]) { + struct in6_addr addr; + + memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { + if (!IN6_IS_ADDR_MULTICAST(&addr)) + print_string(PRINT_ANY, + "remote6", + "remote %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); + } + } + + if (tb[IFLA_GENEVE_TTL_INHERIT] && + rta_getattr_u8(tb[IFLA_GENEVE_TTL_INHERIT])) { + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + } else if (tb[IFLA_GENEVE_TTL]) { + ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); + else + print_string(PRINT_FP, NULL, "ttl %s ", "auto"); + } + + if (tb[IFLA_GENEVE_TOS]) + tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]); + if (tos) { + if (is_json_context() || tos != 1) + print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); + else + print_string(PRINT_FP, NULL, "tos %s ", "inherit"); + } + + if (tb[IFLA_GENEVE_DF]) { + enum ifla_geneve_df df = rta_getattr_u8(tb[IFLA_GENEVE_DF]); + + if (df == GENEVE_DF_UNSET) + print_string(PRINT_JSON, "df", "df %s ", "unset"); + else if (df == GENEVE_DF_SET) + print_string(PRINT_ANY, "df", "df %s ", "set"); + else if (df == GENEVE_DF_INHERIT) + print_string(PRINT_ANY, "df", "df %s ", "inherit"); + } + + if (tb[IFLA_GENEVE_LABEL]) { + __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); + + if (label) + print_0xhex(PRINT_ANY, + "label", "flowlabel %#llx ", + ntohl(label)); + } + + if (tb[IFLA_GENEVE_PORT]) + print_uint(PRINT_ANY, + "port", + "dstport %u ", + rta_getattr_be16(tb[IFLA_GENEVE_PORT])); + + if (tb[IFLA_GENEVE_UDP_CSUM]) { + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_csum", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) + fputs("no", f); + fputs("udpcsum ", f); + } + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_zero_csum6_tx", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) + fputs("no", f); + fputs("udp6zerocsumtx ", f); + } + } + + if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_zero_csum6_rx", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) + fputs("no", f); + fputs("udp6zerocsumrx ", f); + } + } +} + +static void geneve_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util geneve_link_util = { + .id = "geneve", + .maxattr = IFLA_GENEVE_MAX, + .parse_opt = geneve_parse_opt, + .print_opt = geneve_print_opt, + .print_help = geneve_print_help, +}; diff --git a/ip/iplink_hsr.c b/ip/iplink_hsr.c new file mode 100644 index 0000000..da2d03d --- /dev/null +++ b/ip/iplink_hsr.c @@ -0,0 +1,170 @@ +/* + * iplink_hsr.c HSR device support + * + * 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. + * + * Authors: Arvid Brodin + * + * Based on iplink_vlan.c by Patrick McHardy + */ + +#include +#include +#include +#include /* Needed by linux/if.h for some reason */ +#include +#include +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_usage(FILE *f) +{ + fprintf(f, + "Usage:\tip link add name NAME type hsr slave1 SLAVE1-IF slave2 SLAVE2-IF\n" + "\t[ supervision ADDR-BYTE ] [version VERSION] [proto PROTOCOL]\n" + "\n" + "NAME\n" + " name of new hsr device (e.g. hsr0)\n" + "SLAVE1-IF, SLAVE2-IF\n" + " the two slave devices bound to the HSR device\n" + "ADDR-BYTE\n" + " 0-255; the last byte of the multicast address used for HSR supervision\n" + " frames (default = 0)\n" + "VERSION\n" + " 0,1; the protocol version to be used. (default = 0)\n" + "PROTOCOL\n" + " 0 - HSR, 1 - PRP. (default = 0 - HSR)\n"); +} + +static void usage(void) +{ + print_usage(stderr); +} + +static int hsr_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + int ifindex; + unsigned char multicast_spec; + unsigned char protocol_version; + unsigned char protocol = HSR_PROTOCOL_HSR; + + while (argc > 0) { + if (matches(*argv, "supervision") == 0) { + NEXT_ARG(); + if (get_u8(&multicast_spec, *argv, 0)) + invarg("ADDR-BYTE is invalid", *argv); + addattr_l(n, 1024, IFLA_HSR_MULTICAST_SPEC, + &multicast_spec, 1); + } else if (matches(*argv, "version") == 0) { + NEXT_ARG(); + if (!(get_u8(&protocol_version, *argv, 0) == 0 || + get_u8(&protocol_version, *argv, 0) == 1)) + invarg("version is invalid", *argv); + addattr_l(n, 1024, IFLA_HSR_VERSION, + &protocol_version, 1); + } else if (matches(*argv, "proto") == 0) { + NEXT_ARG(); + if (!(get_u8(&protocol, *argv, 0) == HSR_PROTOCOL_HSR || + get_u8(&protocol, *argv, 0) == HSR_PROTOCOL_PRP)) + invarg("protocol is invalid", *argv); + addattr_l(n, 1024, IFLA_HSR_PROTOCOL, + &protocol, 1); + } else if (matches(*argv, "slave1") == 0) { + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) + invarg("No such interface", *argv); + addattr_l(n, 1024, IFLA_HSR_SLAVE1, &ifindex, 4); + } else if (matches(*argv, "slave2") == 0) { + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) + invarg("No such interface", *argv); + addattr_l(n, 1024, IFLA_HSR_SLAVE2, &ifindex, 4); + } else if (matches(*argv, "help") == 0) { + usage(); + return -1; + } else { + fprintf(stderr, "hsr: what is \"%s\"?\n", *argv); + usage(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void hsr_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + SPRINT_BUF(b1); + + if (!tb) + return; + + if (tb[IFLA_HSR_SLAVE1] && + RTA_PAYLOAD(tb[IFLA_HSR_SLAVE1]) < sizeof(__u32)) + return; + if (tb[IFLA_HSR_SLAVE2] && + RTA_PAYLOAD(tb[IFLA_HSR_SLAVE2]) < sizeof(__u32)) + return; + if (tb[IFLA_HSR_SEQ_NR] && + RTA_PAYLOAD(tb[IFLA_HSR_SEQ_NR]) < sizeof(__u16)) + return; + if (tb[IFLA_HSR_SUPERVISION_ADDR] && + RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]) < ETH_ALEN) + return; + + if (tb[IFLA_HSR_SLAVE1]) + print_string(PRINT_ANY, + "slave1", + "slave1 %s ", + ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1]))); + else + print_null(PRINT_ANY, "slave1", "slave1 %s ", ""); + + if (tb[IFLA_HSR_SLAVE2]) + print_string(PRINT_ANY, + "slave2", + "slave2 %s ", + ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2]))); + else + print_null(PRINT_ANY, "slave2", "slave2 %s ", ""); + + if (tb[IFLA_HSR_SEQ_NR]) + print_int(PRINT_ANY, + "seq_nr", + "sequence %d ", + rta_getattr_u16(tb[IFLA_HSR_SEQ_NR])); + + if (tb[IFLA_HSR_SUPERVISION_ADDR]) + print_string(PRINT_ANY, + "supervision_addr", + "supervision %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]), + RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]), + ARPHRD_VOID, + b1, sizeof(b1))); + if (tb[IFLA_HSR_PROTOCOL]) + print_hhu(PRINT_ANY, "proto", "proto %hhu ", + rta_getattr_u8(tb[IFLA_HSR_PROTOCOL])); +} + +static void hsr_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_usage(f); +} + +struct link_util hsr_link_util = { + .id = "hsr", + .maxattr = IFLA_HSR_MAX, + .parse_opt = hsr_parse_opt, + .print_opt = hsr_print_opt, + .print_help = hsr_print_help, +}; diff --git a/ip/iplink_ifb.c b/ip/iplink_ifb.c new file mode 100644 index 0000000..a2a7301 --- /dev/null +++ b/ip/iplink_ifb.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void ifb_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... ifb\n"); +} + +struct link_util ifb_link_util = { + .id = "ifb", + .print_help = ifb_print_help, +}; diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c new file mode 100644 index 0000000..b730c53 --- /dev/null +++ b/ip/iplink_ipoib.c @@ -0,0 +1,145 @@ +/* + * iplink_ipoib.c IPoIB device support + * + * 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. + * + * Authors: Or Gerlitz + * copied iflink_vlan.c authored by Patrick McHardy + */ + +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... ipoib [ pkey PKEY ]\n" + " [ mode {datagram | connected} ]\n" + " [ umcast {0|1} ]\n" + "\n" + "PKEY := 0x8001-0xffff\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static int mode_arg(void) +{ + fprintf(stderr, "Error: argument of \"mode\" must be \"datagram\"or \"connected\"\n"); + return -1; +} + +static int ipoib_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u16 pkey, mode, umcast; + + while (argc > 0) { + if (matches(*argv, "pkey") == 0) { + NEXT_ARG(); + if (get_u16(&pkey, *argv, 0)) + invarg("pkey is invalid", *argv); + addattr_l(n, 1024, IFLA_IPOIB_PKEY, &pkey, 2); + } else if (matches(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "datagram") == 0) + mode = IPOIB_MODE_DATAGRAM; + else if (strcmp(*argv, "connected") == 0) + mode = IPOIB_MODE_CONNECTED; + else + return mode_arg(); + addattr_l(n, 1024, IFLA_IPOIB_MODE, &mode, 2); + } else if (matches(*argv, "umcast") == 0) { + NEXT_ARG(); + if (get_u16(&umcast, *argv, 0)) + invarg("umcast is invalid", *argv); + addattr_l(n, 1024, IFLA_IPOIB_UMCAST, &umcast, 2); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "ipoib: unknown option \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void ipoib_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + __u16 mode; + + if (!tb) + return; + + if (!tb[IFLA_IPOIB_PKEY] || + RTA_PAYLOAD(tb[IFLA_IPOIB_PKEY]) < sizeof(__u16)) + return; + + __u16 pkey = rta_getattr_u16(tb[IFLA_IPOIB_PKEY]); + + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%#.4x", pkey); + print_string(PRINT_JSON, "key", NULL, b1); + } else { + fprintf(f, "pkey %#.4x ", pkey); + } + + if (!tb[IFLA_IPOIB_MODE] || + RTA_PAYLOAD(tb[IFLA_IPOIB_MODE]) < sizeof(__u16)) + return; + + mode = rta_getattr_u16(tb[IFLA_IPOIB_MODE]); + + const char *mode_str = + mode == IPOIB_MODE_DATAGRAM ? "datagram" : + mode == IPOIB_MODE_CONNECTED ? "connected" : "unknown"; + + print_string(PRINT_ANY, "mode", "mode %s ", mode_str); + + if (!tb[IFLA_IPOIB_UMCAST] || + RTA_PAYLOAD(tb[IFLA_IPOIB_UMCAST]) < sizeof(__u16)) + return; + + __u16 umcast = rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]); + + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%.4x", umcast); + print_string(PRINT_JSON, "umcast", NULL, b1); + } else { + fprintf(f, "umcast %.4x ", umcast); + } +} + +static void ipoib_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util ipoib_link_util = { + .id = "ipoib", + .maxattr = IFLA_IPOIB_MAX, + .parse_opt = ipoib_parse_opt, + .print_opt = ipoib_print_opt, + .print_help = ipoib_print_help, +}; diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c new file mode 100644 index 0000000..baae767 --- /dev/null +++ b/ip/iplink_ipvlan.c @@ -0,0 +1,133 @@ +/* iplink_ipvlan.c IPVLAN/IPVTAP device support + * + * 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. + * + * Authors: Mahesh Bandewar + */ + +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_explain(struct link_util *lu, FILE *f) +{ + fprintf(f, + "Usage: ... %s [ mode MODE ] [ FLAGS ]\n" + "\n" + "MODE: l3 | l3s | l2\n" + "FLAGS: bridge | private | vepa\n" + "(first values are the defaults if nothing is specified).\n", + lu->id); +} + +static int ipvlan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u16 flags = 0; + bool mflag_given = false; + + while (argc > 0) { + if (matches(*argv, "mode") == 0) { + __u16 mode = 0; + + NEXT_ARG(); + + if (strcmp(*argv, "l2") == 0) + mode = IPVLAN_MODE_L2; + else if (strcmp(*argv, "l3") == 0) + mode = IPVLAN_MODE_L3; + else if (strcmp(*argv, "l3s") == 0) + mode = IPVLAN_MODE_L3S; + else { + fprintf(stderr, "Error: argument of \"mode\" must be either \"l2\", \"l3\" or \"l3s\"\n"); + return -1; + } + addattr16(n, 1024, IFLA_IPVLAN_MODE, mode); + } else if (matches(*argv, "private") == 0 && !mflag_given) { + flags |= IPVLAN_F_PRIVATE; + mflag_given = true; + } else if (matches(*argv, "vepa") == 0 && !mflag_given) { + flags |= IPVLAN_F_VEPA; + mflag_given = true; + } else if (matches(*argv, "bridge") == 0 && !mflag_given) { + mflag_given = true; + } else if (matches(*argv, "help") == 0) { + print_explain(lu, stderr); + return -1; + } else { + fprintf(stderr, "%s: unknown option \"%s\"?\n", + lu->id, *argv); + print_explain(lu, stderr); + return -1; + } + argc--; + argv++; + } + addattr16(n, 1024, IFLA_IPVLAN_FLAGS, flags); + + return 0; +} + +static void ipvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + + if (!tb) + return; + + if (tb[IFLA_IPVLAN_MODE]) { + if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) { + __u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]); + const char *mode_str = mode == IPVLAN_MODE_L2 ? "l2" : + mode == IPVLAN_MODE_L3 ? "l3" : + mode == IPVLAN_MODE_L3S ? "l3s" : "unknown"; + + print_string(PRINT_ANY, "mode", " mode %s ", mode_str); + } + } + if (tb[IFLA_IPVLAN_FLAGS]) { + if (RTA_PAYLOAD(tb[IFLA_IPVLAN_FLAGS]) == sizeof(__u16)) { + __u16 flags = rta_getattr_u16(tb[IFLA_IPVLAN_FLAGS]); + + if (flags & IPVLAN_F_PRIVATE) + print_bool(PRINT_ANY, "private", "private ", + true); + else if (flags & IPVLAN_F_VEPA) + print_bool(PRINT_ANY, "vepa", "vepa ", + true); + else + print_bool(PRINT_ANY, "bridge", "bridge ", + true); + } + } +} + +static void ipvlan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(lu, f); +} + +struct link_util ipvlan_link_util = { + .id = "ipvlan", + .maxattr = IFLA_IPVLAN_MAX, + .parse_opt = ipvlan_parse_opt, + .print_opt = ipvlan_print_opt, + .print_help = ipvlan_print_help, +}; + +struct link_util ipvtap_link_util = { + .id = "ipvtap", + .maxattr = IFLA_IPVLAN_MAX, + .parse_opt = ipvlan_parse_opt, + .print_opt = ipvlan_print_opt, + .print_help = ipvlan_print_help, +}; diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c new file mode 100644 index 0000000..b966a61 --- /dev/null +++ b/ip/iplink_macvlan.c @@ -0,0 +1,274 @@ +/* + * iplink_macvlan.c macvlan/macvtap device support + * + * 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. + * + * Authors: Patrick McHardy + * Arnd Bergmann + */ + +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define pfx_err(lu, ...) { \ + fprintf(stderr, "%s: ", lu->id); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ +} + +static void print_explain(struct link_util *lu, FILE *f) +{ + fprintf(f, + "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n" + "\n" + "MODE: private | vepa | bridge | passthru | source\n" + "MODE_FLAG: null | nopromisc\n" + "MODE_OPTS: for mode \"source\":\n" + "\tmacaddr { { add | del } | set [ [ ... ] ] | flush }\n", + lu->id + ); +} + +static void explain(struct link_util *lu) +{ + print_explain(lu, stderr); +} + + +static int mode_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n", + arg); + return -1; +} + +static int flag_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n", + arg); + return -1; +} + +static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u32 mode = 0; + __u16 flags = 0; + __u32 mac_mode = 0; + int has_flags = 0; + char mac[ETH_ALEN]; + struct rtattr *nmac; + + while (argc > 0) { + if (matches(*argv, "mode") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "private") == 0) + mode = MACVLAN_MODE_PRIVATE; + else if (strcmp(*argv, "vepa") == 0) + mode = MACVLAN_MODE_VEPA; + else if (strcmp(*argv, "bridge") == 0) + mode = MACVLAN_MODE_BRIDGE; + else if (strcmp(*argv, "passthru") == 0) + mode = MACVLAN_MODE_PASSTHRU; + else if (strcmp(*argv, "source") == 0) + mode = MACVLAN_MODE_SOURCE; + else + return mode_arg(*argv); + } else if (matches(*argv, "flag") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "nopromisc") == 0) + flags |= MACVLAN_FLAG_NOPROMISC; + else if (strcmp(*argv, "null") == 0) + flags |= 0; + else + return flag_arg(*argv); + + has_flags = 1; + + } else if (matches(*argv, "macaddr") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "add") == 0) { + mac_mode = MACVLAN_MACADDR_ADD; + } else if (strcmp(*argv, "del") == 0) { + mac_mode = MACVLAN_MACADDR_DEL; + } else if (strcmp(*argv, "set") == 0) { + mac_mode = MACVLAN_MACADDR_SET; + } else if (strcmp(*argv, "flush") == 0) { + mac_mode = MACVLAN_MACADDR_FLUSH; + } else { + explain(lu); + return -1; + } + + addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode); + + if (mac_mode == MACVLAN_MACADDR_ADD || + mac_mode == MACVLAN_MACADDR_DEL) { + NEXT_ARG(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) + return -1; + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac, + ETH_ALEN); + } + + if (mac_mode == MACVLAN_MACADDR_SET) { + nmac = addattr_nest(n, 1024, + IFLA_MACVLAN_MACADDR_DATA); + while (NEXT_ARG_OK()) { + NEXT_ARG_FWD(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) { + PREV_ARG(); + break; + } + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, + &mac, ETH_ALEN); + } + addattr_nest_end(n, nmac); + } + } else if (matches(*argv, "nopromisc") == 0) { + flags |= MACVLAN_FLAG_NOPROMISC; + has_flags = 1; + } else if (matches(*argv, "help") == 0) { + explain(lu); + return -1; + } else { + pfx_err(lu, "unknown option \"%s\"?", *argv); + explain(lu); + return -1; + } + argc--, argv++; + } + + if (mode) + addattr32(n, 1024, IFLA_MACVLAN_MODE, mode); + + if (has_flags) { + if (flags & MACVLAN_FLAG_NOPROMISC && + mode != MACVLAN_MODE_PASSTHRU) { + pfx_err(lu, "nopromisc flag only valid in passthru mode"); + explain(lu); + return -1; + } + addattr16(n, 1024, IFLA_MACVLAN_FLAGS, flags); + } + return 0; +} + +static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + __u32 mode; + __u16 flags; + __u32 count; + unsigned char *addr; + int len; + struct rtattr *rta; + + if (!tb) + return; + + if (!tb[IFLA_MACVLAN_MODE] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MODE]) < sizeof(__u32)) + return; + + mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]); + print_string(PRINT_ANY, + "mode", + "mode %s ", + mode == MACVLAN_MODE_PRIVATE ? "private" + : mode == MACVLAN_MODE_VEPA ? "vepa" + : mode == MACVLAN_MODE_BRIDGE ? "bridge" + : mode == MACVLAN_MODE_PASSTHRU ? "passthru" + : mode == MACVLAN_MODE_SOURCE ? "source" + : "unknown"); + + if (!tb[IFLA_MACVLAN_FLAGS] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16)) + flags = 0; + else + flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); + + if (flags & MACVLAN_FLAG_NOPROMISC) + print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true); + + /* in source mode, there are more options to print */ + + if (mode != MACVLAN_MODE_SOURCE) + return; + + if (!tb[IFLA_MACVLAN_MACADDR_COUNT] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32)) + return; + + count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count); + + if (!tb[IFLA_MACVLAN_MACADDR_DATA]) + return; + + rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]); + + open_json_array(PRINT_JSON, "macaddr_data"); + for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + if (rta->rta_type != IFLA_MACVLAN_MACADDR || + RTA_PAYLOAD(rta) < 6) + continue; + addr = RTA_DATA(rta); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), + "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + print_string(PRINT_JSON, NULL, NULL, b1); + } else { + fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + } + } + close_json_array(PRINT_JSON, NULL); +} + +static void macvlan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(lu, f); +} + +struct link_util macvlan_link_util = { + .id = "macvlan", + .maxattr = IFLA_MACVLAN_MAX, + .parse_opt = macvlan_parse_opt, + .print_opt = macvlan_print_opt, + .print_help = macvlan_print_help, +}; + +struct link_util macvtap_link_util = { + .id = "macvtap", + .maxattr = IFLA_MACVLAN_MAX, + .parse_opt = macvlan_parse_opt, + .print_opt = macvlan_print_opt, + .print_help = macvlan_print_help, +}; diff --git a/ip/iplink_netdevsim.c b/ip/iplink_netdevsim.c new file mode 100644 index 0000000..3448608 --- /dev/null +++ b/ip/iplink_netdevsim.c @@ -0,0 +1,16 @@ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void netdevsim_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... netdevsim\n"); +} + +struct link_util netdevsim_link_util = { + .id = "netdevsim", + .print_help = netdevsim_print_help, +}; diff --git a/ip/iplink_nlmon.c b/ip/iplink_nlmon.c new file mode 100644 index 0000000..6ffb910 --- /dev/null +++ b/ip/iplink_nlmon.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void nlmon_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... nlmon\n"); +} + +struct link_util nlmon_link_util = { + .id = "nlmon", + .print_help = nlmon_print_help, +}; diff --git a/ip/iplink_rmnet.c b/ip/iplink_rmnet.c new file mode 100644 index 0000000..1d16440 --- /dev/null +++ b/ip/iplink_rmnet.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * iplink_rmnet.c RMNET device support + * + * Authors: Daniele Palmas + */ + +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... rmnet mux_id MUXID\n" + "\n" + "MUXID := 1-254\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static int rmnet_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + __u16 mux_id; + + while (argc > 0) { + if (matches(*argv, "mux_id") == 0) { + NEXT_ARG(); + if (get_u16(&mux_id, *argv, 0)) + invarg("mux_id is invalid", *argv); + addattr16(n, 1024, IFLA_RMNET_MUX_ID, mux_id); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "rmnet: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void rmnet_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (!tb[IFLA_RMNET_MUX_ID] || + RTA_PAYLOAD(tb[IFLA_RMNET_MUX_ID]) < sizeof(__u16)) + return; + + print_uint(PRINT_ANY, + "mux_id", + "mux_id %u ", + rta_getattr_u16(tb[IFLA_RMNET_MUX_ID])); +} + +static void rmnet_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util rmnet_link_util = { + .id = "rmnet", + .maxattr = IFLA_RMNET_MAX, + .parse_opt = rmnet_parse_opt, + .print_opt = rmnet_print_opt, + .print_help = rmnet_print_help, +}; diff --git a/ip/iplink_team.c b/ip/iplink_team.c new file mode 100644 index 0000000..58f955a --- /dev/null +++ b/ip/iplink_team.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void team_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... team\n"); +} + +static void team_slave_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... team_slave\n"); +} + +struct link_util team_link_util = { + .id = "team", + .print_help = team_print_help, +}, team_slave_link_util = { + .id = "team_slave", + .print_help = team_slave_print_help, +}; diff --git a/ip/iplink_vcan.c b/ip/iplink_vcan.c new file mode 100644 index 0000000..74a1505 --- /dev/null +++ b/ip/iplink_vcan.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void vcan_print_help(struct link_util *lu, + int argc, char **argv, FILE *f) +{ + fprintf(f, "Usage: ... vcan\n"); +} + +struct link_util vcan_link_util = { + .id = "vcan", + .print_help = vcan_print_help, +}; diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c new file mode 100644 index 0000000..1e6817f --- /dev/null +++ b/ip/iplink_vlan.c @@ -0,0 +1,286 @@ +/* + * iplink_vlan.c VLAN device support + * + * 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. + * + * Authors: Patrick McHardy + */ + +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... vlan id VLANID\n" + " [ protocol VLANPROTO ]\n" + " [ reorder_hdr { on | off } ]\n" + " [ gvrp { on | off } ]\n" + " [ mvrp { on | off } ]\n" + " [ loose_binding { on | off } ]\n" + " [ bridge_binding { on | off } ]\n" + " [ ingress-qos-map QOS-MAP ]\n" + " [ egress-qos-map QOS-MAP ]\n" + "\n" + "VLANID := 0-4095\n" + "VLANPROTO: [ 802.1Q | 802.1ad ]\n" + "QOS-MAP := [ QOS-MAP ] QOS-MAPPING\n" + "QOS-MAPPING := FROM:TO\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static int on_off(const char *msg, const char *arg) +{ + fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", msg, arg); + return -1; +} + +static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n, + int attrtype) +{ + int argc = *argcp; + char **argv = *argvp; + struct ifla_vlan_qos_mapping m; + struct rtattr *tail; + + tail = addattr_nest(n, 1024, attrtype); + + while (argc > 0) { + char *colon = strchr(*argv, ':'); + + if (!colon) + break; + *colon = '\0'; + + if (get_u32(&m.from, *argv, 0)) + return 1; + if (get_u32(&m.to, colon + 1, 0)) + return 1; + argc--, argv++; + + addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m)); + } + + addattr_nest_end(n, tail); + + *argcp = argc; + *argvp = argv; + return 0; +} + +static int vlan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifla_vlan_flags flags = { 0 }; + __u16 id, proto; + + while (argc > 0) { + if (matches(*argv, "protocol") == 0) { + NEXT_ARG(); + if (ll_proto_a2n(&proto, *argv)) + invarg("protocol is invalid", *argv); + addattr_l(n, 1024, IFLA_VLAN_PROTOCOL, &proto, 2); + } else if (matches(*argv, "id") == 0) { + NEXT_ARG(); + if (get_u16(&id, *argv, 0)) + invarg("id is invalid", *argv); + addattr_l(n, 1024, IFLA_VLAN_ID, &id, 2); + } else if (matches(*argv, "reorder_hdr") == 0) { + NEXT_ARG(); + flags.mask |= VLAN_FLAG_REORDER_HDR; + if (strcmp(*argv, "on") == 0) + flags.flags |= VLAN_FLAG_REORDER_HDR; + else if (strcmp(*argv, "off") == 0) + flags.flags &= ~VLAN_FLAG_REORDER_HDR; + else + return on_off("reorder_hdr", *argv); + } else if (matches(*argv, "gvrp") == 0) { + NEXT_ARG(); + flags.mask |= VLAN_FLAG_GVRP; + if (strcmp(*argv, "on") == 0) + flags.flags |= VLAN_FLAG_GVRP; + else if (strcmp(*argv, "off") == 0) + flags.flags &= ~VLAN_FLAG_GVRP; + else + return on_off("gvrp", *argv); + } else if (matches(*argv, "mvrp") == 0) { + NEXT_ARG(); + flags.mask |= VLAN_FLAG_MVRP; + if (strcmp(*argv, "on") == 0) + flags.flags |= VLAN_FLAG_MVRP; + else if (strcmp(*argv, "off") == 0) + flags.flags &= ~VLAN_FLAG_MVRP; + else + return on_off("mvrp", *argv); + } else if (matches(*argv, "loose_binding") == 0) { + NEXT_ARG(); + flags.mask |= VLAN_FLAG_LOOSE_BINDING; + if (strcmp(*argv, "on") == 0) + flags.flags |= VLAN_FLAG_LOOSE_BINDING; + else if (strcmp(*argv, "off") == 0) + flags.flags &= ~VLAN_FLAG_LOOSE_BINDING; + else + return on_off("loose_binding", *argv); + } else if (matches(*argv, "bridge_binding") == 0) { + NEXT_ARG(); + flags.mask |= VLAN_FLAG_BRIDGE_BINDING; + if (strcmp(*argv, "on") == 0) + flags.flags |= VLAN_FLAG_BRIDGE_BINDING; + else if (strcmp(*argv, "off") == 0) + flags.flags &= ~VLAN_FLAG_BRIDGE_BINDING; + else + return on_off("bridge_binding", *argv); + } else if (matches(*argv, "ingress-qos-map") == 0) { + NEXT_ARG(); + if (vlan_parse_qos_map(&argc, &argv, n, + IFLA_VLAN_INGRESS_QOS)) + invarg("invalid ingress-qos-map", *argv); + continue; + } else if (matches(*argv, "egress-qos-map") == 0) { + NEXT_ARG(); + if (vlan_parse_qos_map(&argc, &argv, n, + IFLA_VLAN_EGRESS_QOS)) + invarg("invalid egress-qos-map", *argv); + continue; + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "vlan: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + if (flags.mask) + addattr_l(n, 1024, IFLA_VLAN_FLAGS, &flags, sizeof(flags)); + + return 0; +} + +static void vlan_print_map(FILE *f, + const char *name_json, + const char *name_fp, + struct rtattr *attr) +{ + struct ifla_vlan_qos_mapping *m; + struct rtattr *i; + int rem; + + open_json_array(PRINT_JSON, name_json); + print_nl(); + print_string(PRINT_FP, NULL, " %s { ", name_fp); + + rem = RTA_PAYLOAD(attr); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + m = RTA_DATA(i); + + if (is_json_context()) { + open_json_object(NULL); + print_uint(PRINT_JSON, "from", NULL, m->from); + print_uint(PRINT_JSON, "to", NULL, m->to); + close_json_object(); + } else { + fprintf(f, "%u:%u ", m->from, m->to); + } + } + + close_json_array(PRINT_JSON, NULL); + print_string(PRINT_FP, NULL, "%s ", "}"); +} + +static void vlan_print_flags(FILE *fp, __u32 flags) +{ + open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<"); +#define _PF(f) if (flags & VLAN_FLAG_##f) { \ + flags &= ~VLAN_FLAG_##f; \ + print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); \ + } + _PF(REORDER_HDR); + _PF(GVRP); + _PF(MVRP); + _PF(LOOSE_BINDING); + _PF(BRIDGE_BINDING); +#undef _PF + if (flags) + print_hex(PRINT_ANY, NULL, "%x", flags); + close_json_array(PRINT_ANY, "> "); +} + +static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + struct ifla_vlan_flags *flags; + + SPRINT_BUF(b1); + + if (!tb) + return; + + if (tb[IFLA_VLAN_PROTOCOL] && + RTA_PAYLOAD(tb[IFLA_VLAN_PROTOCOL]) < sizeof(__u16)) + return; + if (!tb[IFLA_VLAN_ID] || + RTA_PAYLOAD(tb[IFLA_VLAN_ID]) < sizeof(__u16)) + return; + + if (tb[IFLA_VLAN_PROTOCOL]) + print_string(PRINT_ANY, + "protocol", + "protocol %s ", + ll_proto_n2a( + rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]), + b1, sizeof(b1))); + else + print_string(PRINT_ANY, "protocol", "protocol %s ", "802.1q"); + + print_uint(PRINT_ANY, + "id", + "id %u ", + rta_getattr_u16(tb[IFLA_VLAN_ID])); + + if (tb[IFLA_VLAN_FLAGS]) { + if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags)) + return; + flags = RTA_DATA(tb[IFLA_VLAN_FLAGS]); + vlan_print_flags(f, flags->flags); + } + if (tb[IFLA_VLAN_INGRESS_QOS]) + vlan_print_map(f, + "ingress_qos", + "ingress-qos-map", + tb[IFLA_VLAN_INGRESS_QOS]); + if (tb[IFLA_VLAN_EGRESS_QOS]) + vlan_print_map(f, + "egress_qos", + "egress-qos-map", + tb[IFLA_VLAN_EGRESS_QOS]); +} + +static void vlan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util vlan_link_util = { + .id = "vlan", + .maxattr = IFLA_VLAN_MAX, + .parse_opt = vlan_parse_opt, + .print_opt = vlan_print_opt, + .print_help = vlan_print_help, +}; diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c new file mode 100644 index 0000000..5d20f29 --- /dev/null +++ b/ip/iplink_vrf.c @@ -0,0 +1,226 @@ +/* iplink_vrf.c VRF device support + * + * 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. + * + * Authors: Shrijeet Mukherjee + */ + +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static void vrf_explain(FILE *f) +{ + fprintf(f, "Usage: ... vrf table TABLEID\n"); +} + +static void explain(void) +{ + vrf_explain(stderr); +} + +static int vrf_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + while (argc > 0) { + if (matches(*argv, "table") == 0) { + __u32 table; + + NEXT_ARG(); + + if (rtnl_rttable_a2n(&table, *argv)) + invarg("invalid table ID\n", *argv); + addattr32(n, 1024, IFLA_VRF_TABLE, table); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "vrf: unknown option \"%s\"?\n", + *argv); + explain(); + return -1; + } + argc--, argv++; + } + + return 0; +} + +static void vrf_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_VRF_TABLE]) + print_uint(PRINT_ANY, + "table", + "table %u ", + rta_getattr_u32(tb[IFLA_VRF_TABLE])); +} + +static void vrf_slave_print_opt(struct link_util *lu, FILE *f, + struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_VRF_PORT_TABLE]) { + print_uint(PRINT_ANY, + "table", + "table %u ", + rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE])); + } +} + +static void vrf_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + vrf_explain(f); +} + +struct link_util vrf_link_util = { + .id = "vrf", + .maxattr = IFLA_VRF_MAX, + .parse_opt = vrf_parse_opt, + .print_opt = vrf_print_opt, + .print_help = vrf_print_help, +}; + +struct link_util vrf_slave_link_util = { + .id = "vrf_slave", + .maxattr = IFLA_VRF_PORT_MAX, + .print_opt = vrf_slave_print_opt, +}; + +/* returns table id if name is a VRF device */ +__u32 ipvrf_get_table(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct rtattr *vrf_attr[IFLA_VRF_MAX + 1]; + struct ifinfomsg *ifi; + __u32 tb_id = 0; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) { + /* special case "default" vrf to be the main table */ + if (errno == ENODEV && !strcmp(name, "default")) + if (rtnl_rttable_a2n(&tb_id, "main")) + fprintf(stderr, + "BUG: RTTable \"main\" not found.\n"); + + return tb_id; + } + + ifi = NLMSG_DATA(answer); + len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + goto out; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + goto out; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND] || !li[IFLA_INFO_DATA]) + goto out; + + if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) + goto out; + + parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]); + if (vrf_attr[IFLA_VRF_TABLE]) + tb_id = rta_getattr_u32(vrf_attr[IFLA_VRF_TABLE]); + + if (!tb_id) + fprintf(stderr, "BUG: VRF %s is missing table id\n", name); + +out: + free(answer); + return tb_id; +} + +int name_is_vrf(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct ifinfomsg *ifi; + int ifindex = 0; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) + return 0; + + ifi = NLMSG_DATA(answer); + len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + goto out; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + goto out; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND]) + goto out; + + if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) + goto out; + + ifindex = ifi->ifi_index; +out: + free(answer); + return ifindex; +} diff --git a/ip/iplink_vxcan.c b/ip/iplink_vxcan.c new file mode 100644 index 0000000..8b08c9a --- /dev/null +++ b/ip/iplink_vxcan.c @@ -0,0 +1,88 @@ +/* + * iplink_vxcan.c vxcan device support (Virtual CAN Tunnel) + * + * 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. + * + * Author: Oliver Hartkopp + * Based on: link_veth.c from Pavel Emelianov + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void print_usage(FILE *f) +{ + printf("Usage: ip link type vxcan [peer ]\n" + "To get type 'ip link add help'\n"); +} + +static void usage(void) +{ + print_usage(stderr); +} + +static int vxcan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + char *type = NULL; + int err; + struct rtattr *data; + struct ifinfomsg *ifm, *peer_ifm; + unsigned int ifi_flags, ifi_change, ifi_index; + + if (strcmp(argv[0], "peer") != 0) { + usage(); + return -1; + } + + ifm = NLMSG_DATA(n); + ifi_flags = ifm->ifi_flags; + ifi_change = ifm->ifi_change; + ifi_index = ifm->ifi_index; + ifm->ifi_flags = 0; + ifm->ifi_change = 0; + ifm->ifi_index = 0; + + data = addattr_nest(n, 1024, VXCAN_INFO_PEER); + + n->nlmsg_len += sizeof(struct ifinfomsg); + + err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)n, &type); + if (err < 0) + return err; + + if (type) + duparg("type", argv[err]); + + peer_ifm = RTA_DATA(data); + peer_ifm->ifi_index = ifm->ifi_index; + peer_ifm->ifi_flags = ifm->ifi_flags; + peer_ifm->ifi_change = ifm->ifi_change; + ifm->ifi_flags = ifi_flags; + ifm->ifi_change = ifi_change; + ifm->ifi_index = ifi_index; + + addattr_nest_end(n, data); + return argc - 1 - err; +} + +static void vxcan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_usage(f); +} + +struct link_util vxcan_link_util = { + .id = "vxcan", + .parse_opt = vxcan_parse_opt, + .print_help = vxcan_print_help, +}; diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c new file mode 100644 index 0000000..bae9d99 --- /dev/null +++ b/ip/iplink_vxlan.c @@ -0,0 +1,657 @@ +/* + * iplink_vxlan.c VXLAN device support + * + * 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. + * + * Authors: Stephen Hemminger +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define VXLAN_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0) + +static void print_explain(FILE *f) +{ + fprintf(f, + "Usage: ... vxlan id VNI\n" + " [ { group | remote } IP_ADDRESS ]\n" + " [ local ADDR ]\n" + " [ ttl TTL ]\n" + " [ tos TOS ]\n" + " [ df DF ]\n" + " [ flowlabel LABEL ]\n" + " [ dev PHYS_DEV ]\n" + " [ dstport PORT ]\n" + " [ srcport MIN MAX ]\n" + " [ [no]learning ]\n" + " [ [no]proxy ]\n" + " [ [no]rsc ]\n" + " [ [no]l2miss ]\n" + " [ [no]l3miss ]\n" + " [ ageing SECONDS ]\n" + " [ maxaddress NUMBER ]\n" + " [ [no]udpcsum ]\n" + " [ [no]udp6zerocsumtx ]\n" + " [ [no]udp6zerocsumrx ]\n" + " [ [no]remcsumtx ] [ [no]remcsumrx ]\n" + " [ [no]external ] [ gbp ] [ gpe ]\n" + "\n" + "Where: VNI := 0-16777215\n" + " ADDR := { IP_ADDRESS | any }\n" + " TOS := { NUMBER | inherit }\n" + " TTL := { 1..255 | auto | inherit }\n" + " DF := { unset | set | inherit }\n" + " LABEL := 0-1048575\n" + ); +} + +static void explain(void) +{ + print_explain(stderr); +} + +static void check_duparg(__u64 *attrs, int type, const char *key, + const char *argv) +{ + if (!VXLAN_ATTRSET(*attrs, type)) { + *attrs |= (1L << type); + return; + } + duparg2(key, argv); +} + +static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + inet_prefix saddr, daddr; + __u32 vni = 0; + __u8 learning = 1; + __u16 dstport = 0; + __u8 metadata = 0; + __u64 attrs = 0; + bool set_op = (n->nlmsg_type == RTM_NEWLINK && + !(n->nlmsg_flags & NLM_F_CREATE)); + bool selected_family = false; + + saddr.family = daddr.family = AF_UNSPEC; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + while (argc > 0) { + if (!matches(*argv, "id") || + !matches(*argv, "vni")) { + /* We will add ID attribute outside of the loop since we + * need to consider metadata information as well. + */ + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_ID, "id", *argv); + if (get_u32(&vni, *argv, 0) || + vni >= 1u << 24) + invarg("invalid id", *argv); + } else if (!matches(*argv, "group")) { + if (is_addrtype_inet_not_multi(&daddr)) { + fprintf(stderr, "vxlan: both group and remote"); + fprintf(stderr, " cannot be specified\n"); + return -1; + } + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_GROUP, "group", *argv); + get_addr(&daddr, *argv, saddr.family); + if (!is_addrtype_inet_multi(&daddr)) + invarg("invalid group address", *argv); + } else if (!matches(*argv, "remote")) { + if (is_addrtype_inet_multi(&daddr)) { + fprintf(stderr, "vxlan: both group and remote"); + fprintf(stderr, " cannot be specified\n"); + return -1; + } + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_GROUP, "remote", *argv); + get_addr(&daddr, *argv, saddr.family); + if (!is_addrtype_inet_not_multi(&daddr)) + invarg("invalid remote address", *argv); + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_LOCAL, "local", *argv); + get_addr(&saddr, *argv, daddr.family); + if (!is_addrtype_inet_not_multi(&saddr)) + invarg("invalid local address", *argv); + } else if (!matches(*argv, "dev")) { + unsigned int link; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_LINK, "dev", *argv); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + addattr32(n, 1024, IFLA_VXLAN_LINK, link); + } else if (!matches(*argv, "ttl") || + !matches(*argv, "hoplimit")) { + unsigned int uval; + __u8 ttl = 0; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_TTL, "ttl", *argv); + if (strcmp(*argv, "inherit") == 0) { + addattr(n, 1024, IFLA_VXLAN_TTL_INHERIT); + } else if (strcmp(*argv, "auto") == 0) { + addattr8(n, 1024, IFLA_VXLAN_TTL, ttl); + } else { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL", *argv); + if (uval > 255) + invarg("TTL must be <= 255", *argv); + ttl = uval; + addattr8(n, 1024, IFLA_VXLAN_TTL, ttl); + } + } else if (!matches(*argv, "tos") || + !matches(*argv, "dsfield")) { + __u32 uval; + __u8 tos; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_TOS, "tos", *argv); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + tos = uval; + } else + tos = 1; + addattr8(n, 1024, IFLA_VXLAN_TOS, tos); + } else if (!matches(*argv, "df")) { + enum ifla_vxlan_df df; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_DF, "df", *argv); + if (strcmp(*argv, "unset") == 0) + df = VXLAN_DF_UNSET; + else if (strcmp(*argv, "set") == 0) + df = VXLAN_DF_SET; + else if (strcmp(*argv, "inherit") == 0) + df = VXLAN_DF_INHERIT; + else + invarg("DF must be 'unset', 'set' or 'inherit'", + *argv); + + addattr8(n, 1024, IFLA_VXLAN_DF, df); + } else if (!matches(*argv, "label") || + !matches(*argv, "flowlabel")) { + __u32 uval; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_LABEL, "flowlabel", + *argv); + if (get_u32(&uval, *argv, 0) || + (uval & ~LABEL_MAX_MASK)) + invarg("invalid flowlabel", *argv); + addattr32(n, 1024, IFLA_VXLAN_LABEL, htonl(uval)); + } else if (!matches(*argv, "ageing")) { + __u32 age; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_AGEING, "ageing", + *argv); + if (strcmp(*argv, "none") == 0) + age = 0; + else if (get_u32(&age, *argv, 0)) + invarg("ageing timer", *argv); + addattr32(n, 1024, IFLA_VXLAN_AGEING, age); + } else if (!matches(*argv, "maxaddress")) { + __u32 maxaddr; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_LIMIT, + "maxaddress", *argv); + if (strcmp(*argv, "unlimited") == 0) + maxaddr = 0; + else if (get_u32(&maxaddr, *argv, 0)) + invarg("max addresses", *argv); + addattr32(n, 1024, IFLA_VXLAN_LIMIT, maxaddr); + } else if (!matches(*argv, "port") || + !matches(*argv, "srcport")) { + struct ifla_vxlan_port_range range = { 0, 0 }; + + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_PORT_RANGE, "srcport", + *argv); + if (get_be16(&range.low, *argv, 0)) + invarg("min port", *argv); + NEXT_ARG(); + if (get_be16(&range.high, *argv, 0)) + invarg("max port", *argv); + if (range.low || range.high) { + addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE, + &range, sizeof(range)); + } + } else if (!matches(*argv, "dstport")) { + NEXT_ARG(); + check_duparg(&attrs, IFLA_VXLAN_PORT, "dstport", *argv); + if (get_u16(&dstport, *argv, 0)) + invarg("dst port", *argv); + } else if (!matches(*argv, "nolearning")) { + check_duparg(&attrs, IFLA_VXLAN_LEARNING, *argv, *argv); + learning = 0; + } else if (!matches(*argv, "learning")) { + check_duparg(&attrs, IFLA_VXLAN_LEARNING, *argv, *argv); + learning = 1; + } else if (!matches(*argv, "noproxy")) { + check_duparg(&attrs, IFLA_VXLAN_PROXY, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_PROXY, 0); + } else if (!matches(*argv, "proxy")) { + check_duparg(&attrs, IFLA_VXLAN_PROXY, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_PROXY, 1); + } else if (!matches(*argv, "norsc")) { + check_duparg(&attrs, IFLA_VXLAN_RSC, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_RSC, 0); + } else if (!matches(*argv, "rsc")) { + check_duparg(&attrs, IFLA_VXLAN_RSC, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_RSC, 1); + } else if (!matches(*argv, "nol2miss")) { + check_duparg(&attrs, IFLA_VXLAN_L2MISS, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_L2MISS, 0); + } else if (!matches(*argv, "l2miss")) { + check_duparg(&attrs, IFLA_VXLAN_L2MISS, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_L2MISS, 1); + } else if (!matches(*argv, "nol3miss")) { + check_duparg(&attrs, IFLA_VXLAN_L3MISS, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_L3MISS, 0); + } else if (!matches(*argv, "l3miss")) { + check_duparg(&attrs, IFLA_VXLAN_L3MISS, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_L3MISS, 1); + } else if (!matches(*argv, "udpcsum")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_CSUM, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, 1); + } else if (!matches(*argv, "noudpcsum")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_CSUM, *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, 0); + } else if (!matches(*argv, "udp6zerocsumtx")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, 1); + } else if (!matches(*argv, "noudp6zerocsumtx")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, 0); + } else if (!matches(*argv, "udp6zerocsumrx")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 1); + } else if (!matches(*argv, "noudp6zerocsumrx")) { + check_duparg(&attrs, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, 0); + } else if (!matches(*argv, "remcsumtx")) { + check_duparg(&attrs, IFLA_VXLAN_REMCSUM_TX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, 1); + } else if (!matches(*argv, "noremcsumtx")) { + check_duparg(&attrs, IFLA_VXLAN_REMCSUM_TX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_TX, 0); + } else if (!matches(*argv, "remcsumrx")) { + check_duparg(&attrs, IFLA_VXLAN_REMCSUM_RX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, 1); + } else if (!matches(*argv, "noremcsumrx")) { + check_duparg(&attrs, IFLA_VXLAN_REMCSUM_RX, + *argv, *argv); + addattr8(n, 1024, IFLA_VXLAN_REMCSUM_RX, 0); + } else if (!matches(*argv, "external")) { + check_duparg(&attrs, IFLA_VXLAN_COLLECT_METADATA, + *argv, *argv); + metadata = 1; + learning = 0; + /* we will add LEARNING attribute outside of the loop */ + addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA, + metadata); + } else if (!matches(*argv, "noexternal")) { + check_duparg(&attrs, IFLA_VXLAN_COLLECT_METADATA, + *argv, *argv); + metadata = 0; + addattr8(n, 1024, IFLA_VXLAN_COLLECT_METADATA, + metadata); + } else if (!matches(*argv, "gbp")) { + check_duparg(&attrs, IFLA_VXLAN_GBP, *argv, *argv); + addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0); + } else if (!matches(*argv, "gpe")) { + check_duparg(&attrs, IFLA_VXLAN_GPE, *argv, *argv); + addattr_l(n, 1024, IFLA_VXLAN_GPE, NULL, 0); + } else if (matches(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "vxlan: unknown command \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--, argv++; + } + + if (metadata && VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID)) { + fprintf(stderr, "vxlan: both 'external' and vni cannot be specified\n"); + return -1; + } + + if (!metadata && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) { + fprintf(stderr, "vxlan: missing virtual network identifier\n"); + return -1; + } + + if (is_addrtype_inet_multi(&daddr) && + !VXLAN_ATTRSET(attrs, IFLA_VXLAN_LINK)) { + fprintf(stderr, "vxlan: 'group' requires 'dev' to be specified\n"); + return -1; + } + + if (!VXLAN_ATTRSET(attrs, IFLA_VXLAN_PORT) && + VXLAN_ATTRSET(attrs, IFLA_VXLAN_GPE)) { + dstport = 4790; + } else if (!VXLAN_ATTRSET(attrs, IFLA_VXLAN_PORT) && !set_op) { + fprintf(stderr, "vxlan: destination port not specified\n" + "Will use Linux kernel default (non-standard value)\n"); + fprintf(stderr, + "Use 'dstport 4789' to get the IANA assigned value\n" + "Use 'dstport 0' to get default and quiet this message\n"); + } + + if (VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID)) + addattr32(n, 1024, IFLA_VXLAN_ID, vni); + + if (is_addrtype_inet(&saddr)) { + int type = (saddr.family == AF_INET) ? IFLA_VXLAN_LOCAL + : IFLA_VXLAN_LOCAL6; + addattr_l(n, 1024, type, saddr.data, saddr.bytelen); + selected_family = true; + } + + if (is_addrtype_inet(&daddr)) { + int type = (daddr.family == AF_INET) ? IFLA_VXLAN_GROUP + : IFLA_VXLAN_GROUP6; + addattr_l(n, 1024, type, daddr.data, daddr.bytelen); + selected_family = true; + } + + if (!selected_family) { + if (preferred_family == AF_INET) { + get_addr(&daddr, "default", AF_INET); + addattr_l(n, 1024, IFLA_VXLAN_GROUP, + daddr.data, daddr.bytelen); + } else if (preferred_family == AF_INET6) { + get_addr(&daddr, "default", AF_INET6); + addattr_l(n, 1024, IFLA_VXLAN_GROUP6, + daddr.data, daddr.bytelen); + } + } + + if (!set_op || VXLAN_ATTRSET(attrs, IFLA_VXLAN_LEARNING)) + addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning); + + if (dstport) + addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport)); + + return 0; +} + +static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + __u32 vni; + __u8 ttl = 0; + __u8 tos = 0; + __u32 maxaddr; + + if (!tb) + return; + + if (tb[IFLA_VXLAN_COLLECT_METADATA] && + rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA])) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + if (!tb[IFLA_VXLAN_ID] || + RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) < sizeof(__u32)) + return; + + vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]); + print_uint(PRINT_ANY, "id", "id %u ", vni); + + if (tb[IFLA_VXLAN_GROUP]) { + __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]); + + if (addr) { + if (IN_MULTICAST(ntohl(addr))) + print_string(PRINT_ANY, + "group", + "group %s ", + format_host(AF_INET, 4, &addr)); + else + print_string(PRINT_ANY, + "remote", + "remote %s ", + format_host(AF_INET, 4, &addr)); + } + } else if (tb[IFLA_VXLAN_GROUP6]) { + struct in6_addr addr; + + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { + if (IN6_IS_ADDR_MULTICAST(&addr)) + print_string(PRINT_ANY, + "group6", + "group %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); + else + print_string(PRINT_ANY, + "remote6", + "remote %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); + } + } + + if (tb[IFLA_VXLAN_LOCAL]) { + __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]); + + if (addr) + print_string(PRINT_ANY, + "local", + "local %s ", + format_host(AF_INET, 4, &addr)); + } else if (tb[IFLA_VXLAN_LOCAL6]) { + struct in6_addr addr; + + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) + print_string(PRINT_ANY, + "local6", + "local %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); + } + + if (tb[IFLA_VXLAN_LINK]) { + unsigned int link = rta_getattr_u32(tb[IFLA_VXLAN_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + const struct ifla_vxlan_port_range *r + = RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]); + if (is_json_context()) { + open_json_object("port_range"); + print_uint(PRINT_JSON, "low", NULL, ntohs(r->low)); + print_uint(PRINT_JSON, "high", NULL, ntohs(r->high)); + close_json_object(); + } else { + fprintf(f, "srcport %u %u ", + ntohs(r->low), ntohs(r->high)); + } + } + + if (tb[IFLA_VXLAN_PORT]) + print_uint(PRINT_ANY, + "port", + "dstport %u ", + rta_getattr_be16(tb[IFLA_VXLAN_PORT])); + + if (tb[IFLA_VXLAN_LEARNING]) { + __u8 learning = rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]); + + print_bool(PRINT_JSON, "learning", NULL, learning); + if (!learning) + print_bool(PRINT_FP, NULL, "nolearning ", true); + } + + if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY])) + print_bool(PRINT_ANY, "proxy", "proxy ", true); + + if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC])) + print_bool(PRINT_ANY, "rsc", "rsc ", true); + + if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS])) + print_bool(PRINT_ANY, "l2miss", "l2miss ", true); + + if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS])) + print_bool(PRINT_ANY, "l3miss", "l3miss ", true); + + if (tb[IFLA_VXLAN_TOS]) + tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]); + if (tos) { + if (is_json_context() || tos != 1) + print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos); + else + print_string(PRINT_FP, NULL, "tos %s ", "inherit"); + } + + if (tb[IFLA_VXLAN_TTL_INHERIT] && + rta_getattr_u8(tb[IFLA_VXLAN_TTL_INHERIT])) { + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + } else if (tb[IFLA_VXLAN_TTL]) { + ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); + else + print_string(PRINT_FP, NULL, "ttl %s ", "auto"); + } + + if (tb[IFLA_VXLAN_DF]) { + enum ifla_vxlan_df df = rta_getattr_u8(tb[IFLA_VXLAN_DF]); + + if (df == VXLAN_DF_UNSET) + print_string(PRINT_JSON, "df", "df %s ", "unset"); + else if (df == VXLAN_DF_SET) + print_string(PRINT_ANY, "df", "df %s ", "set"); + else if (df == VXLAN_DF_INHERIT) + print_string(PRINT_ANY, "df", "df %s ", "inherit"); + } + + if (tb[IFLA_VXLAN_LABEL]) { + __u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]); + + if (label) + print_0xhex(PRINT_ANY, "label", + "flowlabel %#llx ", ntohl(label)); + } + + if (tb[IFLA_VXLAN_AGEING]) { + __u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]); + + if (age == 0) + print_uint(PRINT_ANY, "ageing", "ageing none ", 0); + else + print_uint(PRINT_ANY, "ageing", "ageing %u ", age); + } + + if (tb[IFLA_VXLAN_LIMIT] && + ((maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT])) != 0)) + print_uint(PRINT_ANY, "limit", "maxaddr %u ", maxaddr); + + if (tb[IFLA_VXLAN_UDP_CSUM]) { + __u8 udp_csum = rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]); + + if (is_json_context()) { + print_bool(PRINT_ANY, "udp_csum", NULL, udp_csum); + } else { + if (!udp_csum) + fputs("no", f); + fputs("udpcsum ", f); + } + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { + __u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]); + + if (is_json_context()) { + print_bool(PRINT_ANY, + "udp_zero_csum6_tx", NULL, csum6); + } else { + if (!csum6) + fputs("no", f); + fputs("udp6zerocsumtx ", f); + } + } + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) { + __u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]); + + if (is_json_context()) { + print_bool(PRINT_ANY, + "udp_zero_csum6_rx", + NULL, + csum6); + } else { + if (!csum6) + fputs("no", f); + fputs("udp6zerocsumrx ", f); + } + } + + if (tb[IFLA_VXLAN_REMCSUM_TX] && + rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_TX])) + print_bool(PRINT_ANY, "remcsum_tx", "remcsumtx ", true); + + if (tb[IFLA_VXLAN_REMCSUM_RX] && + rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_RX])) + print_bool(PRINT_ANY, "remcsum_rx", "remcsumrx ", true); + + if (tb[IFLA_VXLAN_GBP]) + print_bool(PRINT_ANY, "gbp", "gbp ", true); + if (tb[IFLA_VXLAN_GPE]) + print_bool(PRINT_ANY, "gpe", "gpe ", true); +} + +static void vxlan_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_explain(f); +} + +struct link_util vxlan_link_util = { + .id = "vxlan", + .maxattr = IFLA_VXLAN_MAX, + .parse_opt = vxlan_parse_opt, + .print_opt = vxlan_print_opt, + .print_help = vxlan_print_help, +}; diff --git a/ip/iplink_xdp.c b/ip/iplink_xdp.c new file mode 100644 index 0000000..4a490bc --- /dev/null +++ b/ip/iplink_xdp.c @@ -0,0 +1,199 @@ +/* + * iplink_xdp.c XDP program loader + * + * 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. + * + * Authors: Daniel Borkmann + */ + +#include +#include + +#include + +#include "bpf_util.h" +#include "utils.h" +#include "ip_common.h" + +extern int force; + +struct xdp_req { + struct iplink_req *req; + __u32 flags; +}; + +static void xdp_ebpf_cb(void *raw, int fd, const char *annotation) +{ + struct xdp_req *xdp = raw; + struct iplink_req *req = xdp->req; + struct rtattr *xdp_attr; + + xdp_attr = addattr_nest(&req->n, sizeof(*req), IFLA_XDP); + addattr32(&req->n, sizeof(*req), IFLA_XDP_FD, fd); + if (xdp->flags) + addattr32(&req->n, sizeof(*req), IFLA_XDP_FLAGS, xdp->flags); + addattr_nest_end(&req->n, xdp_attr); +} + +static const struct bpf_cfg_ops bpf_cb_ops = { + .ebpf_cb = xdp_ebpf_cb, +}; + +static int xdp_delete(struct xdp_req *xdp) +{ + xdp_ebpf_cb(xdp, -1, NULL); + return 0; +} + +int xdp_parse(int *argc, char ***argv, struct iplink_req *req, + const char *ifname, bool generic, bool drv, bool offload) +{ + struct bpf_cfg_in cfg = { + .type = BPF_PROG_TYPE_XDP, + .argc = *argc, + .argv = *argv, + }; + struct xdp_req xdp = { + .req = req, + }; + + if (offload) { + int ifindex = ll_name_to_index(ifname); + + if (!ifindex) + incomplete_command(); + cfg.ifindex = ifindex; + } + + if (!force) + xdp.flags |= XDP_FLAGS_UPDATE_IF_NOEXIST; + if (generic) + xdp.flags |= XDP_FLAGS_SKB_MODE; + if (drv) + xdp.flags |= XDP_FLAGS_DRV_MODE; + if (offload) + xdp.flags |= XDP_FLAGS_HW_MODE; + + if (*argc == 1) { + if (strcmp(**argv, "none") == 0 || + strcmp(**argv, "off") == 0) + return xdp_delete(&xdp); + } + + if (bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &xdp)) + return -1; + + *argc = cfg.argc; + *argv = cfg.argv; + return 0; +} + +static void xdp_dump_json_one(struct rtattr *tb[IFLA_XDP_MAX + 1], __u32 attr, + __u8 mode) +{ + if (!tb[attr]) + return; + + open_json_object(NULL); + print_uint(PRINT_JSON, "mode", NULL, mode); + bpf_dump_prog_info(NULL, rta_getattr_u32(tb[attr])); + close_json_object(); +} + +static void xdp_dump_json(struct rtattr *tb[IFLA_XDP_MAX + 1]) +{ + __u32 prog_id = 0; + __u8 mode; + + mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]); + if (tb[IFLA_XDP_PROG_ID]) + prog_id = rta_getattr_u32(tb[IFLA_XDP_PROG_ID]); + + open_json_object("xdp"); + print_uint(PRINT_JSON, "mode", NULL, mode); + if (prog_id) + bpf_dump_prog_info(NULL, prog_id); + + open_json_array(PRINT_JSON, "attached"); + if (tb[IFLA_XDP_SKB_PROG_ID] || + tb[IFLA_XDP_DRV_PROG_ID] || + tb[IFLA_XDP_HW_PROG_ID]) { + xdp_dump_json_one(tb, IFLA_XDP_SKB_PROG_ID, XDP_ATTACHED_SKB); + xdp_dump_json_one(tb, IFLA_XDP_DRV_PROG_ID, XDP_ATTACHED_DRV); + xdp_dump_json_one(tb, IFLA_XDP_HW_PROG_ID, XDP_ATTACHED_HW); + } else if (tb[IFLA_XDP_PROG_ID]) { + /* Older kernel - use IFLA_XDP_PROG_ID */ + xdp_dump_json_one(tb, IFLA_XDP_PROG_ID, mode); + } + close_json_array(PRINT_JSON, NULL); + + close_json_object(); +} + +static void xdp_dump_prog_one(FILE *fp, struct rtattr *tb[IFLA_XDP_MAX + 1], + __u32 attr, bool link, bool details, + const char *pfx) +{ + __u32 prog_id; + + if (!tb[attr]) + return; + + prog_id = rta_getattr_u32(tb[attr]); + if (!details) { + if (prog_id && !link && attr == IFLA_XDP_PROG_ID) + fprintf(fp, "/id:%u", prog_id); + return; + } + + if (prog_id) { + fprintf(fp, "%s prog/xdp%s ", _SL_, pfx); + bpf_dump_prog_info(fp, prog_id); + } +} + +void xdp_dump(FILE *fp, struct rtattr *xdp, bool link, bool details) +{ + struct rtattr *tb[IFLA_XDP_MAX + 1]; + __u8 mode; + + parse_rtattr_nested(tb, IFLA_XDP_MAX, xdp); + + if (!tb[IFLA_XDP_ATTACHED]) + return; + + mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]); + if (mode == XDP_ATTACHED_NONE) + return; + else if (is_json_context()) + return details ? (void)0 : xdp_dump_json(tb); + else if (details && link) + /* don't print mode */; + else if (mode == XDP_ATTACHED_DRV) + fprintf(fp, "xdp"); + else if (mode == XDP_ATTACHED_SKB) + fprintf(fp, "xdpgeneric"); + else if (mode == XDP_ATTACHED_HW) + fprintf(fp, "xdpoffload"); + else if (mode == XDP_ATTACHED_MULTI) + fprintf(fp, "xdpmulti"); + else + fprintf(fp, "xdp[%u]", mode); + + xdp_dump_prog_one(fp, tb, IFLA_XDP_PROG_ID, link, details, ""); + + if (mode == XDP_ATTACHED_MULTI) { + xdp_dump_prog_one(fp, tb, IFLA_XDP_SKB_PROG_ID, link, details, + "generic"); + xdp_dump_prog_one(fp, tb, IFLA_XDP_DRV_PROG_ID, link, details, + "drv"); + xdp_dump_prog_one(fp, tb, IFLA_XDP_HW_PROG_ID, link, details, + "offload"); + } + + if (!details || !link) + fprintf(fp, " "); +} diff --git a/ip/iplink_xstats.c b/ip/iplink_xstats.c new file mode 100644 index 0000000..c64e688 --- /dev/null +++ b/ip/iplink_xstats.c @@ -0,0 +1,82 @@ +/* + * iplink_stats.c Extended statistics commands + * + * 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. + * + * Authors: Nikolay Aleksandrov + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void print_explain(FILE *f) +{ + fprintf(f, "Usage: ... xstats type TYPE [ ARGS ]\n"); +} + +int iplink_ifla_xstats(int argc, char **argv) +{ + struct link_util *lu = NULL; + __u32 filt_mask; + + if (!argc) { + fprintf(stderr, "xstats: missing argument\n"); + return -1; + } + + if (matches(*argv, "type") == 0) { + NEXT_ARG(); + lu = get_link_kind(*argv); + if (!lu) + invarg("invalid type", *argv); + } else if (matches(*argv, "help") == 0) { + print_explain(stdout); + return 0; + } else { + invarg("unknown argument", *argv); + } + + if (!lu) { + print_explain(stderr); + return -1; + } + + if (!lu->print_ifla_xstats) { + fprintf(stderr, "xstats: link type %s doesn't support xstats\n", + lu->id); + return -1; + } + + if (lu->parse_ifla_xstats && + lu->parse_ifla_xstats(lu, argc-1, argv+1)) + return -1; + + if (strstr(lu->id, "_slave")) + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE); + else + filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS); + + if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { + perror("Cannont send dump request"); + return -1; + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, lu->print_ifla_xstats, stdout) < 0) { + delete_json_obj(); + fprintf(stderr, "Dump terminated\n"); + return -1; + } + delete_json_obj(); + + return 0; +} diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c new file mode 100644 index 0000000..18289ec --- /dev/null +++ b/ip/ipmacsec.c @@ -0,0 +1,1481 @@ +/* + * ipmacsec.c "ip macsec". + * + * 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. + * + * Authors: Sabrina Dubroca + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "ll_map.h" +#include "libgenl.h" + +static const char * const values_on_off[] = { "off", "on" }; + +static const char * const validate_str[] = { + [MACSEC_VALIDATE_DISABLED] = "disabled", + [MACSEC_VALIDATE_CHECK] = "check", + [MACSEC_VALIDATE_STRICT] = "strict", +}; + +static const char * const offload_str[] = { + [MACSEC_OFFLOAD_OFF] = "off", + [MACSEC_OFFLOAD_PHY] = "phy", + [MACSEC_OFFLOAD_MAC] = "mac", +}; + +struct sci { + __u64 sci; + __u16 port; + char abuf[6]; +}; + +struct sa_desc { + __u8 an; + __u32 pn; + __u8 key_id[MACSEC_KEYID_LEN]; + __u32 key_len; + __u8 key[MACSEC_MAX_KEY_LEN]; + __u8 active; +}; + +struct cipher_args { + __u64 id; + __u8 icv_len; +}; + +struct txsc_desc { + int ifindex; + __u64 sci; + __be16 port; + struct cipher_args cipher; + __u32 window; + enum macsec_validation_type validate; + __u8 encoding_sa; +}; + +struct rxsc_desc { + int ifindex; + __u64 sci; + __u8 active; +}; + +#define MACSEC_BUFLEN 1024 + + +/* netlink socket */ +static struct rtnl_handle genl_rth; +static int genl_family = -1; + +#define MACSEC_GENL_REQ(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, MACSEC_GENL_VERSION, \ + _cmd, _flags) + + +static void ipmacsec_usage(void) +{ + fprintf(stderr, + "Usage: ip macsec add DEV tx sa { 0..3 } [ OPTS ] key ID KEY\n" + " ip macsec set DEV tx sa { 0..3 } [ OPTS ]\n" + " ip macsec del DEV tx sa { 0..3 }\n" + " ip macsec add DEV rx SCI [ on | off ]\n" + " ip macsec set DEV rx SCI [ on | off ]\n" + " ip macsec del DEV rx SCI\n" + " ip macsec add DEV rx SCI sa { 0..3 } [ OPTS ] key ID KEY\n" + " ip macsec set DEV rx SCI sa { 0..3 } [ OPTS ]\n" + " ip macsec del DEV rx SCI sa { 0..3 }\n" + " ip macsec show\n" + " ip macsec show DEV\n" + " ip macsec offload DEV [ off | phy | mac ]\n" + "where OPTS := [ pn ] [ on | off ]\n" + " ID := 128-bit hex string\n" + " KEY := 128-bit or 256-bit hex string\n" + " SCI := { sci | port { 1..2^16-1 } address }\n"); + + exit(-1); +} + +static int one_of(const char *msg, const char *realval, const char * const *list, + size_t len, int *index) +{ + int i; + + for (i = 0; i < len; i++) { + if (matches(realval, list[i]) == 0) { + *index = i; + return 0; + } + } + + fprintf(stderr, "Error: argument of \"%s\" must be one of ", msg); + for (i = 0; i < len; i++) + fprintf(stderr, "\"%s\", ", list[i]); + fprintf(stderr, "not \"%s\"\n", realval); + return -1; +} + +static int get_an(__u8 *val, const char *arg) +{ + int ret = get_u8(val, arg, 0); + + if (ret) + return ret; + + if (*val > 3) + return -1; + + return 0; +} + +static int get_sci(__u64 *sci, const char *arg) +{ + return get_be64(sci, arg, 16); +} + +static int get_port(__be16 *port, const char *arg) +{ + return get_be16(port, arg, 0); +} + +#define _STR(a) #a +#define STR(a) _STR(a) + +static void get_icvlen(__u8 *icvlen, char *arg) +{ + int ret = get_u8(icvlen, arg, 10); + + if (ret) + invarg("expected ICV length", arg); + + if (*icvlen < MACSEC_MIN_ICV_LEN || *icvlen > MACSEC_STD_ICV_LEN) + invarg("ICV length must be in the range {" + STR(MACSEC_MIN_ICV_LEN) ".." STR(MACSEC_STD_ICV_LEN) + "}", arg); +} + +static bool get_sa(int *argcp, char ***argvp, __u8 *an) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (argc <= 0 || strcmp(*argv, "sa") != 0) + return false; + + NEXT_ARG(); + ret = get_an(an, *argv); + if (ret) + invarg("expected an { 0..3 }", *argv); + argc--; argv++; + + *argvp = argv; + *argcp = argc; + return true; +} + +static int parse_sa_args(int *argcp, char ***argvp, struct sa_desc *sa) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + bool active_set = false; + + while (argc > 0) { + if (strcmp(*argv, "pn") == 0) { + if (sa->pn != 0) + duparg2("pn", "pn"); + NEXT_ARG(); + ret = get_u32(&sa->pn, *argv, 0); + if (ret) + invarg("expected pn", *argv); + if (sa->pn == 0) + invarg("expected pn != 0", *argv); + } else if (strcmp(*argv, "key") == 0) { + unsigned int len; + + NEXT_ARG(); + if (!hexstring_a2n(*argv, sa->key_id, MACSEC_KEYID_LEN, + &len)) + invarg("expected key id", *argv); + NEXT_ARG(); + if (!hexstring_a2n(*argv, sa->key, MACSEC_MAX_KEY_LEN, + &sa->key_len)) + invarg("expected key", *argv); + } else if (strcmp(*argv, "on") == 0) { + if (active_set) + duparg2("on/off", "on"); + sa->active = true; + active_set = true; + } else if (strcmp(*argv, "off") == 0) { + if (active_set) + duparg2("on/off", "off"); + sa->active = false; + active_set = true; + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + ipmacsec_usage(); + } + + argv++; argc--; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +static __u64 make_sci(char *addr, __be16 port) +{ + __u64 sci; + + memcpy(&sci, addr, ETH_ALEN); + memcpy(((char *)&sci) + ETH_ALEN, &port, sizeof(port)); + + return sci; +} + +static bool sci_complete(bool sci, bool port, bool addr, bool port_only) +{ + return sci || (port && (addr || port_only)); +} + +static int get_sci_portaddr(struct sci *sci, int *argcp, char ***argvp, + bool port_only, bool optional) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + bool p = false, a = false, s = false; + + while (argc > 0) { + if (strcmp(*argv, "sci") == 0) { + if (p) + invarg("expected address", *argv); + if (a) + invarg("expected port", *argv); + NEXT_ARG(); + ret = get_sci(&sci->sci, *argv); + if (ret) + invarg("expected sci", *argv); + s = true; + } else if (strcmp(*argv, "port") == 0) { + NEXT_ARG(); + ret = get_port(&sci->port, *argv); + if (ret) + invarg("expected port", *argv); + if (sci->port == 0) + invarg("expected port != 0", *argv); + p = true; + } else if (strcmp(*argv, "address") == 0) { + NEXT_ARG(); + ret = ll_addr_a2n(sci->abuf, sizeof(sci->abuf), *argv); + if (ret < 0) + invarg("expected lladdr", *argv); + a = true; + } else if (optional) { + break; + } else { + invarg("expected sci, port, or address", *argv); + } + + argv++; argc--; + + if (sci_complete(s, p, a, port_only)) + break; + } + + if (!optional && !sci_complete(s, p, a, port_only)) + return -1; + + if (p && a) + sci->sci = make_sci(sci->abuf, sci->port); + + *argvp = argv; + *argcp = argc; + + return p || a || s; +} + +static bool parse_rxsci(int *argcp, char ***argvp, struct rxsc_desc *rxsc, + struct sa_desc *rxsa) +{ + struct sci sci = { 0 }; + + if (*argcp == 0 || + get_sci_portaddr(&sci, argcp, argvp, false, false) < 0) { + fprintf(stderr, "expected sci\n"); + ipmacsec_usage(); + } + + rxsc->sci = sci.sci; + + return get_sa(argcp, argvp, &rxsa->an); +} + +static int parse_rxsci_args(int *argcp, char ***argvp, struct rxsc_desc *rxsc) +{ + int argc = *argcp; + char **argv = *argvp; + bool active_set = false; + + while (argc > 0) { + if (strcmp(*argv, "on") == 0) { + if (active_set) + duparg2("on/off", "on"); + rxsc->active = true; + active_set = true; + } else if (strcmp(*argv, "off") == 0) { + if (active_set) + duparg2("on/off", "off"); + rxsc->active = false; + active_set = true; + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + ipmacsec_usage(); + } + + argv++; argc--; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +enum cmd { + CMD_ADD, + CMD_DEL, + CMD_UPD, + CMD_OFFLOAD, + __CMD_MAX +}; + +static const enum macsec_nl_commands macsec_commands[__CMD_MAX][2][2] = { + [CMD_ADD] = { + [0] = {-1, MACSEC_CMD_ADD_RXSC}, + [1] = {MACSEC_CMD_ADD_TXSA, MACSEC_CMD_ADD_RXSA}, + }, + [CMD_UPD] = { + [0] = {-1, MACSEC_CMD_UPD_RXSC}, + [1] = {MACSEC_CMD_UPD_TXSA, MACSEC_CMD_UPD_RXSA}, + }, + [CMD_DEL] = { + [0] = {-1, MACSEC_CMD_DEL_RXSC}, + [1] = {MACSEC_CMD_DEL_TXSA, MACSEC_CMD_DEL_RXSA}, + }, + [CMD_OFFLOAD] = { + [0] = {-1, MACSEC_CMD_UPD_OFFLOAD }, + }, +}; + +static int do_modify_nl(enum cmd c, enum macsec_nl_commands cmd, int ifindex, + struct rxsc_desc *rxsc, struct sa_desc *sa) +{ + struct rtattr *attr_sa; + + MACSEC_GENL_REQ(req, MACSEC_BUFLEN, cmd, NLM_F_REQUEST); + + addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex); + if (rxsc) { + struct rtattr *attr_rxsc; + + attr_rxsc = addattr_nest(&req.n, MACSEC_BUFLEN, + MACSEC_ATTR_RXSC_CONFIG); + addattr64(&req.n, MACSEC_BUFLEN, + MACSEC_RXSC_ATTR_SCI, rxsc->sci); + if (c != CMD_DEL && rxsc->active != 0xff) + addattr8(&req.n, MACSEC_BUFLEN, + MACSEC_RXSC_ATTR_ACTIVE, rxsc->active); + + addattr_nest_end(&req.n, attr_rxsc); + } + + if (sa->an == 0xff) + goto talk; + + attr_sa = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_SA_CONFIG); + + addattr8(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_AN, sa->an); + + if (c != CMD_DEL) { + if (sa->pn) + addattr32(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_PN, + sa->pn); + + if (sa->key_len) { + addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEYID, + sa->key_id, MACSEC_KEYID_LEN); + addattr_l(&req.n, MACSEC_BUFLEN, MACSEC_SA_ATTR_KEY, + sa->key, sa->key_len); + } + + if (sa->active != 0xff) { + addattr8(&req.n, MACSEC_BUFLEN, + MACSEC_SA_ATTR_ACTIVE, sa->active); + } + } + + addattr_nest_end(&req.n, attr_sa); + +talk: + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static bool check_sa_args(enum cmd c, struct sa_desc *sa) +{ + if (c == CMD_ADD) { + if (!sa->key_len) { + fprintf(stderr, "cannot create SA without key\n"); + return -1; + } + + if (sa->pn == 0) { + fprintf(stderr, "must specify a packet number != 0\n"); + return -1; + } + } else if (c == CMD_UPD) { + if (sa->key_len) { + fprintf(stderr, "cannot change key on SA\n"); + return -1; + } + } + + return 0; +} + +static int do_modify_txsa(enum cmd c, int argc, char **argv, int ifindex) +{ + struct sa_desc txsa = {0}; + enum macsec_nl_commands cmd; + + txsa.an = 0xff; + txsa.active = 0xff; + + if (argc == 0 || !get_sa(&argc, &argv, &txsa.an)) + ipmacsec_usage(); + + if (c == CMD_DEL) + goto modify; + + if (parse_sa_args(&argc, &argv, &txsa)) + return -1; + + if (check_sa_args(c, &txsa)) + return -1; + +modify: + cmd = macsec_commands[c][1][0]; + return do_modify_nl(c, cmd, ifindex, NULL, &txsa); +} + +static int do_modify_rxsci(enum cmd c, int argc, char **argv, int ifindex) +{ + struct rxsc_desc rxsc = {0}; + struct sa_desc rxsa = {0}; + bool sa_set; + enum macsec_nl_commands cmd; + + rxsc.ifindex = ifindex; + rxsc.active = 0xff; + rxsa.an = 0xff; + rxsa.active = 0xff; + + sa_set = parse_rxsci(&argc, &argv, &rxsc, &rxsa); + + if (c == CMD_DEL) + goto modify; + + if (sa_set && (parse_sa_args(&argc, &argv, &rxsa) || + check_sa_args(c, &rxsa))) + return -1; + if (!sa_set && parse_rxsci_args(&argc, &argv, &rxsc)) + return -1; + +modify: + cmd = macsec_commands[c][sa_set][1]; + return do_modify_nl(c, cmd, rxsc.ifindex, &rxsc, &rxsa); +} + +static int do_modify(enum cmd c, int argc, char **argv) +{ + int ifindex; + + if (argc == 0) + ipmacsec_usage(); + + ifindex = ll_name_to_index(*argv); + if (!ifindex) { + fprintf(stderr, "Device \"%s\" does not exist.\n", *argv); + return -1; + } + argc--; argv++; + + if (argc == 0) + ipmacsec_usage(); + + if (strcmp(*argv, "tx") == 0) + return do_modify_txsa(c, argc-1, argv+1, ifindex); + if (strcmp(*argv, "rx") == 0) + return do_modify_rxsci(c, argc-1, argv+1, ifindex); + + ipmacsec_usage(); + return -1; +} + +static int do_offload(enum cmd c, int argc, char **argv) +{ + enum macsec_offload offload; + struct rtattr *attr; + int ifindex, ret; + + if (argc == 0) + ipmacsec_usage(); + + ifindex = ll_name_to_index(*argv); + if (!ifindex) { + fprintf(stderr, "Device \"%s\" does not exist.\n", *argv); + return -1; + } + argc--; argv++; + + if (argc == 0) + ipmacsec_usage(); + + ret = one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str), + (int *)&offload); + if (ret) + ipmacsec_usage(); + + MACSEC_GENL_REQ(req, MACSEC_BUFLEN, macsec_commands[c][0][1], NLM_F_REQUEST); + + addattr32(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_IFINDEX, ifindex); + + attr = addattr_nest(&req.n, MACSEC_BUFLEN, MACSEC_ATTR_OFFLOAD); + addattr8(&req.n, MACSEC_BUFLEN, MACSEC_OFFLOAD_ATTR_TYPE, offload); + addattr_nest_end(&req.n, attr); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +/* dump/show */ +static struct { + int ifindex; + __u64 sci; +} filter; + +static int validate_dump(struct rtattr **attrs) +{ + return attrs[MACSEC_ATTR_IFINDEX] && attrs[MACSEC_ATTR_SECY] && + attrs[MACSEC_ATTR_TXSA_LIST] && attrs[MACSEC_ATTR_RXSC_LIST] && + attrs[MACSEC_ATTR_TXSC_STATS] && attrs[MACSEC_ATTR_SECY_STATS]; + +} + +static int validate_secy_dump(struct rtattr **attrs) +{ + return attrs[MACSEC_SECY_ATTR_SCI] && + attrs[MACSEC_SECY_ATTR_ENCODING_SA] && + attrs[MACSEC_SECY_ATTR_CIPHER_SUITE] && + attrs[MACSEC_SECY_ATTR_ICV_LEN] && + attrs[MACSEC_SECY_ATTR_PROTECT] && + attrs[MACSEC_SECY_ATTR_REPLAY] && + attrs[MACSEC_SECY_ATTR_OPER] && + attrs[MACSEC_SECY_ATTR_VALIDATE] && + attrs[MACSEC_SECY_ATTR_ENCRYPT] && + attrs[MACSEC_SECY_ATTR_INC_SCI] && + attrs[MACSEC_SECY_ATTR_ES] && + attrs[MACSEC_SECY_ATTR_SCB]; +} + +static void print_flag(struct rtattr *attrs[], const char *desc, + int field) +{ + __u8 flag; + + if (!attrs[field]) + return; + + flag = rta_getattr_u8(attrs[field]); + if (is_json_context()) + print_bool(PRINT_JSON, desc, NULL, flag); + else { + print_string(PRINT_FP, NULL, "%s ", desc); + print_string(PRINT_FP, NULL, "%s ", + flag ? "on" : "off"); + } +} + +static void print_key(struct rtattr *key) +{ + SPRINT_BUF(keyid); + + print_string(PRINT_ANY, "key", " key %s\n", + hexstring_n2a(RTA_DATA(key), RTA_PAYLOAD(key), + keyid, sizeof(keyid))); +} + +#define CIPHER_NAME_GCM_AES_128 "GCM-AES-128" +#define CIPHER_NAME_GCM_AES_256 "GCM-AES-256" +#define DEFAULT_CIPHER_NAME CIPHER_NAME_GCM_AES_128 + +static const char *cs_id_to_name(__u64 cid) +{ + switch (cid) { + case MACSEC_DEFAULT_CIPHER_ID: + return DEFAULT_CIPHER_NAME; + case MACSEC_CIPHER_ID_GCM_AES_128: + /* MACSEC_DEFAULT_CIPHER_ALT: */ + return CIPHER_NAME_GCM_AES_128; + case MACSEC_CIPHER_ID_GCM_AES_256: + return CIPHER_NAME_GCM_AES_256; + default: + return "(unknown)"; + } +} + +static const char *validate_to_str(__u8 validate) +{ + if (validate >= ARRAY_SIZE(validate_str)) + return "(unknown)"; + + return validate_str[validate]; +} + +static const char *offload_to_str(__u8 offload) +{ + if (offload >= ARRAY_SIZE(offload_str)) + return "(unknown)"; + + return offload_str[offload]; +} + +static void print_attrs(struct rtattr *attrs[]) +{ + print_flag(attrs, "protect", MACSEC_SECY_ATTR_PROTECT); + + if (attrs[MACSEC_SECY_ATTR_VALIDATE]) { + __u8 val = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_VALIDATE]); + + print_string(PRINT_ANY, "validate", + "validate %s ", validate_to_str(val)); + } + + print_flag(attrs, "sc", MACSEC_RXSC_ATTR_ACTIVE); + print_flag(attrs, "sa", MACSEC_SA_ATTR_ACTIVE); + print_flag(attrs, "encrypt", MACSEC_SECY_ATTR_ENCRYPT); + print_flag(attrs, "send_sci", MACSEC_SECY_ATTR_INC_SCI); + print_flag(attrs, "end_station", MACSEC_SECY_ATTR_ES); + print_flag(attrs, "scb", MACSEC_SECY_ATTR_SCB); + print_flag(attrs, "replay", MACSEC_SECY_ATTR_REPLAY); + + if (attrs[MACSEC_SECY_ATTR_WINDOW]) { + __u32 win = rta_getattr_u32(attrs[MACSEC_SECY_ATTR_WINDOW]); + + print_uint(PRINT_ANY, "window", "window %u ", win); + } + + if (attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]) { + __u64 cid = rta_getattr_u64(attrs[MACSEC_SECY_ATTR_CIPHER_SUITE]); + + print_nl(); + print_string(PRINT_ANY, "cipher_suite", + " cipher suite: %s,", cs_id_to_name(cid)); + } + + if (attrs[MACSEC_SECY_ATTR_ICV_LEN]) { + __u8 icv_len = rta_getattr_u8(attrs[MACSEC_SECY_ATTR_ICV_LEN]); + + print_uint(PRINT_ANY, "icv_length", + " using ICV length %u\n", icv_len); + } +} + +static __u64 getattr_u64(const struct rtattr *stat) +{ + size_t len = RTA_PAYLOAD(stat); + + switch (len) { + case sizeof(__u64): + return rta_getattr_u64(stat); + case sizeof(__u32): + return rta_getattr_u32(stat); + case sizeof(__u16): + return rta_getattr_u16(stat); + case sizeof(__u8): + return rta_getattr_u8(stat); + default: + fprintf(stderr, "invalid attribute length %zu\n", + len); + exit(-1); + } +} + +static void print_fp_stats(const char *prefix, + const char *names[], unsigned int num, + struct rtattr *stats[]) +{ + unsigned int i; + int pad; + + printf("%sstats:", prefix); + + for (i = 1; i < num; i++) { + if (!names[i]) + continue; + printf(" %s", names[i]); + } + + printf("\n%s ", prefix); + + for (i = 1; i < num; i++) { + if (!names[i]) + continue; + + pad = strlen(names[i]) + 1; + if (stats[i]) + printf("%*llu", pad, getattr_u64(stats[i])); + else + printf("%*c", pad, '-'); + } + printf("\n"); +} + +static void print_json_stats(const char *names[], unsigned int num, + struct rtattr *stats[]) +{ + unsigned int i; + + for (i = 1; i < num; i++) { + if (!names[i] || !stats[i]) + continue; + + print_u64(PRINT_JSON, names[i], + NULL, getattr_u64(stats[i])); + } +} + +static void print_stats(const char *prefix, + const char *names[], unsigned int num, + struct rtattr *stats[]) +{ + + if (is_json_context()) + print_json_stats(names, num, stats); + else + print_fp_stats(prefix, names, num, stats); +} + +static const char *txsc_stats_names[NUM_MACSEC_TXSC_STATS_ATTR] = { + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutOctetsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutOctetsEncrypted", +}; + +static void print_txsc_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_TXSC_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_TXSC_STATS_ATTR_MAX, attr); + + print_stats(prefix, txsc_stats_names, NUM_MACSEC_TXSC_STATS_ATTR, + stats); +} + +static const char *secy_stats_names[NUM_MACSEC_SECY_STATS_ATTR] = { + [MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED] = "OutPktsUntagged", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED] = "InPktsUntagged", + [MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG] = "OutPktsTooLong", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG] = "InPktsNoTag", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG] = "InPktsBadTag", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI] = "InPktsUnknownSCI", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI] = "InPktsNoSCI", + [MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN] = "InPktsOverrun", +}; + +static void print_secy_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SECY_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SECY_STATS_ATTR_MAX, attr); + + print_stats(prefix, secy_stats_names, + NUM_MACSEC_SECY_STATS_ATTR, stats); +} + +static const char *rxsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = { + [MACSEC_SA_STATS_ATTR_IN_PKTS_OK] = "InPktsOK", + [MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid", + [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid", + [MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA", + [MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA", +}; + +static void print_rxsa_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr); + + print_stats(prefix, rxsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats); +} + +static const char *txsa_stats_names[NUM_MACSEC_SA_STATS_ATTR] = { + [MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected", + [MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted", +}; + +static void print_txsa_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_SA_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_SA_STATS_ATTR_MAX, attr); + + print_stats(prefix, txsa_stats_names, NUM_MACSEC_SA_STATS_ATTR, stats); +} + +static void print_tx_sc(const char *prefix, __u64 sci, __u8 encoding_sa, + struct rtattr *txsc_stats, struct rtattr *secy_stats, + struct rtattr *sa) +{ + struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1]; + struct rtattr *a; + int rem; + + print_string(PRINT_FP, NULL, "%s", prefix); + print_0xhex(PRINT_ANY, "sci", + "TXSC: %016llx", ntohll(sci)); + print_uint(PRINT_ANY, "encoding_sa", + " on SA %d\n", encoding_sa); + + print_secy_stats(prefix, secy_stats); + print_txsc_stats(prefix, txsc_stats); + + open_json_array(PRINT_JSON, "sa_list"); + rem = RTA_PAYLOAD(sa); + for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) { + bool state; + + open_json_object(NULL); + parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a); + state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]); + + print_string(PRINT_FP, NULL, "%s", prefix); + print_string(PRINT_FP, NULL, "%s", prefix); + print_uint(PRINT_ANY, "an", "%d:", + rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN])); + print_uint(PRINT_ANY, "pn", " PN %u,", + rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN])); + + print_bool(PRINT_JSON, "active", NULL, state); + print_string(PRINT_FP, NULL, + " state %s,", state ? "on" : "off"); + print_key(sa_attr[MACSEC_SA_ATTR_KEYID]); + + print_txsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); +} + +static const char *rxsc_stats_names[NUM_MACSEC_RXSC_STATS_ATTR] = { + [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED] = "InOctetsValidated", + [MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED] = "InOctetsDecrypted", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED] = "InPktsUnchecked", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED] = "InPktsDelayed", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK] = "InPktsOK", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID] = "InPktsInvalid", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE] = "InPktsLate", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID] = "InPktsNotValid", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA] = "InPktsNotUsingSA", + [MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA] = "InPktsUnusedSA", +}; + +static void print_rxsc_stats(const char *prefix, struct rtattr *attr) +{ + struct rtattr *stats[MACSEC_RXSC_STATS_ATTR_MAX + 1]; + + if (!attr || show_stats == 0) + return; + + parse_rtattr_nested(stats, MACSEC_RXSC_STATS_ATTR_MAX, attr); + + print_stats(prefix, rxsc_stats_names, + NUM_MACSEC_RXSC_STATS_ATTR, stats); +} + +static void print_rx_sc(const char *prefix, __be64 sci, __u8 active, + struct rtattr *rxsc_stats, struct rtattr *sa) +{ + struct rtattr *sa_attr[MACSEC_SA_ATTR_MAX + 1]; + struct rtattr *a; + int rem; + + print_string(PRINT_FP, NULL, "%s", prefix); + print_0xhex(PRINT_ANY, "sci", + "RXSC: %016llx,", ntohll(sci)); + print_bool(PRINT_JSON, "active", NULL, active); + print_string(PRINT_FP, NULL, + " state %s\n", active ? "on" : "off"); + print_rxsc_stats(prefix, rxsc_stats); + + open_json_array(PRINT_JSON, "sa_list"); + rem = RTA_PAYLOAD(sa); + for (a = RTA_DATA(sa); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) { + bool state; + + open_json_object(NULL); + parse_rtattr_nested(sa_attr, MACSEC_SA_ATTR_MAX, a); + state = rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_ACTIVE]); + + print_string(PRINT_FP, NULL, "%s", prefix); + print_string(PRINT_FP, NULL, "%s", prefix); + print_uint(PRINT_ANY, "an", "%u:", + rta_getattr_u8(sa_attr[MACSEC_SA_ATTR_AN])); + print_uint(PRINT_ANY, "pn", " PN %u,", + rta_getattr_u32(sa_attr[MACSEC_SA_ATTR_PN])); + + print_bool(PRINT_JSON, "active", NULL, state); + print_string(PRINT_FP, NULL, " state %s,", + state ? "on" : "off"); + + print_key(sa_attr[MACSEC_SA_ATTR_KEYID]); + + print_rxsa_stats(prefix, sa_attr[MACSEC_SA_ATTR_STATS]); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); +} + +static void print_rxsc_list(struct rtattr *sc) +{ + int rem = RTA_PAYLOAD(sc); + struct rtattr *c; + + open_json_array(PRINT_JSON, "rx_sc"); + for (c = RTA_DATA(sc); RTA_OK(c, rem); c = RTA_NEXT(c, rem)) { + struct rtattr *sc_attr[MACSEC_RXSC_ATTR_MAX + 1]; + + open_json_object(NULL); + + parse_rtattr_nested(sc_attr, MACSEC_RXSC_ATTR_MAX, c); + print_rx_sc(" ", + rta_getattr_u64(sc_attr[MACSEC_RXSC_ATTR_SCI]), + rta_getattr_u32(sc_attr[MACSEC_RXSC_ATTR_ACTIVE]), + sc_attr[MACSEC_RXSC_ATTR_STATS], + sc_attr[MACSEC_RXSC_ATTR_SA_LIST]); + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); +} + +static int process(struct nlmsghdr *n, void *arg) +{ + struct genlmsghdr *ghdr; + struct rtattr *attrs[MACSEC_ATTR_MAX + 1]; + struct rtattr *attrs_secy[MACSEC_SECY_ATTR_MAX + 1]; + int len = n->nlmsg_len; + int ifindex; + __u64 sci; + __u8 encoding_sa; + + if (n->nlmsg_type != genl_family) + return -1; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + if (ghdr->cmd != MACSEC_CMD_GET_TXSC) + return 0; + + parse_rtattr(attrs, MACSEC_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + if (!validate_dump(attrs)) { + fprintf(stderr, "incomplete dump message\n"); + return -1; + } + + ifindex = rta_getattr_u32(attrs[MACSEC_ATTR_IFINDEX]); + parse_rtattr_nested(attrs_secy, MACSEC_SECY_ATTR_MAX, + attrs[MACSEC_ATTR_SECY]); + + if (!validate_secy_dump(attrs_secy)) { + fprintf(stderr, "incomplete dump message\n"); + return -1; + } + + sci = rta_getattr_u64(attrs_secy[MACSEC_SECY_ATTR_SCI]); + encoding_sa = rta_getattr_u8(attrs_secy[MACSEC_SECY_ATTR_ENCODING_SA]); + + if (filter.ifindex && ifindex != filter.ifindex) + return 0; + + if (filter.sci && sci != filter.sci) + return 0; + + open_json_object(NULL); + print_uint(PRINT_ANY, "ifindex", "%u: ", ifindex); + print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", + "%s: ", ll_index_to_name(ifindex)); + + print_attrs(attrs_secy); + + print_tx_sc(" ", sci, encoding_sa, + attrs[MACSEC_ATTR_TXSC_STATS], + attrs[MACSEC_ATTR_SECY_STATS], + attrs[MACSEC_ATTR_TXSA_LIST]); + + if (attrs[MACSEC_ATTR_RXSC_LIST]) + print_rxsc_list(attrs[MACSEC_ATTR_RXSC_LIST]); + + if (attrs[MACSEC_ATTR_OFFLOAD]) { + struct rtattr *attrs_offload[MACSEC_OFFLOAD_ATTR_MAX + 1]; + __u8 offload; + + parse_rtattr_nested(attrs_offload, MACSEC_OFFLOAD_ATTR_MAX, + attrs[MACSEC_ATTR_OFFLOAD]); + + offload = rta_getattr_u8(attrs_offload[MACSEC_OFFLOAD_ATTR_TYPE]); + print_string(PRINT_ANY, "offload", + " offload: %s ", offload_to_str(offload)); + print_nl(); + } + + close_json_object(); + + return 0; +} + +static int do_dump(int ifindex) +{ + MACSEC_GENL_REQ(req, MACSEC_BUFLEN, MACSEC_CMD_GET_TXSC, + NLM_F_REQUEST | NLM_F_DUMP); + + memset(&filter, 0, sizeof(filter)); + filter.ifindex = ifindex; + + req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq; + if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) { + perror("Failed to send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&genl_rth, process, stdout) < 0) { + delete_json_obj(); + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + + return 0; +} + +static int do_show(int argc, char **argv) +{ + int ifindex; + + if (argc == 0) + return do_dump(0); + + ifindex = ll_name_to_index(*argv); + if (ifindex == 0) { + fprintf(stderr, "Device \"%s\" does not exist.\n", *argv); + return -1; + } + + argc--, argv++; + if (argc == 0) + return do_dump(ifindex); + + ipmacsec_usage(); + return -1; +} + +int do_ipmacsec(int argc, char **argv) +{ + if (argc < 1) + ipmacsec_usage(); + + if (matches(*argv, "help") == 0) + ipmacsec_usage(); + + if (genl_init_handle(&genl_rth, MACSEC_GENL_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "show") == 0) + return do_show(argc-1, argv+1); + + if (matches(*argv, "add") == 0) + return do_modify(CMD_ADD, argc-1, argv+1); + if (matches(*argv, "set") == 0) + return do_modify(CMD_UPD, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_modify(CMD_DEL, argc-1, argv+1); + if (matches(*argv, "offload") == 0) + return do_offload(CMD_OFFLOAD, argc-1, argv+1); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip macsec help\".\n", + *argv); + exit(-1); +} + +/* device creation */ +static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_MACSEC_SCI]) { + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%016llx", + ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + print_string(PRINT_JSON, "sci", NULL, b1); + } else { + fprintf(f, "sci %016llx ", + ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + } + } + + print_flag(tb, "protect", IFLA_MACSEC_PROTECT); + + if (tb[IFLA_MACSEC_CIPHER_SUITE]) { + __u64 csid + = rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); + + print_string(PRINT_ANY, + "cipher_suite", + "cipher %s ", + cs_id_to_name(csid)); + } + + if (tb[IFLA_MACSEC_ICV_LEN]) { + if (is_json_context()) { + char b2[4]; + + snprintf(b2, sizeof(b2), "%hhu", + rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + print_uint(PRINT_JSON, "icv_len", NULL, atoi(b2)); + } else { + fprintf(f, "icvlen %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + } + } + + if (tb[IFLA_MACSEC_ENCODING_SA]) { + if (is_json_context()) { + char b2[4]; + + snprintf(b2, sizeof(b2), "%hhu", + rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + print_uint(PRINT_JSON, "encoding_sa", NULL, atoi(b2)); + } else { + fprintf(f, "encodingsa %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + } + } + + if (tb[IFLA_MACSEC_VALIDATION]) { + __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]); + + print_string(PRINT_ANY, + "validation", + "validate %s ", + validate_to_str(val)); + } + + if (tb[IFLA_MACSEC_OFFLOAD]) { + __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_OFFLOAD]); + + print_string(PRINT_ANY, + "offload", + "offload %s ", + offload_to_str(val)); + } + + const char *inc_sci, *es, *replay; + + if (is_json_context()) { + inc_sci = "inc_sci"; + replay = "replay_protect"; + es = "es"; + } else { + inc_sci = "send_sci"; + es = "end_station"; + replay = "replay"; + } + + print_flag(tb, "encrypt", IFLA_MACSEC_ENCRYPT); + print_flag(tb, inc_sci, IFLA_MACSEC_INC_SCI); + print_flag(tb, es, IFLA_MACSEC_ES); + print_flag(tb, "scb", IFLA_MACSEC_SCB); + print_flag(tb, replay, IFLA_MACSEC_REPLAY_PROTECT); + + if (tb[IFLA_MACSEC_WINDOW]) + print_int(PRINT_ANY, + "window", + "window %d ", + rta_getattr_u32(tb[IFLA_MACSEC_WINDOW])); +} + +static bool check_txsc_flags(bool es, bool scb, bool sci) +{ + if (sci && (es || scb)) + return false; + if (es && scb) + return false; + return true; +} + +static void usage(FILE *f) +{ + fprintf(f, + "Usage: ... macsec [ [ address ] port { 1..2^16-1 } | sci ]\n" + " [ cipher { default | gcm-aes-128 | gcm-aes-256 } ]\n" + " [ icvlen { 8..16 } ]\n" + " [ encrypt { on | off } ]\n" + " [ send_sci { on | off } ]\n" + " [ end_station { on | off } ]\n" + " [ scb { on | off } ]\n" + " [ protect { on | off } ]\n" + " [ replay { on | off} window { 0..2^32-1 } ]\n" + " [ validate { strict | check | disabled } ]\n" + " [ encodingsa { 0..3 } ]\n" + " [ offload { mac | phy | off } ]\n" + ); +} + +static int macsec_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + int ret; + __u8 encoding_sa = 0xff; + __u32 window = -1; + enum macsec_offload offload; + struct cipher_args cipher = {0}; + enum macsec_validation_type validate; + bool es = false, scb = false, send_sci = false; + int replay_protect = -1; + struct sci sci = { 0 }; + + ret = get_sci_portaddr(&sci, &argc, &argv, true, true); + if (ret < 0) { + fprintf(stderr, "expected sci\n"); + return -1; + } + + if (ret > 0) { + if (sci.sci) + addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_SCI, + &sci.sci, sizeof(sci.sci)); + else + addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_PORT, + &sci.port, sizeof(sci.port)); + } + + while (argc > 0) { + if (strcmp(*argv, "cipher") == 0) { + NEXT_ARG(); + if (cipher.id) + duparg("cipher", *argv); + if (strcmp(*argv, "default") == 0) + cipher.id = MACSEC_DEFAULT_CIPHER_ID; + else if (strcmp(*argv, "gcm-aes-128") == 0 || + strcmp(*argv, "GCM-AES-128") == 0) + cipher.id = MACSEC_CIPHER_ID_GCM_AES_128; + else if (strcmp(*argv, "gcm-aes-256") == 0 || + strcmp(*argv, "GCM-AES-256") == 0) + cipher.id = MACSEC_CIPHER_ID_GCM_AES_256; + else + invarg("expected: default, gcm-aes-128 or" + " gcm-aes-256", *argv); + } else if (strcmp(*argv, "icvlen") == 0) { + NEXT_ARG(); + if (cipher.icv_len) + duparg("icvlen", *argv); + get_icvlen(&cipher.icv_len, *argv); + } else if (strcmp(*argv, "encrypt") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("encrypt", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i); + } else if (strcmp(*argv, "send_sci") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("send_sci", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + send_sci = i; + addattr8(n, MACSEC_BUFLEN, + IFLA_MACSEC_INC_SCI, send_sci); + } else if (strcmp(*argv, "end_station") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("end_station", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + es = i; + addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ES, es); + } else if (strcmp(*argv, "scb") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("scb", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + scb = i; + addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_SCB, scb); + } else if (strcmp(*argv, "protect") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("protect", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i); + } else if (strcmp(*argv, "replay") == 0) { + NEXT_ARG(); + int i; + + ret = one_of("replay", *argv, values_on_off, + ARRAY_SIZE(values_on_off), &i); + if (ret != 0) + return ret; + replay_protect = !!i; + } else if (strcmp(*argv, "window") == 0) { + NEXT_ARG(); + ret = get_u32(&window, *argv, 0); + if (ret) + invarg("expected replay window size", *argv); + } else if (strcmp(*argv, "validate") == 0) { + NEXT_ARG(); + ret = one_of("validate", *argv, + validate_str, ARRAY_SIZE(validate_str), + (int *)&validate); + if (ret != 0) + return ret; + addattr8(n, MACSEC_BUFLEN, + IFLA_MACSEC_VALIDATION, validate); + } else if (strcmp(*argv, "encodingsa") == 0) { + if (encoding_sa != 0xff) + duparg2("encodingsa", "encodingsa"); + NEXT_ARG(); + ret = get_an(&encoding_sa, *argv); + if (ret) + invarg("expected an { 0..3 }", *argv); + } else if (strcmp(*argv, "offload") == 0) { + NEXT_ARG(); + ret = one_of("offload", *argv, + offload_str, ARRAY_SIZE(offload_str), + (int *)&offload); + if (ret != 0) + return ret; + addattr8(n, MACSEC_BUFLEN, + IFLA_MACSEC_OFFLOAD, offload); + } else { + fprintf(stderr, "macsec: unknown command \"%s\"?\n", + *argv); + usage(stderr); + return -1; + } + + argv++; argc--; + } + + if (!check_txsc_flags(es, scb, send_sci)) { + fprintf(stderr, + "invalid combination of send_sci/end_station/scb\n"); + return -1; + } + + if (window != -1 && replay_protect == -1) { + fprintf(stderr, + "replay window set, but replay protection not enabled. did you mean 'replay on window %u'?\n", + window); + return -1; + } else if (window == -1 && replay_protect == 1) { + fprintf(stderr, + "replay protection enabled, but no window set. did you mean 'replay on window VALUE'?\n"); + return -1; + } + + if (cipher.id) + addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_CIPHER_SUITE, + &cipher.id, sizeof(cipher.id)); + if (cipher.icv_len) + addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ICV_LEN, + &cipher.icv_len, sizeof(cipher.icv_len)); + + if (replay_protect != -1) { + addattr32(n, MACSEC_BUFLEN, IFLA_MACSEC_WINDOW, window); + addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_REPLAY_PROTECT, + replay_protect); + } + + if (encoding_sa != 0xff) { + addattr_l(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCODING_SA, + &encoding_sa, sizeof(encoding_sa)); + } + + return 0; +} + +static void macsec_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + usage(f); +} + +struct link_util macsec_link_util = { + .id = "macsec", + .maxattr = IFLA_MACSEC_MAX, + .parse_opt = macsec_parse_opt, + .print_help = macsec_print_help, + .print_opt = macsec_print_opt, +}; diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c new file mode 100644 index 0000000..d41ac63 --- /dev/null +++ b/ip/ipmaddr.c @@ -0,0 +1,374 @@ +/* + * ipmaddr.c "ip maddress". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +static struct { + char *dev; + int family; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n" + " ip maddr show [ dev STRING ]\n"); + exit(-1); +} + +static int parse_hex(char *str, unsigned char *addr, size_t size) +{ + int len = 0; + + while (*str && (len < 2 * size)) { + int tmp; + + if (str[1] == 0) + return -1; + if (sscanf(str, "%02x", &tmp) != 1) + return -1; + addr[len] = tmp; + len++; + str += 2; + } + return len; +} + +struct ma_info { + struct ma_info *next; + int index; + int users; + char *features; + char name[IFNAMSIZ]; + inet_prefix addr; +}; + +static void maddr_ins(struct ma_info **lst, struct ma_info *m) +{ + struct ma_info *mp; + + for (; (mp = *lst) != NULL; lst = &mp->next) { + if (mp->index > m->index) + break; + } + m->next = *lst; + *lst = m; +} + +static void read_dev_mcast(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/dev_mcast", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m = { .addr.family = AF_PACKET }; + int len; + int st; + + sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st, + hexa); + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data)); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + if (st) + ma->features = "static"; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +static void read_igmp(struct ma_info **result_p) +{ + struct ma_info m = { + .addr.family = AF_INET, + .addr.bitlen = 32, + .addr.bytelen = 4, + }; + char buf[256]; + FILE *fp = fopen("/proc/net/igmp", "r"); + + if (!fp) + return; + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + return; + } + + while (fgets(buf, sizeof(buf), fp)) { + struct ma_info *ma; + + if (buf[0] != '\t') { + size_t len; + + sscanf(buf, "%d%s", &m.index, m.name); + len = strlen(m.name); + if (m.name[len - 1] == ':') + m.name[len - 1] = '\0'; + continue; + } + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + sscanf(buf, "%08x%d", (__u32 *)&m.addr.data, &m.users); + + ma = malloc(sizeof(m)); + memcpy(ma, &m, sizeof(m)); + maddr_ins(result_p, ma); + } + fclose(fp); +} + + +static void read_igmp6(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/igmp6", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m = { .addr.family = AF_INET6 }; + int len; + + sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users); + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + len = parse_hex(hexa, (unsigned char *)&m.addr.data, sizeof(m.addr.data)); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +static void print_maddr(FILE *fp, struct ma_info *list) +{ + print_string(PRINT_FP, NULL, "\t", NULL); + + open_json_object(NULL); + if (list->addr.family == AF_PACKET) { + SPRINT_BUF(b1); + + print_string(PRINT_FP, NULL, "link ", NULL); + print_color_string(PRINT_ANY, COLOR_MAC, "link", "%s", + ll_addr_n2a((void *)list->addr.data, list->addr.bytelen, + 0, b1, sizeof(b1))); + } else { + print_string(PRINT_ANY, "family", "%-5s ", + family_name(list->addr.family)); + print_color_string(PRINT_ANY, ifa_family_color(list->addr.family), + "address", "%s", + format_host(list->addr.family, + -1, list->addr.data)); + } + + if (list->users != 1) + print_uint(PRINT_ANY, "users", " users %u", list->users); + + if (list->features) + print_string(PRINT_ANY, "features", " %s", list->features); + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); +} + +static void print_mlist(FILE *fp, struct ma_info *list) +{ + int cur_index = 0; + + new_json_obj(json); + for (; list; list = list->next) { + + if (list->index != cur_index || oneline) { + if (cur_index) { + close_json_array(PRINT_JSON, NULL); + close_json_object(); + } + open_json_object(NULL); + + print_uint(PRINT_ANY, "ifindex", "%d:", list->index); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "\t%s", list->name); + print_nl(); + cur_index = list->index; + + open_json_array(PRINT_JSON, "maddr"); + } + + print_maddr(fp, list); + } + if (cur_index) { + close_json_array(PRINT_JSON, NULL); + close_json_object(); + } + + delete_json_obj(); +} + +static int multiaddr_list(int argc, char **argv) +{ + struct ma_info *list = NULL; + + if (!filter.family) + filter.family = preferred_family; + + while (argc > 0) { + if (1) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } else if (matches(*argv, "help") == 0) + usage(); + if (filter.dev) + duparg2("dev", *argv); + filter.dev = *argv; + } + argv++; argc--; + } + + if (!filter.family || filter.family == AF_PACKET) + read_dev_mcast(&list); + if (!filter.family || filter.family == AF_INET) + read_igmp(&list); + if (!filter.family || filter.family == AF_INET6) + read_igmp6(&list); + print_mlist(stdout, list); + return 0; +} + +static int multiaddr_modify(int cmd, int argc, char **argv) +{ + struct ifreq ifr = {}; + int family; + int fd, len; + + if (cmd == RTM_NEWADDR) + cmd = SIOCADDMULTI; + else + cmd = SIOCDELMULTI; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (ifr.ifr_name[0]) + duparg("dev", *argv); + if (get_ifname(ifr.ifr_name, *argv)) + invarg("\"dev\" not a valid ifname", *argv); + } else { + if (matches(*argv, "address") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (ifr.ifr_hwaddr.sa_data[0]) + duparg("address", *argv); + len = ll_addr_a2n(ifr.ifr_hwaddr.sa_data, + sizeof(ifr.ifr_hwaddr.sa_data), + *argv); + if (len < 0) + exit(1); + + if (len != ETH_ALEN) { + fprintf(stderr, "Error: Invalid address length %d - must be %d bytes\n", len, ETH_ALEN); + exit(1); + } + } + argc--; argv++; + } + if (ifr.ifr_name[0] == 0) { + fprintf(stderr, "Not enough information: \"dev\" is required.\n"); + exit(-1); + } + + switch (preferred_family) { + case AF_INET6: + case AF_PACKET: + case AF_INET: + family = preferred_family; + break; + default: + family = AF_INET; + } + + fd = socket(family, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot create socket"); + exit(1); + } + if (ioctl(fd, cmd, (char *)&ifr) != 0) { + perror("ioctl"); + exit(1); + } + close(fd); + + exit(0); +} + + +int do_multiaddr(int argc, char **argv) +{ + if (argc < 1) + return multiaddr_list(0, NULL); + if (matches(*argv, "add") == 0) + return multiaddr_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return multiaddr_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return multiaddr_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip maddr help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c new file mode 100644 index 0000000..685be52 --- /dev/null +++ b/ip/ipmonitor.c @@ -0,0 +1,355 @@ +/* + * ipmonitor.c "ip monitor". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static void usage(void) __attribute__((noreturn)); +static int prefix_banner; +int listen_all_nsid; + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] [ label ] [all-nsid] [dev DEVICE]\n" + "LISTofOBJECTS := link | address | route | mroute | prefix |\n" + " neigh | netconf | rule | nsid\n" + "FILE := file FILENAME\n"); + exit(-1); +} + +static void print_headers(FILE *fp, char *label, struct rtnl_ctrl_data *ctrl) +{ + if (timestamp) + print_timestamp(fp); + + if (listen_all_nsid) { + if (ctrl == NULL || ctrl->nsid < 0) + fprintf(fp, "[nsid current]"); + else + fprintf(fp, "[nsid %d]", ctrl->nsid); + } + + if (prefix_banner) + fprintf(fp, "%s", label); +} + +static int accept_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + + switch (n->nlmsg_type) { + case RTM_NEWROUTE: + case RTM_DELROUTE: { + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_flags & RTM_F_CLONED) + return 0; + + if (r->rtm_family == RTNL_FAMILY_IPMR || + r->rtm_family == RTNL_FAMILY_IP6MR) { + print_headers(fp, "[MROUTE]", ctrl); + print_mroute(n, arg); + return 0; + } else { + print_headers(fp, "[ROUTE]", ctrl); + print_route(n, arg); + return 0; + } + } + + case RTM_NEWNEXTHOP: + case RTM_DELNEXTHOP: + print_headers(fp, "[NEXTHOP]", ctrl); + print_nexthop(n, arg); + return 0; + + case RTM_NEWLINK: + case RTM_DELLINK: + ll_remember_index(n, NULL); + print_headers(fp, "[LINK]", ctrl); + print_linkinfo(n, arg); + return 0; + + case RTM_NEWADDR: + case RTM_DELADDR: + print_headers(fp, "[ADDR]", ctrl); + print_addrinfo(n, arg); + return 0; + + case RTM_NEWADDRLABEL: + case RTM_DELADDRLABEL: + print_headers(fp, "[ADDRLABEL]", ctrl); + print_addrlabel(n, arg); + return 0; + + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + case RTM_GETNEIGH: + if (preferred_family) { + struct ndmsg *r = NLMSG_DATA(n); + + if (r->ndm_family != preferred_family) + return 0; + } + + print_headers(fp, "[NEIGH]", ctrl); + print_neigh(n, arg); + return 0; + + case RTM_NEWPREFIX: + print_headers(fp, "[PREFIX]", ctrl); + print_prefix(n, arg); + return 0; + + case RTM_NEWRULE: + case RTM_DELRULE: + print_headers(fp, "[RULE]", ctrl); + print_rule(n, arg); + return 0; + + case NLMSG_TSTAMP: + print_nlmsg_timestamp(fp, n); + return 0; + + case RTM_NEWNETCONF: + case RTM_DELNETCONF: + print_headers(fp, "[NETCONF]", ctrl); + print_netconf(ctrl, n, arg); + return 0; + + case RTM_DELNSID: + case RTM_NEWNSID: + print_headers(fp, "[NSID]", ctrl); + print_nsid(n, arg); + return 0; + + case NLMSG_ERROR: + case NLMSG_NOOP: + case NLMSG_DONE: + break; /* ignore */ + + default: + fprintf(stderr, + "Unknown message: type=0x%08x(%d) flags=0x%08x(%d) len=0x%08x(%d)\n", + n->nlmsg_type, n->nlmsg_type, + n->nlmsg_flags, n->nlmsg_flags, n->nlmsg_len, + n->nlmsg_len); + } + return 0; +} + +int do_ipmonitor(int argc, char **argv) +{ + int lnexthop = 0, nh_set = 1; + char *file = NULL; + unsigned int groups = 0; + int llink = 0; + int laddr = 0; + int lroute = 0; + int lmroute = 0; + int lprefix = 0; + int lneigh = 0; + int lnetconf = 0; + int lrule = 0; + int lnsid = 0; + int ifindex = 0; + + groups |= nl_mgrp(RTNLGRP_LINK); + groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); + groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR); + groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE); + groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); + groups |= nl_mgrp(RTNLGRP_MPLS_ROUTE); + groups |= nl_mgrp(RTNLGRP_IPV4_MROUTE); + groups |= nl_mgrp(RTNLGRP_IPV6_MROUTE); + groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX); + groups |= nl_mgrp(RTNLGRP_NEIGH); + groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF); + groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF); + groups |= nl_mgrp(RTNLGRP_IPV4_RULE); + groups |= nl_mgrp(RTNLGRP_IPV6_RULE); + groups |= nl_mgrp(RTNLGRP_NSID); + groups |= nl_mgrp(RTNLGRP_MPLS_NETCONF); + + rtnl_close(&rth); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (matches(*argv, "label") == 0) { + prefix_banner = 1; + } else if (matches(*argv, "link") == 0) { + llink = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "address") == 0) { + laddr = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "route") == 0) { + lroute = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "mroute") == 0) { + lmroute = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "prefix") == 0) { + lprefix = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "neigh") == 0) { + lneigh = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "netconf") == 0) { + lnetconf = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "rule") == 0) { + lrule = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "nsid") == 0) { + lnsid = 1; + groups = 0; + nh_set = 0; + } else if (matches(*argv, "nexthop") == 0) { + lnexthop = 1; + groups = 0; + } else if (strcmp(*argv, "all") == 0) { + prefix_banner = 1; + } else if (matches(*argv, "all-nsid") == 0) { + listen_all_nsid = 1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"ip monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + ipaddr_reset_filter(1, ifindex); + iproute_reset_filter(ifindex); + ipmroute_reset_filter(ifindex); + ipneigh_reset_filter(ifindex); + ipnetconf_reset_filter(ifindex); + + if (llink) + groups |= nl_mgrp(RTNLGRP_LINK); + if (laddr) { + if (!preferred_family || preferred_family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR); + } + if (lroute) { + if (!preferred_family || preferred_family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE); + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); + if (!preferred_family || preferred_family == AF_MPLS) + groups |= nl_mgrp(RTNLGRP_MPLS_ROUTE); + } + if (lmroute) { + if (!preferred_family || preferred_family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_MROUTE); + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_MROUTE); + } + if (lprefix) { + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_PREFIX); + } + if (lneigh) { + groups |= nl_mgrp(RTNLGRP_NEIGH); + } + if (lnetconf) { + if (!preferred_family || preferred_family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_NETCONF); + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_NETCONF); + if (!preferred_family || preferred_family == AF_MPLS) + groups |= nl_mgrp(RTNLGRP_MPLS_NETCONF); + } + if (lrule) { + if (!preferred_family || preferred_family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_RULE); + if (!preferred_family || preferred_family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_RULE); + } + if (lnsid) { + groups |= nl_mgrp(RTNLGRP_NSID); + } + if (nh_set) + lnexthop = 1; + + if (file) { + FILE *fp; + int err; + + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + err = rtnl_from_file(fp, accept_msg, stdout); + fclose(fp); + return err; + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (lnexthop && rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) { + fprintf(stderr, "Failed to add nexthop group to list\n"); + exit(1); + } + + if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0) + exit(1); + + ll_init_map(&rth); + netns_nsid_socket_init(); + netns_map_init(); + + if (rtnl_listen(&rth, accept_msg, stdout) < 0) + exit(2); + + return 0; +} diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c new file mode 100644 index 0000000..e1ffafb --- /dev/null +++ b/ip/ipmptcp.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include +#include + +#include "utils.h" +#include "ip_common.h" +#include "libgenl.h" +#include "json_print.h" + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n" + " [ FLAG-LIST ]\n" + " ip mptcp endpoint delete id ID\n" + " ip mptcp endpoint show [ id ID ]\n" + " ip mptcp endpoint flush\n" + " ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n" + " ip mptcp limits show\n" + "FLAG-LIST := [ FLAG-LIST ] FLAG\n" + "FLAG := [ signal | subflow | backup ]\n"); + + exit(-1); +} + +/* netlink socket */ +static struct rtnl_handle genl_rth = { .fd = -1 }; +static int genl_family = -1; + +#define MPTCP_BUFLEN 4096 +#define MPTCP_REQUEST(_req, _cmd, _flags) \ + GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0, \ + MPTCP_PM_VER, _cmd, _flags) + +/* Mapping from argument to address flag mask */ +static const struct { + const char *name; + unsigned long value; +} mptcp_addr_flag_names[] = { + { "signal", MPTCP_PM_ADDR_FLAG_SIGNAL }, + { "subflow", MPTCP_PM_ADDR_FLAG_SUBFLOW }, + { "backup", MPTCP_PM_ADDR_FLAG_BACKUP }, +}; + +static void print_mptcp_addr_flags(unsigned int flags) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) { + unsigned long mask = mptcp_addr_flag_names[i].value; + + if (flags & mask) { + print_string(PRINT_FP, NULL, "%s ", + mptcp_addr_flag_names[i].name); + print_bool(PRINT_JSON, + mptcp_addr_flag_names[i].name, NULL, true); + } + + flags &= ~mask; + } + + if (flags) { + /* unknown flags */ + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%02x", flags); + print_string(PRINT_ANY, "rawflags", "rawflags %s ", b1); + } +} + +static int get_flags(const char *arg, __u32 *flags) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) { + if (strcmp(arg, mptcp_addr_flag_names[i].name)) + continue; + + *flags |= mptcp_addr_flag_names[i].value; + return 0; + } + return -1; +} + +static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n, + bool adding) +{ + struct rtattr *attr_addr; + bool addr_set = false; + inet_prefix address; + bool id_set = false; + __u32 index = 0; + __u32 flags = 0; + __u8 id = 0; + + ll_init_map(&rth); + while (argc > 0) { + if (get_flags(*argv, &flags) == 0) { + } else if (matches(*argv, "id") == 0) { + NEXT_ARG(); + + if (get_u8(&id, *argv, 0)) + invarg("invalid ID\n", *argv); + id_set = true; + } else if (matches(*argv, "dev") == 0) { + const char *ifname; + + NEXT_ARG(); + + ifname = *argv; + + if (check_ifname(ifname)) + invarg("invalid interface name\n", ifname); + + index = ll_name_to_index(ifname); + + if (!index) + invarg("device does not exist\n", ifname); + + } else if (get_addr(&address, *argv, AF_UNSPEC) == 0) { + addr_set = true; + } else { + invarg("unknown argument", *argv); + } + NEXT_ARG_FWD(); + } + + if (!addr_set && adding) + missarg("ADDRESS"); + + if (!id_set && !adding) + missarg("ID"); + + attr_addr = addattr_nest(n, MPTCP_BUFLEN, + MPTCP_PM_ATTR_ADDR | NLA_F_NESTED); + if (id_set) + addattr8(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_ID, id); + if (flags) + addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags); + if (index) + addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index); + if (addr_set) { + int type; + + addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FAMILY, + address.family); + type = address.family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 : + MPTCP_PM_ADDR_ATTR_ADDR6; + addattr_l(n, MPTCP_BUFLEN, type, &address.data, + address.bytelen); + } + + addattr_nest_end(n, attr_addr); + return 0; +} + +static int mptcp_addr_modify(int argc, char **argv, int cmd) +{ + MPTCP_REQUEST(req, cmd, NLM_F_REQUEST); + int ret; + + ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR); + if (ret) + return ret; + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int print_mptcp_addrinfo(struct rtattr *addrinfo) +{ + struct rtattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1]; + __u8 family = AF_UNSPEC, addr_attr_type; + const char *ifname; + unsigned int flags; + int index; + __u16 id; + + parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo); + + open_json_object(NULL); + if (tb[MPTCP_PM_ADDR_ATTR_FAMILY]) + family = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_FAMILY]); + + addr_attr_type = family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 : + MPTCP_PM_ADDR_ATTR_ADDR6; + if (tb[addr_attr_type]) { + print_string(PRINT_ANY, "address", "%s ", + format_host_rta(family, tb[addr_attr_type])); + } + if (tb[MPTCP_PM_ADDR_ATTR_ID]) { + id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]); + print_uint(PRINT_ANY, "id", "id %u ", id); + } + if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) { + flags = rta_getattr_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); + print_mptcp_addr_flags(flags); + } + if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) { + index = rta_getattr_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]); + ifname = index ? ll_index_to_name(index) : NULL; + + if (ifname) + print_string(PRINT_ANY, "dev", "dev %s ", ifname); + } + + close_json_object(); + print_string(PRINT_FP, NULL, "\n", NULL); + fflush(stdout); + + return 0; +} + +static int print_mptcp_addr(struct nlmsghdr *n, void *arg) +{ + struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1]; + struct genlmsghdr *ghdr; + struct rtattr *addrinfo; + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return 0; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + parse_rtattr_flags(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, + len, NLA_F_NESTED); + addrinfo = tb[MPTCP_PM_ATTR_ADDR]; + if (!addrinfo) + return -1; + + ll_init_map(&rth); + return print_mptcp_addrinfo(addrinfo); +} + +static int mptcp_addr_dump(void) +{ + MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST | NLM_F_DUMP); + + if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) { + perror("Cannot send show request"); + exit(1); + } + + new_json_obj(json); + + if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + delete_json_obj(); + fflush(stdout); + return -2; + } + + close_json_object(); + fflush(stdout); + return 0; +} + +static int mptcp_addr_show(int argc, char **argv) +{ + MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST); + struct nlmsghdr *answer; + int ret; + + if (argc <= 0) + return mptcp_addr_dump(); + + ret = mptcp_parse_opt(argc, argv, &req.n, false); + if (ret) + return ret; + + if (rtnl_talk(&genl_rth, &req.n, &answer) < 0) + return -2; + + return print_mptcp_addr(answer, stdout); +} + +static int mptcp_addr_flush(int argc, char **argv) +{ + MPTCP_REQUEST(req, MPTCP_PM_CMD_FLUSH_ADDRS, NLM_F_REQUEST); + + if (rtnl_talk(&genl_rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int mptcp_parse_limit(int argc, char **argv, struct nlmsghdr *n) +{ + bool set_rcv_add_addrs = false; + bool set_subflows = false; + __u32 rcv_add_addrs = 0; + __u32 subflows = 0; + + while (argc > 0) { + if (matches(*argv, "subflows") == 0) { + NEXT_ARG(); + + if (get_u32(&subflows, *argv, 0)) + invarg("invalid subflows\n", *argv); + set_subflows = true; + } else if (matches(*argv, "add_addr_accepted") == 0) { + NEXT_ARG(); + + if (get_u32(&rcv_add_addrs, *argv, 0)) + invarg("invalid add_addr_accepted\n", *argv); + set_rcv_add_addrs = true; + } else { + invarg("unknown limit", *argv); + } + NEXT_ARG_FWD(); + } + + if (set_rcv_add_addrs) + addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_RCV_ADD_ADDRS, + rcv_add_addrs); + if (set_subflows) + addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_SUBFLOWS, subflows); + return set_rcv_add_addrs || set_subflows; +} + +static int print_mptcp_limit(struct nlmsghdr *n, void *arg) +{ + struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1]; + struct genlmsghdr *ghdr; + int len = n->nlmsg_len; + __u32 val; + + if (n->nlmsg_type != genl_family) + return 0; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + parse_rtattr(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len); + + open_json_object(NULL); + if (tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]) { + val = rta_getattr_u32(tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]); + + print_uint(PRINT_ANY, "add_addr_accepted", + "add_addr_accepted %d ", val); + } + + if (tb[MPTCP_PM_ATTR_SUBFLOWS]) { + val = rta_getattr_u32(tb[MPTCP_PM_ATTR_SUBFLOWS]); + + print_uint(PRINT_ANY, "subflows", "subflows %d ", val); + } + print_string(PRINT_FP, NULL, "%s", "\n"); + fflush(stdout); + close_json_object(); + return 0; +} + +static int mptcp_limit_get_set(int argc, char **argv, int cmd) +{ + bool do_get = cmd == MPTCP_PM_CMD_GET_LIMITS; + MPTCP_REQUEST(req, cmd, NLM_F_REQUEST); + struct nlmsghdr *answer; + int ret; + + ret = mptcp_parse_limit(argc, argv, &req.n); + if (ret < 0) + return -1; + + if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0) + return -2; + + if (do_get) + return print_mptcp_limit(answer, stdout); + return 0; +} + +int do_mptcp(int argc, char **argv) +{ + if (argc == 0) + usage(); + + if (matches(*argv, "help") == 0) + usage(); + + if (genl_init_handle(&genl_rth, MPTCP_PM_NAME, &genl_family)) + exit(1); + + if (matches(*argv, "endpoint") == 0) { + NEXT_ARG_FWD(); + if (argc == 0) + return mptcp_addr_show(0, NULL); + + if (matches(*argv, "add") == 0) + return mptcp_addr_modify(argc-1, argv+1, + MPTCP_PM_CMD_ADD_ADDR); + if (matches(*argv, "delete") == 0) + return mptcp_addr_modify(argc-1, argv+1, + MPTCP_PM_CMD_DEL_ADDR); + if (matches(*argv, "show") == 0) + return mptcp_addr_show(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return mptcp_addr_flush(argc-1, argv+1); + + goto unknown; + } + + if (matches(*argv, "limits") == 0) { + NEXT_ARG_FWD(); + if (argc == 0) + return mptcp_limit_get_set(0, NULL, + MPTCP_PM_CMD_GET_LIMITS); + + if (matches(*argv, "set") == 0) + return mptcp_limit_get_set(argc-1, argv+1, + MPTCP_PM_CMD_SET_LIMITS); + if (matches(*argv, "show") == 0) + return mptcp_limit_get_set(argc-1, argv+1, + MPTCP_PM_CMD_GET_LIMITS); + } + +unknown: + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n", + *argv); + exit(-1); +} diff --git a/ip/ipmroute.c b/ip/ipmroute.c new file mode 100644 index 0000000..656ea0d --- /dev/null +++ b/ip/ipmroute.c @@ -0,0 +1,340 @@ +/* + * ipmroute.c "ip mroute". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip mroute show [ [ to ] PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n" + " [ table TABLE_ID ]\n" + "TABLE_ID := [ local | main | default | all | NUMBER ]\n" +#if 0 + "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n" +#endif + ); + exit(-1); +} + +static struct rtfilter { + int tb; + int af; + int iif; + inet_prefix mdst; + inet_prefix msrc; +} filter; + +int print_mroute(struct nlmsghdr *n, void *arg) +{ + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[RTA_MAX+1]; + FILE *fp = arg; + const char *src, *dst; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + __u32 table; + int iif = 0; + int family; + + if ((n->nlmsg_type != RTM_NEWROUTE && + n->nlmsg_type != RTM_DELROUTE)) { + fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_type != RTN_MULTICAST) { + fprintf(stderr, + "Non multicast route received, kernel does support IP multicast?\n"); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + table = rtm_get_table(r, tb); + + if (filter.tb > 0 && filter.tb != table) + return 0; + + if (tb[RTA_IIF]) + iif = rta_getattr_u32(tb[RTA_IIF]); + if (filter.iif && filter.iif != iif) + return 0; + + if (filter.af && filter.af != r->rtm_family) + return 0; + + if (inet_addr_match_rta(&filter.mdst, tb[RTA_DST])) + return 0; + + if (inet_addr_match_rta(&filter.msrc, tb[RTA_SRC])) + return 0; + + family = get_real_family(r->rtm_type, r->rtm_family); + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELROUTE) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[RTA_SRC]) + src = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), b1, sizeof(b1)); + else + src = "unknown"; + + if (tb[RTA_DST]) + dst = rt_addr_n2a_r(family, RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), b2, sizeof(b2)); + else + dst = "unknown"; + + if (is_json_context()) { + print_string(PRINT_JSON, "src", NULL, src); + print_string(PRINT_JSON, "dst", NULL, dst); + } else { + char obuf[256]; + + snprintf(obuf, sizeof(obuf), "(%s,%s)", src, dst); + print_string(PRINT_FP, NULL, + "%-32s Iif: ", obuf); + } + + if (iif) + print_color_string(PRINT_ANY, COLOR_IFNAME, + "iif", "%-10s ", ll_index_to_name(iif)); + else + print_string(PRINT_ANY,"iif", "%s ", "unresolved"); + + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); + int first = 1; + + open_json_array(PRINT_JSON, "multipath"); + len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + + for (;;) { + if (len < sizeof(*nh)) + break; + if (nh->rtnh_len > len) + break; + + open_json_object(NULL); + if (first) { + print_string(PRINT_FP, NULL, "Oifs: ", NULL); + first = 0; + } + + print_color_string(PRINT_ANY, COLOR_IFNAME, + "oif", "%s", ll_index_to_name(nh->rtnh_ifindex)); + + if (nh->rtnh_hops > 1) + print_uint(PRINT_ANY, + "ttl", "(ttl %u) ", nh->rtnh_hops); + else + print_string(PRINT_FP, NULL, " ", NULL); + + close_json_object(); + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + } + close_json_array(PRINT_JSON, NULL); + } + + print_string(PRINT_ANY, "state", " State: %s", + (r->rtm_flags & RTNH_F_UNRESOLVED) ? "unresolved" : "resolved"); + + if (r->rtm_flags & RTNH_F_OFFLOAD) + print_null(PRINT_ANY, "offload", " offload", NULL); + + if (show_stats && tb[RTA_MFC_STATS]) { + struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); + + print_nl(); + print_u64(PRINT_ANY, "packets", " %"PRIu64" packets,", + mfcs->mfcs_packets); + print_u64(PRINT_ANY, "bytes", " %"PRIu64" bytes", mfcs->mfcs_bytes); + + if (mfcs->mfcs_wrong_if) + print_u64(PRINT_ANY, "wrong_if", + ", %"PRIu64" arrived on wrong iif.", + mfcs->mfcs_wrong_if); + } + + if (show_stats && tb[RTA_EXPIRES]) { + struct timeval tv; + double age; + + __jiffies_to_tv(&tv, rta_getattr_u64(tb[RTA_EXPIRES])); + age = tv.tv_sec; + age += tv.tv_usec / 1000000.; + print_float(PRINT_ANY, "expires", + ", Age %.2f", age); + } + + if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) + print_string(PRINT_ANY, "table", " Table: %s", + rtnl_rttable_n2a(table, b1, sizeof(b1))); + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); + fflush(fp); + return 0; +} + +void ipmroute_reset_filter(int ifindex) +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; + filter.iif = ifindex; +} + +static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter.tb) { + err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb); + if (err) + return err; + } + + return 0; +} + +static int mroute_list(int argc, char **argv) +{ + char *id = NULL; + int family = preferred_family; + + ipmroute_reset_filter(0); + if (family == AF_INET || family == AF_UNSPEC) { + family = RTNL_FAMILY_IPMR; + filter.af = RTNL_FAMILY_IPMR; + filter.tb = RT_TABLE_DEFAULT; /* for backward compatibility */ + } else if (family == AF_INET6) { + family = RTNL_FAMILY_IP6MR; + filter.af = RTNL_FAMILY_IP6MR; + } else { + /* family does not have multicast routing */ + return 0; + } + + filter.msrc.family = filter.mdst.family = family; + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + filter.tb = 0; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } else + filter.tb = tid; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (get_prefix(&filter.msrc, *argv, family)) + invarg("from value is invalid\n", *argv); + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (get_prefix(&filter.mdst, *argv, family)) + invarg("to value is invalid\n", *argv); + } + argc--; argv++; + } + + ll_init_map(&rth); + + if (id) { + int idx; + + idx = ll_name_to_index(id); + if (!idx) + return nodev(id); + filter.iif = idx; + } + + if (rtnl_routedump_req(&rth, filter.af, iproute_dump_filter) < 0) { + perror("Cannot send dump request"); + return 1; + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_mroute, stdout) < 0) { + delete_json_obj(); + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + + return 0; +} + +int do_multiroute(int argc, char **argv) +{ + if (argc < 1) + return mroute_list(0, NULL); +#if 0 + if (matches(*argv, "add") == 0) + return mroute_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return mroute_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return mroute_get(argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return mroute_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipneigh.c b/ip/ipneigh.c new file mode 100644 index 0000000..678b403 --- /dev/null +++ b/ip/ipneigh.c @@ -0,0 +1,706 @@ +/* + * ipneigh.c "ip neigh". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) +#define MAX_ROUNDS 10 + +static struct +{ + int family; + int index; + int state; + int unused_only; + inet_prefix pfx; + int flushed; + char *flushb; + int flushp; + int flushe; + int master; + int protocol; + __u8 ndm_flags; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip neigh { add | del | change | replace }\n" + " { ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } [ dev DEV ]\n" + " [ router ] [ extern_learn ] [ protocol PROTO ]\n" + "\n" + " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n" + " [ vrf NAME ]\n" + " ip neigh get { ADDR | proxy ADDR } dev DEV\n" + "\n" + "STATE := { permanent | noarp | stale | reachable | none |\n" + " incomplete | delay | probe | failed }\n"); + exit(-1); +} + +static int nud_state_a2n(unsigned int *state, const char *arg) +{ + if (matches(arg, "permanent") == 0) + *state = NUD_PERMANENT; + else if (matches(arg, "reachable") == 0) + *state = NUD_REACHABLE; + else if (strcmp(arg, "noarp") == 0) + *state = NUD_NOARP; + else if (strcmp(arg, "none") == 0) + *state = NUD_NONE; + else if (strcmp(arg, "stale") == 0) + *state = NUD_STALE; + else if (strcmp(arg, "incomplete") == 0) + *state = NUD_INCOMPLETE; + else if (strcmp(arg, "delay") == 0) + *state = NUD_DELAY; + else if (strcmp(arg, "probe") == 0) + *state = NUD_PROBE; + else if (matches(arg, "failed") == 0) + *state = NUD_FAILED; + else { + if (get_unsigned(state, arg, 0)) + return -1; + if (*state >= 0x100 || (*state&((*state)-1))) + return -1; + } + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request"); + return -1; + } + filter.flushp = 0; + return 0; +} + + +static int ipneigh_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndm.ndm_family = preferred_family, + .ndm.ndm_state = NUD_PERMANENT, + }; + char *dev = NULL; + int dst_ok = 0; + int dev_ok = 0; + int lladdr_ok = 0; + char *lla = NULL; + inet_prefix dst; + + while (argc > 0) { + if (matches(*argv, "lladdr") == 0) { + NEXT_ARG(); + if (lladdr_ok) + duparg("lladdr", *argv); + lla = *argv; + lladdr_ok = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned int state; + + NEXT_ARG(); + if (nud_state_a2n(&state, *argv)) + invarg("nud state is bad", *argv); + req.ndm.ndm_state = state; + } else if (matches(*argv, "proxy") == 0) { + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg("address", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + dev_ok = 1; + req.ndm.ndm_flags |= NTF_PROXY; + } else if (strcmp(*argv, "router") == 0) { + req.ndm.ndm_flags |= NTF_ROUTER; + } else if (matches(*argv, "extern_learn") == 0) { + req.ndm.ndm_flags |= NTF_EXT_LEARNED; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + dev = *argv; + dev_ok = 1; + } else if (matches(*argv, "protocol") == 0) { + __u32 proto; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&proto, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + if (addattr8(&req.n, sizeof(req), NDA_PROTOCOL, proto)) + return -1; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) { + NEXT_ARG(); + } + if (dst_ok) + duparg2("to", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + } + argc--; argv++; + } + if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) { + fprintf(stderr, "Device and destination are required arguments.\n"); + exit(-1); + } + req.ndm.ndm_family = dst.family; + if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0) + return -1; + + if (lla && strcmp(lla, "null")) { + char llabuf[20]; + int l; + + l = ll_addr_a2n(llabuf, sizeof(llabuf), lla); + if (l < 0) + return -1; + + if (addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l) < 0) + return -1; + } + + ll_init_map(&rth); + + if (dev) { + req.ndm.ndm_ifindex = ll_name_to_index(dev); + if (!req.ndm.ndm_ifindex) + return nodev(dev); + } + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + return 0; +} + +static void print_cacheinfo(const struct nda_cacheinfo *ci) +{ + static int hz; + + if (!hz) + hz = get_user_hz(); + + if (ci->ndm_refcnt) + print_uint(PRINT_ANY, "refcnt", + " ref %u", ci->ndm_refcnt); + + print_uint(PRINT_ANY, "used", " used %u", ci->ndm_used / hz); + print_uint(PRINT_ANY, "confirmed", "/%u", ci->ndm_confirmed / hz); + print_uint(PRINT_ANY, "updated", "/%u", ci->ndm_updated / hz); +} + +static void print_neigh_state(unsigned int nud) +{ + + open_json_array(PRINT_JSON, + is_json_context() ? "state" : ""); + +#define PRINT_FLAG(f) \ + if (nud & NUD_##f) { \ + nud &= ~NUD_##f; \ + print_string(PRINT_ANY, NULL, " %s", #f); \ + } + + PRINT_FLAG(INCOMPLETE); + PRINT_FLAG(REACHABLE); + PRINT_FLAG(STALE); + PRINT_FLAG(DELAY); + PRINT_FLAG(PROBE); + PRINT_FLAG(FAILED); + PRINT_FLAG(NOARP); + PRINT_FLAG(PERMANENT); +#undef PRINT_FLAG + + close_json_array(PRINT_JSON, NULL); +} + +int print_neigh(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[NDA_MAX+1]; + static int logit = 1; + __u8 protocol = 0; + + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH && + n->nlmsg_type != RTM_GETNEIGH) { + fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + + return 0; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + if (filter.family && filter.family != r->ndm_family) + return 0; + if (filter.index && filter.index != r->ndm_ifindex) + return 0; + if (!(filter.state&r->ndm_state) && + !(r->ndm_flags & NTF_PROXY) && + !(r->ndm_flags & NTF_EXT_LEARNED) && + (r->ndm_state || !(filter.state&0x100)) && + (r->ndm_family != AF_DECnet)) + return 0; + + if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) { + if (logit) { + logit = 0; + fprintf(fp, + "\nWARNING: Kernel does not support filtering by master device\n\n"); + } + } + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (inet_addr_match_rta(&filter.pfx, tb[NDA_DST])) + return 0; + + if (tb[NDA_PROTOCOL]) + protocol = rta_getattr_u8(tb[NDA_PROTOCOL]); + + if (filter.protocol && filter.protocol != protocol) + return 0; + + if (filter.unused_only && tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + + if (ci->ndm_refcnt) + return 0; + } + + if (filter.flushb) { + struct nlmsghdr *fn; + + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELNEIGH; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++rth.seq; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELNEIGH) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + else if (n->nlmsg_type == RTM_GETNEIGH) + print_null(PRINT_ANY, "miss", "%s ", "miss"); + + if (tb[NDA_DST]) { + const char *dst; + int family = r->ndm_family; + + if (family == AF_BRIDGE) { + if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr)) + family = AF_INET6; + else + family = AF_INET; + } + + dst = format_host_rta(family, tb[NDA_DST]); + print_color_string(PRINT_ANY, + ifa_family_color(family), + "dst", "%s ", dst); + } + + if (!filter.index && r->ndm_ifindex) { + if (!is_json_context()) + fprintf(fp, "dev "); + + print_color_string(PRINT_ANY, COLOR_IFNAME, + "dev", "%s ", + ll_index_to_name(r->ndm_ifindex)); + } + + if (tb[NDA_LLADDR]) { + const char *lladdr; + SPRINT_BUF(b1); + + lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1)); + + if (!is_json_context()) + fprintf(fp, "lladdr "); + + print_color_string(PRINT_ANY, COLOR_MAC, + "lladdr", "%s", lladdr); + } + + if (r->ndm_flags & NTF_ROUTER) + print_null(PRINT_ANY, "router", " %s", "router"); + + if (r->ndm_flags & NTF_PROXY) + print_null(PRINT_ANY, "proxy", " %s", "proxy"); + + if (r->ndm_flags & NTF_EXT_LEARNED) + print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn"); + + if (r->ndm_flags & NTF_OFFLOADED) + print_null(PRINT_ANY, "offload", " %s", "offload"); + + if (show_stats) { + if (tb[NDA_CACHEINFO]) + print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO])); + + if (tb[NDA_PROBES]) + print_uint(PRINT_ANY, "probes", " probes %u", + rta_getattr_u32(tb[NDA_PROBES])); + } + + if (r->ndm_state) + print_neigh_state(r->ndm_state); + + if (protocol) { + SPRINT_BUF(b1); + + print_string(PRINT_ANY, "protocol", " proto %s ", + rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); + } + + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + fflush(stdout); + + return 0; +} + +void ipneigh_reset_filter(int ifindex) +{ + memset(&filter, 0, sizeof(filter)); + filter.state = ~0; + filter.index = ifindex; +} + +static int ipneigh_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + struct ndmsg *ndm = NLMSG_DATA(nlh); + int err; + + ndm->ndm_flags = filter.ndm_flags; + + if (filter.index) { + err = addattr32(nlh, reqlen, NDA_IFINDEX, filter.index); + if (err) + return err; + } + if (filter.master) { + err = addattr32(nlh, reqlen, NDA_MASTER, filter.master); + if (err) + return err; + } + + return 0; +} + +static int do_show_or_flush(int argc, char **argv, int flush) +{ + char *filter_dev = NULL; + int state_given = 0; + + ipneigh_reset_filter(0); + + if (!filter.family) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + filter.state = ~(NUD_PERMANENT|NUD_NOARP); + } else + filter.state = 0xFF & ~NUD_NOARP; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } else if (strcmp(*argv, "master") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + filter.master = ifindex; + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + filter.master = ifindex; + } else if (strcmp(*argv, "unused") == 0) { + filter.unused_only = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned int state; + + NEXT_ARG(); + if (!state_given) { + state_given = 1; + filter.state = 0; + } + if (nud_state_a2n(&state, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("nud state is bad", *argv); + state = ~0; + if (flush) + state &= ~NUD_NOARP; + } + if (state == 0) + state = 0x100; + filter.state |= state; + } else if (strcmp(*argv, "proxy") == 0) { + filter.ndm_flags = NTF_PROXY; + } else if (matches(*argv, "protocol") == 0) { + __u32 prot; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all")) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + } + filter.protocol = prot; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (get_prefix(&filter.pfx, *argv, filter.family)) + invarg("to value is invalid\n", *argv); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } + argc--; argv++; + } + + ll_init_map(&rth); + + if (filter_dev) { + filter.index = ll_name_to_index(filter_dev); + if (!filter.index) + return nodev(filter_dev); + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + + while (round < MAX_ROUNDS) { + if (rtnl_neighdump_req(&rth, filter.family, + ipneigh_dump_filter) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (show_stats) { + if (round == 0) + printf("Nothing to flush.\n"); + else + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); + } + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + filter.state &= ~NUD_FAILED; + } + printf("*** Flush not complete bailing out after %d rounds\n", + MAX_ROUNDS); + return 1; + } + + if (rtnl_neighdump_req(&rth, filter.family, ipneigh_dump_filter) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + + return 0; +} + +static int ipneigh_get(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNEIGH, + .ndm.ndm_family = preferred_family, + }; + struct nlmsghdr *answer; + char *d = NULL; + int dst_ok = 0; + int dev_ok = 0; + inet_prefix dst; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + dev_ok = 1; + } else if (matches(*argv, "proxy") == 0) { + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg("address", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + dev_ok = 1; + req.ndm.ndm_flags |= NTF_PROXY; + } else { + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + } + argc--; argv++; + } + + if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) { + fprintf(stderr, "Device and address are required arguments.\n"); + return -1; + } + + req.ndm.ndm_family = dst.family; + if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0) + return -1; + + if (d) { + req.ndm.ndm_ifindex = ll_name_to_index(d); + if (!req.ndm.ndm_ifindex) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + } + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + ipneigh_reset_filter(0); + if (print_neigh(answer, stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + return -1; + } + + return 0; +} + +int do_ipneigh(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "change") == 0 || + strcmp(*argv, "chg") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return ipneigh_get(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return do_show_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show_or_flush(0, NULL, 0); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c new file mode 100644 index 0000000..0e946ca --- /dev/null +++ b/ip/ipnetconf.c @@ -0,0 +1,256 @@ +/* + * ipnetconf.c "ip netconf". + * + * 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. + * + * Authors: Nicolas Dichtel, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static struct { + int family; + int ifindex; +} filter; + +static const char * const rp_filter_names[] = { + "off", "strict", "loose" +}; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip netconf show [ dev STRING ]\n"); + exit(-1); +} + +static void print_onoff(FILE *fp, const char *flag, __u32 val) +{ + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, val); + else + fprintf(fp, "%s %s ", flag, val ? "on" : "off"); +} + +static struct rtattr *netconf_rta(struct netconfmsg *ncm) +{ + return (struct rtattr *)((char *)ncm + + NLMSG_ALIGN(sizeof(struct netconfmsg))); +} + +int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct netconfmsg *ncm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[NETCONFA_MAX+1]; + int ifindex = 0; + + if (n->nlmsg_type == NLMSG_ERROR) + return -1; + + if (n->nlmsg_type != RTM_NEWNETCONF && + n->nlmsg_type != RTM_DELNETCONF) { + fprintf(stderr, "Not a netconf message: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + + return -1; + } + len -= NLMSG_SPACE(sizeof(*ncm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.family && filter.family != ncm->ncm_family) + return 0; + + parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), + NLMSG_PAYLOAD(n, sizeof(*ncm))); + + if (tb[NETCONFA_IFINDEX]) + ifindex = rta_getattr_u32(tb[NETCONFA_IFINDEX]); + + if (filter.ifindex && filter.ifindex != ifindex) + return 0; + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELNETCONF) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + print_string(PRINT_ANY, "family", + "%s ", family_name(ncm->ncm_family)); + + if (tb[NETCONFA_IFINDEX]) { + const char *dev; + + switch (ifindex) { + case NETCONFA_IFINDEX_ALL: + dev = "all"; + break; + case NETCONFA_IFINDEX_DEFAULT: + dev = "default"; + break; + default: + dev = ll_index_to_name(ifindex); + break; + } + print_color_string(PRINT_ANY, COLOR_IFNAME, + "interface", "%s ", dev); + } + + if (tb[NETCONFA_FORWARDING]) + print_onoff(fp, "forwarding", + rta_getattr_u32(tb[NETCONFA_FORWARDING])); + + if (tb[NETCONFA_RP_FILTER]) { + __u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]); + + if (rp_filter < ARRAY_SIZE(rp_filter_names)) + print_string(PRINT_ANY, "rp_filter", + "rp_filter %s ", + rp_filter_names[rp_filter]); + else + print_uint(PRINT_ANY, "rp_filter", + "rp_filter %u ", rp_filter); + } + + if (tb[NETCONFA_MC_FORWARDING]) + print_onoff(fp, "mc_forwarding", + rta_getattr_u32(tb[NETCONFA_MC_FORWARDING])); + + if (tb[NETCONFA_PROXY_NEIGH]) + print_onoff(fp, "proxy_neigh", + rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH])); + + if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) + print_onoff(fp, "ignore_routes_with_linkdown", + rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])); + + if (tb[NETCONFA_INPUT]) + print_onoff(fp, "input", rta_getattr_u32(tb[NETCONFA_INPUT])); + + close_json_object(); + print_string(PRINT_FP, NULL, "\n", NULL); + fflush(fp); + return 0; +} + +static int print_netconf2(struct nlmsghdr *n, void *arg) +{ + return print_netconf(NULL, n, arg); +} + +void ipnetconf_reset_filter(int ifindex) +{ + memset(&filter, 0, sizeof(filter)); + filter.ifindex = ifindex; +} + +static int do_show(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct netconfmsg ncm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .n.nlmsg_type = RTM_GETNETCONF, + }; + + ipnetconf_reset_filter(0); + filter.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + filter.ifindex = ll_name_to_index(*argv); + if (filter.ifindex <= 0) { + fprintf(stderr, + "Device \"%s\" does not exist.\n", + *argv); + return -1; + } + } + argv++; argc--; + } + + ll_init_map(&rth); + + if (filter.ifindex && filter.family != AF_UNSPEC) { + req.ncm.ncm_family = filter.family; + addattr_l(&req.n, sizeof(req), NETCONFA_IFINDEX, + &filter.ifindex, sizeof(filter.ifindex)); + + if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { + perror("Can not send request"); + exit(1); + } + rtnl_listen(&rth, print_netconf, stdout); + } else { + rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR; +dump: + if (rtnl_netconfdump_req(&rth, filter.family) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_netconf2, stdout) < 0) { + /* kernel does not support netconf dump on AF_UNSPEC; + * fall back to requesting by family + */ + if (errno == EOPNOTSUPP && + filter.family == AF_UNSPEC) { + filter.family = AF_INET; + goto dump; + } + perror("RTNETLINK answers"); + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + if (preferred_family == AF_UNSPEC && filter.family == AF_INET) { + preferred_family = AF_INET6; + filter.family = AF_INET6; + goto dump; + } + } + return 0; +} + +int do_ipnetconf(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip netconf help\".\n", + *argv); + exit(-1); +} diff --git a/ip/ipnetns.c b/ip/ipnetns.c new file mode 100644 index 0000000..14e8e08 --- /dev/null +++ b/ip/ipnetns.c @@ -0,0 +1,1058 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define _ATFILE_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils.h" +#include "list.h" +#include "ip_common.h" +#include "namespace.h" +#include "json_print.h" + +static int usage(void) +{ + fprintf(stderr, + "Usage: ip netns list\n" + " ip netns add NAME\n" + " ip netns attach NAME PID\n" + " ip netns set NAME NETNSID\n" + " ip [-all] netns delete [NAME]\n" + " ip netns identify [PID]\n" + " ip netns pids NAME\n" + " ip [-all] netns exec [NAME] cmd ...\n" + " ip netns monitor\n" + " ip netns list-id [target-nsid POSITIVE-INT] [nsid POSITIVE-INT]\n" + "NETNSID := auto | POSITIVE-INT\n"); + exit(-1); +} + +/* This socket is used to get nsid */ +static struct rtnl_handle rtnsh = { .fd = -1 }; + +static int have_rtnl_getnsid = -1; +static int saved_netns = -1; +static struct link_filter filter; + +static int ipnetns_accept_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); + + if (n->nlmsg_type == NLMSG_ERROR && + (err->error == -EOPNOTSUPP || err->error == -EINVAL)) + have_rtnl_getnsid = 0; + else + have_rtnl_getnsid = 1; + return -1; +} + +static int ipnetns_have_nsid(void) +{ + struct { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; + int fd; + + if (have_rtnl_getnsid >= 0) { + fd = open("/proc/self/ns/net", O_RDONLY); + if (fd < 0) { + fprintf(stderr, + "/proc/self/ns/net: %s. Continuing anyway.\n", + strerror(errno)); + have_rtnl_getnsid = 0; + return 0; + } + + addattr32(&req.n, 1024, NETNSA_FD, fd); + + if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { + fprintf(stderr, + "rtnl_send(RTM_GETNSID): %s. Continuing anyway.\n", + strerror(errno)); + have_rtnl_getnsid = 0; + close(fd); + return 0; + } + rtnl_listen(&rth, ipnetns_accept_msg, NULL); + close(fd); + } + + return have_rtnl_getnsid; +} + +int get_netnsid_from_name(const char *name) +{ + struct { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; + struct nlmsghdr *answer; + struct rtattr *tb[NETNSA_MAX + 1]; + struct rtgenmsg *rthdr; + int len, fd, ret = -1; + + netns_nsid_socket_init(); + + fd = netns_get_fd(name); + if (fd < 0) + return fd; + + addattr32(&req.n, 1024, NETNSA_FD, fd); + if (rtnl_talk(&rtnsh, &req.n, &answer) < 0) { + close(fd); + return -2; + } + close(fd); + + /* Validate message and parse attributes */ + if (answer->nlmsg_type == NLMSG_ERROR) + goto out; + + rthdr = NLMSG_DATA(answer); + len = answer->nlmsg_len - NLMSG_SPACE(sizeof(*rthdr)); + if (len < 0) + goto out; + + parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len); + + if (tb[NETNSA_NSID]) { + ret = rta_getattr_s32(tb[NETNSA_NSID]); + } + +out: + free(answer); + return ret; +} + +struct nsid_cache { + struct hlist_node nsid_hash; + struct hlist_node name_hash; + int nsid; + char name[0]; +}; + +#define NSIDMAP_SIZE 128 +#define NSID_HASH_NSID(nsid) (nsid & (NSIDMAP_SIZE - 1)) +#define NSID_HASH_NAME(name) (namehash(name) & (NSIDMAP_SIZE - 1)) + +static struct hlist_head nsid_head[NSIDMAP_SIZE]; +static struct hlist_head name_head[NSIDMAP_SIZE]; + +static struct nsid_cache *netns_map_get_by_nsid(int nsid) +{ + struct hlist_node *n; + uint32_t h; + + if (nsid < 0) + return NULL; + + h = NSID_HASH_NSID(nsid); + hlist_for_each(n, &nsid_head[h]) { + struct nsid_cache *c = container_of(n, struct nsid_cache, + nsid_hash); + if (c->nsid == nsid) + return c; + } + + return NULL; +} + +char *get_name_from_nsid(int nsid) +{ + struct nsid_cache *c; + + if (nsid < 0) + return NULL; + + netns_nsid_socket_init(); + netns_map_init(); + + c = netns_map_get_by_nsid(nsid); + if (c) + return c->name; + + return NULL; +} + +static int netns_map_add(int nsid, const char *name) +{ + struct nsid_cache *c; + uint32_t h; + + if (netns_map_get_by_nsid(nsid) != NULL) + return -EEXIST; + + c = malloc(sizeof(*c) + strlen(name) + 1); + if (c == NULL) { + perror("malloc"); + return -ENOMEM; + } + c->nsid = nsid; + strcpy(c->name, name); + + h = NSID_HASH_NSID(nsid); + hlist_add_head(&c->nsid_hash, &nsid_head[h]); + + h = NSID_HASH_NAME(name); + hlist_add_head(&c->name_hash, &name_head[h]); + + return 0; +} + +static void netns_map_del(struct nsid_cache *c) +{ + hlist_del(&c->name_hash); + hlist_del(&c->nsid_hash); + free(c); +} + +void netns_nsid_socket_init(void) +{ + if (rtnsh.fd > -1 || !ipnetns_have_nsid()) + return; + + if (rtnl_open(&rtnsh, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + +} + +void netns_map_init(void) +{ + static int initialized; + struct dirent *entry; + DIR *dir; + int nsid; + + if (initialized || !ipnetns_have_nsid()) + return; + + dir = opendir(NETNS_RUN_DIR); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + nsid = get_netnsid_from_name(entry->d_name); + + if (nsid >= 0) + netns_map_add(nsid, entry->d_name); + } + closedir(dir); + initialized = 1; +} + +static int netns_get_name(int nsid, char *name) +{ + struct dirent *entry; + DIR *dir; + int id; + + if (nsid < 0) + return -EINVAL; + + dir = opendir(NETNS_RUN_DIR); + if (!dir) + return -ENOENT; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + id = get_netnsid_from_name(entry->d_name); + + if (id >= 0 && nsid == id) { + strcpy(name, entry->d_name); + closedir(dir); + return 0; + } + } + closedir(dir); + return -ENOENT; +} + +int print_nsid(struct nlmsghdr *n, void *arg) +{ + struct rtgenmsg *rthdr = NLMSG_DATA(n); + struct rtattr *tb[NETNSA_MAX+1]; + int len = n->nlmsg_len; + FILE *fp = (FILE *)arg; + struct nsid_cache *c; + char name[NAME_MAX]; + int nsid, current; + + if (n->nlmsg_type != RTM_NEWNSID && n->nlmsg_type != RTM_DELNSID) + return 0; + + len -= NLMSG_SPACE(sizeof(*rthdr)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d in %s\n", len, + __func__); + return -1; + } + + parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len); + if (tb[NETNSA_NSID] == NULL) { + fprintf(stderr, "BUG: NETNSA_NSID is missing %s\n", __func__); + return -1; + } + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELNSID) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + nsid = rta_getattr_s32(tb[NETNSA_NSID]); + if (nsid < 0) + print_string(PRINT_FP, NULL, "nsid unassigned ", NULL); + else + print_int(PRINT_ANY, "nsid", "nsid %d ", nsid); + + if (tb[NETNSA_CURRENT_NSID]) { + current = rta_getattr_s32(tb[NETNSA_CURRENT_NSID]); + if (current < 0) + print_string(PRINT_FP, NULL, + "current-nsid unassigned ", NULL); + else + print_int(PRINT_ANY, "current-nsid", + "current-nsid %d ", current); + } + + c = netns_map_get_by_nsid(tb[NETNSA_CURRENT_NSID] ? current : nsid); + if (c != NULL) { + print_string(PRINT_ANY, "name", + "(iproute2 netns name: %s)", c->name); + netns_map_del(c); + } + + /* nsid might not be in cache */ + if (c == NULL && n->nlmsg_type == RTM_NEWNSID) + if (netns_get_name(nsid, name) == 0) { + print_string(PRINT_ANY, "name", + "(iproute2 netns name: %s)", name); + netns_map_add(nsid, name); + } + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); + fflush(fp); + return 0; +} + +static int get_netnsid_from_netnsid(int nsid) +{ + struct { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtgenmsg))), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNSID, + .g.rtgen_family = AF_UNSPEC, + }; + struct nlmsghdr *answer; + int err; + + netns_nsid_socket_init(); + + err = addattr32(&req.n, sizeof(req), NETNSA_NSID, nsid); + if (err) + return err; + + if (filter.target_nsid >= 0) { + err = addattr32(&req.n, sizeof(req), NETNSA_TARGET_NSID, + filter.target_nsid); + if (err) + return err; + } + + if (rtnl_talk(&rtnsh, &req.n, &answer) < 0) + return -2; + + /* Validate message and parse attributes */ + if (answer->nlmsg_type == NLMSG_ERROR) + goto err_out; + + new_json_obj(json); + err = print_nsid(answer, stdout); + delete_json_obj(); +err_out: + free(answer); + return err; +} + +static int netns_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter.target_nsid >= 0) { + err = addattr32(nlh, reqlen, NETNSA_TARGET_NSID, + filter.target_nsid); + if (err) + return err; + } + + return 0; +} + +static int netns_list_id(int argc, char **argv) +{ + int nsid = -1; + + if (!ipnetns_have_nsid()) { + fprintf(stderr, + "RTM_GETNSID is not supported by the kernel.\n"); + return -ENOTSUP; + } + + filter.target_nsid = -1; + while (argc > 0) { + if (strcmp(*argv, "target-nsid") == 0) { + if (filter.target_nsid >= 0) + duparg("target-nsid", *argv); + NEXT_ARG(); + + if (get_integer(&filter.target_nsid, *argv, 0)) + invarg("\"target-nsid\" value is invalid", + *argv); + else if (filter.target_nsid < 0) + invarg("\"target-nsid\" value should be >= 0", + argv[1]); + } else if (strcmp(*argv, "nsid") == 0) { + if (nsid >= 0) + duparg("nsid", *argv); + NEXT_ARG(); + + if (get_integer(&nsid, *argv, 0)) + invarg("\"nsid\" value is invalid", *argv); + else if (nsid < 0) + invarg("\"nsid\" value should be >= 0", + argv[1]); + } else + usage(); + argc--; argv++; + } + + if (nsid >= 0) + return get_netnsid_from_netnsid(nsid); + + if (rtnl_nsiddump_req_filter_fn(&rth, AF_UNSPEC, + netns_filter_req) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_nsid, stdout) < 0) { + delete_json_obj(); + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + return 0; +} + +static int netns_list(int argc, char **argv) +{ + struct dirent *entry; + DIR *dir; + int id; + + dir = opendir(NETNS_RUN_DIR); + if (!dir) + return 0; + + new_json_obj(json); + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + + open_json_object(NULL); + print_string(PRINT_ANY, "name", + "%s", entry->d_name); + if (ipnetns_have_nsid()) { + id = get_netnsid_from_name(entry->d_name); + if (id >= 0) + print_int(PRINT_ANY, "id", " (id: %d)", id); + } + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); + } + closedir(dir); + delete_json_obj(); + return 0; +} + +static int do_switch(void *arg) +{ + char *netns = arg; + + /* we just changed namespaces. clear any vrf association + * with prior namespace before exec'ing command + */ + vrf_reset(); + + return netns_switch(netns); +} + +static int on_netns_exec(char *nsname, void *arg) +{ + char **argv = arg; + + printf("\nnetns: %s\n", nsname); + cmd_exec(argv[0], argv, true, do_switch, nsname); + return 0; +} + +static int netns_exec(int argc, char **argv) +{ + /* Setup the proper environment for apps that are not netns + * aware, and execute a program in that environment. + */ + if (argc < 1 && !do_all) { + fprintf(stderr, "No netns name specified\n"); + return -1; + } + if ((argc < 2 && !do_all) || (argc < 1 && do_all)) { + fprintf(stderr, "No command specified\n"); + return -1; + } + + if (do_all) + return netns_foreach(on_netns_exec, argv); + + /* ip must return the status of the child, + * but do_cmd() will add a minus to this, + * so let's add another one here to cancel it. + */ + return -cmd_exec(argv[1], argv + 1, !!batch_mode, do_switch, argv[0]); +} + +static int is_pid(const char *str) +{ + int ch; + + for (; (ch = *str); str++) { + if (!isdigit(ch)) + return 0; + } + return 1; +} + +static int netns_pids(int argc, char **argv) +{ + const char *name; + char net_path[PATH_MAX]; + int netns; + struct stat netst; + DIR *dir; + struct dirent *entry; + + if (argc < 1) { + fprintf(stderr, "No netns name specified\n"); + return -1; + } + if (argc > 1) { + fprintf(stderr, "extra arguments specified\n"); + return -1; + } + + name = argv[0]; + snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); + netns = open(net_path, O_RDONLY); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace: %s\n", + strerror(errno)); + return -1; + } + if (fstat(netns, &netst) < 0) { + fprintf(stderr, "Stat of netns failed: %s\n", + strerror(errno)); + return -1; + } + dir = opendir("/proc/"); + if (!dir) { + fprintf(stderr, "Open of /proc failed: %s\n", + strerror(errno)); + return -1; + } + while ((entry = readdir(dir))) { + char pid_net_path[PATH_MAX]; + struct stat st; + + if (!is_pid(entry->d_name)) + continue; + snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net", + entry->d_name); + if (stat(pid_net_path, &st) != 0) + continue; + if ((st.st_dev == netst.st_dev) && + (st.st_ino == netst.st_ino)) { + printf("%s\n", entry->d_name); + } + } + closedir(dir); + return 0; + +} + +int netns_identify_pid(const char *pidstr, char *name, int len) +{ + char net_path[PATH_MAX]; + int netns; + struct stat netst; + DIR *dir; + struct dirent *entry; + + name[0] = '\0'; + + snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr); + netns = open(net_path, O_RDONLY); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace: %s\n", + strerror(errno)); + return -1; + } + if (fstat(netns, &netst) < 0) { + fprintf(stderr, "Stat of netns failed: %s\n", + strerror(errno)); + return -1; + } + dir = opendir(NETNS_RUN_DIR); + if (!dir) { + /* Succeed treat a missing directory as an empty directory */ + if (errno == ENOENT) + return 0; + + fprintf(stderr, "Failed to open directory %s:%s\n", + NETNS_RUN_DIR, strerror(errno)); + return -1; + } + + while ((entry = readdir(dir))) { + char name_path[PATH_MAX]; + struct stat st; + + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + + snprintf(name_path, sizeof(name_path), "%s/%s", NETNS_RUN_DIR, + entry->d_name); + + if (stat(name_path, &st) != 0) + continue; + + if ((st.st_dev == netst.st_dev) && + (st.st_ino == netst.st_ino)) { + strlcpy(name, entry->d_name, len); + } + } + closedir(dir); + return 0; + +} + +static int netns_identify(int argc, char **argv) +{ + const char *pidstr; + char name[256]; + int rc; + + if (argc < 1) { + pidstr = "self"; + } else if (argc > 1) { + fprintf(stderr, "extra arguments specified\n"); + return -1; + } else { + pidstr = argv[0]; + if (!is_pid(pidstr)) { + fprintf(stderr, "Specified string '%s' is not a pid\n", + pidstr); + return -1; + } + } + + rc = netns_identify_pid(pidstr, name, sizeof(name)); + if (!rc) + printf("%s\n", name); + + return rc; +} + +static int on_netns_del(char *nsname, void *arg) +{ + char netns_path[PATH_MAX]; + + snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, nsname); + umount2(netns_path, MNT_DETACH); + if (unlink(netns_path) < 0) { + fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n", + netns_path, strerror(errno)); + return -1; + } + return 0; +} + +static int netns_delete(int argc, char **argv) +{ + if (argc < 1 && !do_all) { + fprintf(stderr, "No netns name specified\n"); + return -1; + } + + if (do_all) + return netns_foreach(on_netns_del, NULL); + + return on_netns_del(argv[0], NULL); +} + +static int create_netns_dir(void) +{ + /* Create the base netns directory if it doesn't exist */ + if (mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { + if (errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", + NETNS_RUN_DIR, strerror(errno)); + return -1; + } + } + + return 0; +} + +/* Obtain a FD for the current namespace, so we can reenter it later */ +static void netns_save(void) +{ + if (saved_netns != -1) + return; + + saved_netns = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC); + if (saved_netns == -1) { + perror("Cannot open init namespace"); + exit(1); + } +} + +static void netns_restore(void) +{ + if (saved_netns == -1) + return; + + if (setns(saved_netns, CLONE_NEWNET)) { + perror("setns"); + exit(1); + } + + close(saved_netns); + saved_netns = -1; +} + +static int netns_add(int argc, char **argv, bool create) +{ + /* This function creates a new network namespace and + * a new mount namespace and bind them into a well known + * location in the filesystem based on the name provided. + * + * If create is true, a new namespace will be created, + * otherwise an existing one will be attached to the file. + * + * The mount namespace is created so that any necessary + * userspace tweaks like remounting /sys, or bind mounting + * a new /etc/resolv.conf can be shared between users. + */ + char netns_path[PATH_MAX], proc_path[PATH_MAX]; + const char *name; + pid_t pid; + int fd; + int made_netns_run_dir_mount = 0; + + if (create) { + if (argc < 1) { + fprintf(stderr, "No netns name specified\n"); + return -1; + } + } else { + if (argc < 2) { + fprintf(stderr, "No netns name and PID specified\n"); + return -1; + } + + if (get_s32(&pid, argv[1], 0) || !pid) { + fprintf(stderr, "Invalid PID: %s\n", argv[1]); + return -1; + } + } + name = argv[0]; + + snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); + + if (create_netns_dir()) + return -1; + + /* Make it possible for network namespace mounts to propagate between + * mount namespaces. This makes it likely that a unmounting a network + * namespace file in one namespace will unmount the network namespace + * file in all namespaces allowing the network namespace to be freed + * sooner. + */ + while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) { + /* Fail unless we need to make the mount point */ + if (errno != EINVAL || made_netns_run_dir_mount) { + fprintf(stderr, "mount --make-shared %s failed: %s\n", + NETNS_RUN_DIR, strerror(errno)); + return -1; + } + + /* Upgrade NETNS_RUN_DIR to a mount point */ + if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND | MS_REC, NULL)) { + fprintf(stderr, "mount --bind %s %s failed: %s\n", + NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno)); + return -1; + } + made_netns_run_dir_mount = 1; + } + + /* Create the filesystem state */ + fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0); + if (fd < 0) { + fprintf(stderr, "Cannot create namespace file \"%s\": %s\n", + netns_path, strerror(errno)); + return -1; + } + close(fd); + + if (create) { + netns_save(); + if (unshare(CLONE_NEWNET) < 0) { + fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n", + name, strerror(errno)); + goto out_delete; + } + + strcpy(proc_path, "/proc/self/ns/net"); + } else { + snprintf(proc_path, sizeof(proc_path), "/proc/%d/ns/net", pid); + } + + /* Bind the netns last so I can watch for it */ + if (mount(proc_path, netns_path, "none", MS_BIND, NULL) < 0) { + fprintf(stderr, "Bind %s -> %s failed: %s\n", + proc_path, netns_path, strerror(errno)); + goto out_delete; + } + netns_restore(); + + return 0; +out_delete: + if (create) { + netns_restore(); + netns_delete(argc, argv); + } else if (unlink(netns_path) < 0) { + fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n", + netns_path, strerror(errno)); + } + return -1; +} + +int set_netnsid_from_name(const char *name, int nsid) +{ + struct { + struct nlmsghdr n; + struct rtgenmsg g; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_NEWNSID, + .g.rtgen_family = AF_UNSPEC, + }; + int fd, err = 0; + + netns_nsid_socket_init(); + + fd = netns_get_fd(name); + if (fd < 0) + return fd; + + addattr32(&req.n, 1024, NETNSA_FD, fd); + addattr32(&req.n, 1024, NETNSA_NSID, nsid); + if (rtnl_talk(&rth, &req.n, NULL) < 0) + err = -2; + + close(fd); + return err; +} + +static int netns_set(int argc, char **argv) +{ + char netns_path[PATH_MAX]; + const char *name; + int netns, nsid; + + if (argc < 1) { + fprintf(stderr, "No netns name specified\n"); + return -1; + } + if (argc < 2) { + fprintf(stderr, "No nsid specified\n"); + return -1; + } + name = argv[0]; + /* If a negative nsid is specified the kernel will select the nsid. */ + if (strcmp(argv[1], "auto") == 0) + nsid = -1; + else if (get_integer(&nsid, argv[1], 0)) + invarg("Invalid \"netnsid\" value", argv[1]); + else if (nsid < 0) + invarg("\"netnsid\" value should be >= 0", argv[1]); + + snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); + netns = open(netns_path, O_RDONLY | O_CLOEXEC); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace \"%s\": %s\n", + name, strerror(errno)); + return -1; + } + + return set_netnsid_from_name(name, nsid); +} + +static int netns_monitor(int argc, char **argv) +{ + char buf[4096]; + struct inotify_event *event; + int fd; + + fd = inotify_init(); + if (fd < 0) { + fprintf(stderr, "inotify_init failed: %s\n", + strerror(errno)); + return -1; + } + + if (create_netns_dir()) + return -1; + + if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) { + fprintf(stderr, "inotify_add_watch failed: %s\n", + strerror(errno)); + return -1; + } + for (;;) { + ssize_t len = read(fd, buf, sizeof(buf)); + + if (len < 0) { + fprintf(stderr, "read failed: %s\n", + strerror(errno)); + return -1; + } + for (event = (struct inotify_event *)buf; + (char *)event < &buf[len]; + event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) { + if (event->mask & IN_CREATE) + printf("add %s\n", event->name); + if (event->mask & IN_DELETE) + printf("delete %s\n", event->name); + } + } + return 0; +} + +static int invalid_name(const char *name) +{ + return !*name || strlen(name) > NAME_MAX || + strchr(name, '/') || !strcmp(name, ".") || !strcmp(name, ".."); +} + +int do_netns(int argc, char **argv) +{ + netns_nsid_socket_init(); + + if (argc < 1) { + netns_map_init(); + return netns_list(0, NULL); + } + + if (!do_all && argc > 1 && invalid_name(argv[1])) { + fprintf(stderr, "Invalid netns name \"%s\"\n", argv[1]); + exit(-1); + } + + if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) || + (matches(*argv, "lst") == 0)) { + netns_map_init(); + return netns_list(argc-1, argv+1); + } + + if ((matches(*argv, "list-id") == 0)) { + netns_map_init(); + return netns_list_id(argc-1, argv+1); + } + + if (matches(*argv, "help") == 0) + return usage(); + + if (matches(*argv, "add") == 0) + return netns_add(argc-1, argv+1, true); + + if (matches(*argv, "set") == 0) + return netns_set(argc-1, argv+1); + + if (matches(*argv, "delete") == 0) + return netns_delete(argc-1, argv+1); + + if (matches(*argv, "identify") == 0) + return netns_identify(argc-1, argv+1); + + if (matches(*argv, "pids") == 0) + return netns_pids(argc-1, argv+1); + + if (matches(*argv, "exec") == 0) + return netns_exec(argc-1, argv+1); + + if (matches(*argv, "monitor") == 0) + return netns_monitor(argc-1, argv+1); + + if (matches(*argv, "attach") == 0) + return netns_add(argc-1, argv+1, false); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c new file mode 100644 index 0000000..22c6649 --- /dev/null +++ b/ip/ipnexthop.c @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ip nexthop + * + * Copyright (c) 2017-19 David Ahern + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +static struct { + unsigned int flushed; + unsigned int groups; + unsigned int ifindex; + unsigned int master; + unsigned int proto; + unsigned int fdb; +} filter; + +enum { + IPNH_LIST, + IPNH_FLUSH, +}; + +#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \ + NLMSG_ALIGN(sizeof(struct nhmsg)))) + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n" + " ip nexthop { add | replace } id ID NH [ protocol ID ]\n" + " ip nexthop { get| del } id ID\n" + "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n" + " [ groups ] [ fdb ]\n" + "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n" + " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n" + "GROUP := [ id[,weight]>//... ]\n" + "ENCAPTYPE := [ mpls ]\n" + "ENCAPHDR := [ MPLSLABEL ]\n"); + exit(-1); +} + +static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter.ifindex) { + err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex); + if (err) + return err; + } + + if (filter.groups) { + err = addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0); + if (err) + return err; + } + + if (filter.master) { + err = addattr32(nlh, reqlen, NHA_MASTER, filter.master); + if (err) + return err; + } + + if (filter.fdb) { + err = addattr_l(nlh, reqlen, NHA_FDB, NULL, 0); + if (err) + return err; + } + + return 0; +} + +static struct rtnl_handle rth_del = { .fd = -1 }; + +static int delete_nexthop(__u32 id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_DELNEXTHOP, + .nhm.nh_family = AF_UNSPEC, + }; + + req.n.nlmsg_seq = ++rth_del.seq; + + addattr32(&req.n, sizeof(req), NHA_ID, id); + + if (rtnl_talk(&rth_del, &req.n, NULL) < 0) + return -1; + return 0; +} + +static int flush_nexthop(struct nlmsghdr *nlh, void *arg) +{ + struct nhmsg *nhm = NLMSG_DATA(nlh); + struct rtattr *tb[NHA_MAX+1]; + __u32 id = 0; + int len; + + len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.proto && nhm->nh_protocol != filter.proto) + return 0; + + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + if (tb[NHA_ID]) + id = rta_getattr_u32(tb[NHA_ID]); + + if (id && !delete_nexthop(id)) + filter.flushed++; + + return 0; +} + +static int ipnh_flush(unsigned int all) +{ + int rc = -2; + + if (all) { + filter.groups = 1; + filter.ifindex = 0; + filter.master = 0; + } + + if (rtnl_open(&rth_del, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return EXIT_FAILURE; + } +again: + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { + perror("Cannot send dump request"); + goto out; + } + + if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) { + fprintf(stderr, "Dump terminated. Failed to flush nexthops\n"); + goto out; + } + + /* if deleting all, then remove groups first */ + if (all && filter.groups) { + filter.groups = 0; + goto again; + } + + rc = 0; +out: + rtnl_close(&rth_del); + if (!filter.flushed) + printf("Nothing to flush\n"); + else + printf("Flushed %d nexthops\n", filter.flushed); + + return rc; +} + +static void print_nh_group(FILE *fp, const struct rtattr *grps_attr) +{ + struct nexthop_grp *nhg = RTA_DATA(grps_attr); + int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg); + int i; + + if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) { + fprintf(fp, ""); + return; + } + + open_json_array(PRINT_JSON, "group"); + print_string(PRINT_FP, NULL, "%s", "group "); + for (i = 0; i < num; ++i) { + open_json_object(NULL); + + if (i) + print_string(PRINT_FP, NULL, "%s", "/"); + + print_uint(PRINT_ANY, "id", "%u", nhg[i].id); + if (nhg[i].weight) + print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight + 1); + + close_json_object(); + } + print_string(PRINT_FP, NULL, "%s", " "); + close_json_array(PRINT_JSON, NULL); +} + +int print_nexthop(struct nlmsghdr *n, void *arg) +{ + struct nhmsg *nhm = NLMSG_DATA(n); + struct rtattr *tb[NHA_MAX+1]; + FILE *fp = (FILE *)arg; + int len; + + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_DELNEXTHOP && + n->nlmsg_type != RTM_NEWNEXTHOP) { + fprintf(stderr, "Not a nexthop: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return -1; + } + + len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); + if (len < 0) { + close_json_object(); + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.proto && filter.proto != nhm->nh_protocol) + return 0; + + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + + open_json_object(NULL); + + if (n->nlmsg_type == RTM_DELNEXTHOP) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[NHA_ID]) + print_uint(PRINT_ANY, "id", "id %u ", + rta_getattr_u32(tb[NHA_ID])); + + if (tb[NHA_GROUP]) + print_nh_group(fp, tb[NHA_GROUP]); + + if (tb[NHA_ENCAP]) + lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]); + + if (tb[NHA_GATEWAY]) + print_rta_gateway(fp, nhm->nh_family, tb[NHA_GATEWAY]); + + if (tb[NHA_OIF]) + print_rta_if(fp, tb[NHA_OIF], "dev"); + + if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) { + print_string(PRINT_ANY, "scope", "scope %s ", + rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1))); + } + + if (tb[NHA_BLACKHOLE]) + print_null(PRINT_ANY, "blackhole", "blackhole ", NULL); + + if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) { + print_string(PRINT_ANY, "protocol", "proto %s ", + rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1))); + } + + if (tb[NHA_OIF]) + print_rt_flags(fp, nhm->nh_flags); + + if (tb[NHA_FDB]) + print_null(PRINT_ANY, "fdb", "fdb", NULL); + + print_string(PRINT_FP, NULL, "%s", "\n"); + close_json_object(); + fflush(fp); + + return 0; +} + +static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv) +{ + struct nexthop_grp *grps; + int count = 0, i; + char *sep, *wsep; + + if (*argv != '\0') + count = 1; + + /* separator is '/' */ + sep = strchr(argv, '/'); + while (sep) { + count++; + sep = strchr(sep + 1, '/'); + } + + if (count == 0) + return -1; + + grps = calloc(count, sizeof(*grps)); + if (!grps) + return -1; + + for (i = 0; i < count; ++i) { + sep = strchr(argv, '/'); + if (sep) + *sep = '\0'; + + wsep = strchr(argv, ','); + if (wsep) + *wsep = '\0'; + + if (get_unsigned(&grps[i].id, argv, 0)) + return -1; + if (wsep) { + unsigned int w; + + wsep++; + if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", wsep); + grps[i].weight = w - 1; + } + + if (!sep) + break; + + argv = sep + 1; + } + + return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps)); +} + +static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .nhm.nh_family = preferred_family, + }; + __u32 nh_flags = 0; + + while (argc > 0) { + if (!strcmp(*argv, "id")) { + __u32 id; + + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + addattr32(&req.n, sizeof(req), NHA_ID, id); + } else if (!strcmp(*argv, "dev")) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr32(&req.n, sizeof(req), NHA_OIF, ifindex); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = AF_INET; + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + int family; + + NEXT_ARG(); + family = read_family(*argv); + if (family == AF_UNSPEC) + family = req.nhm.nh_family; + else + NEXT_ARG(); + get_addr(&addr, *argv, family); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = addr.family; + else if (req.nhm.nh_family != addr.family) + invarg("address family mismatch\n", *argv); + addattr_l(&req.n, sizeof(req), NHA_GATEWAY, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "encap") == 0) { + char buf[1024]; + struct rtattr *rta = (void *)buf; + + rta->rta_type = NHA_ENCAP; + rta->rta_len = RTA_LENGTH(0); + + lwt_parse_encap(rta, sizeof(buf), &argc, &argv, + NHA_ENCAP, NHA_ENCAP_TYPE); + + if (rta->rta_len > RTA_LENGTH(0)) { + addraw_l(&req.n, 1024, RTA_DATA(rta), + RTA_PAYLOAD(rta)); + } + } else if (!strcmp(*argv, "blackhole")) { + addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = AF_INET; + } else if (!strcmp(*argv, "fdb")) { + addattr_l(&req.n, sizeof(req), NHA_FDB, NULL, 0); + } else if (!strcmp(*argv, "onlink")) { + nh_flags |= RTNH_F_ONLINK; + } else if (!strcmp(*argv, "group")) { + NEXT_ARG(); + + if (add_nh_group_attr(&req.n, sizeof(req), *argv)) + invarg("\"group\" value is invalid\n", *argv); + } else if (matches(*argv, "protocol") == 0) { + __u32 prot; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.nhm.nh_protocol = prot; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("", *argv); + } + argc--; argv++; + } + + req.nhm.nh_flags = nh_flags; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int ipnh_get_id(__u32 id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNEXTHOP, + .nhm.nh_family = preferred_family, + }; + struct nlmsghdr *answer; + + addattr32(&req.n, sizeof(req), NHA_ID, id); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + new_json_obj(json); + + if (print_nexthop(answer, (void *)stdout) < 0) { + free(answer); + return -1; + } + + delete_json_obj(); + fflush(stdout); + + free(answer); + + return 0; +} + +static int ipnh_list_flush(int argc, char **argv, int action) +{ + unsigned int all = (argc == 0); + + while (argc > 0) { + if (!matches(*argv, "dev")) { + NEXT_ARG(); + filter.ifindex = ll_name_to_index(*argv); + if (!filter.ifindex) + invarg("Device does not exist\n", *argv); + } else if (!matches(*argv, "groups")) { + filter.groups = 1; + } else if (!matches(*argv, "master")) { + NEXT_ARG(); + filter.master = ll_name_to_index(*argv); + if (!filter.master) + invarg("Device does not exist\n", *argv); + } else if (matches(*argv, "vrf") == 0) { + NEXT_ARG(); + if (!name_is_vrf(*argv)) + invarg("Invalid VRF\n", *argv); + filter.master = ll_name_to_index(*argv); + if (!filter.master) + invarg("VRF does not exist\n", *argv); + } else if (!strcmp(*argv, "id")) { + __u32 id; + + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + return ipnh_get_id(id); + } else if (!matches(*argv, "protocol")) { + __u32 proto; + + NEXT_ARG(); + if (get_unsigned(&proto, *argv, 0)) + invarg("invalid protocol value", *argv); + filter.proto = proto; + } else if (!matches(*argv, "fdb")) { + filter.fdb = 1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + invarg("", *argv); + } + argc--; argv++; + } + + if (action == IPNH_FLUSH) + return ipnh_flush(all); + + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { + perror("Cannot send dump request"); + return -2; + } + + new_json_obj(json); + + if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -2; + } + + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int ipnh_get(int argc, char **argv) +{ + __u32 id = 0; + + while (argc > 0) { + if (!strcmp(*argv, "id")) { + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + } else { + usage(); + } + argc--; argv++; + } + + if (!id) { + usage(); + return -1; + } + + return ipnh_get_id(id); +} + +int do_ipnh(int argc, char **argv) +{ + if (argc < 1) + return ipnh_list_flush(0, NULL, IPNH_LIST); + + if (!matches(*argv, "add")) + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (!matches(*argv, "replace")) + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (!matches(*argv, "delete")) + return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1); + + if (!matches(*argv, "list") || + !matches(*argv, "show") || + !matches(*argv, "lst")) + return ipnh_list_flush(argc-1, argv+1, IPNH_LIST); + + if (!matches(*argv, "get")) + return ipnh_get(argc-1, argv+1); + + if (!matches(*argv, "flush")) + return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH); + + if (!matches(*argv, "help")) + usage(); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipntable.c b/ip/ipntable.c new file mode 100644 index 0000000..b5b06a3 --- /dev/null +++ b/ip/ipntable.c @@ -0,0 +1,701 @@ +/* + * Copyright (C)2006 USAGI/WIDE Project + * + * 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, see . + */ +/* + * based on ipneigh.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +static struct +{ + int family; + int index; +#define NONE_DEV (-1) + const char *name; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip ntable change name NAME [ dev DEV ]\n" + " [ thresh1 VAL ] [ thresh2 VAL ] [ thresh3 VAL ] [ gc_int MSEC ]\n" + " [ PARMS ]\n" + "Usage: ip ntable show [ dev DEV ] [ name NAME ]\n" + + "PARMS := [ base_reachable MSEC ] [ retrans MSEC ] [ gc_stale MSEC ]\n" + " [ delay_probe MSEC ] [ queue LEN ]\n" + " [ app_probes VAL ] [ ucast_probes VAL ] [ mcast_probes VAL ]\n" + " [ anycast_delay MSEC ] [ proxy_delay MSEC ] [ proxy_queue LEN ]\n" + " [ locktime MSEC ]\n" + ); + + exit(-1); +} + +static int ipntable_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct ndtmsg ndtm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .ndtm.ndtm_family = preferred_family, + }; + char *namep = NULL; + char *threshsp = NULL; + char *gc_intp = NULL; + char parms_buf[1024] = {}; + struct rtattr *parms_rta = (struct rtattr *)parms_buf; + int parms_change = 0; + + parms_rta->rta_type = NDTA_PARMS; + parms_rta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "name") == 0) { + int len; + + NEXT_ARG(); + if (namep) + duparg("NAME", *argv); + + namep = *argv; + len = strlen(namep) + 1; + addattr_l(&req.n, sizeof(req), NDTA_NAME, namep, len); + } else if (strcmp(*argv, "thresh1") == 0) { + __u32 thresh1; + + NEXT_ARG(); + threshsp = *argv; + + if (get_u32(&thresh1, *argv, 0)) + invarg("\"thresh1\" value is invalid", *argv); + + addattr32(&req.n, sizeof(req), NDTA_THRESH1, thresh1); + } else if (strcmp(*argv, "thresh2") == 0) { + __u32 thresh2; + + NEXT_ARG(); + threshsp = *argv; + + if (get_u32(&thresh2, *argv, 0)) + invarg("\"thresh2\" value is invalid", *argv); + + addattr32(&req.n, sizeof(req), NDTA_THRESH2, thresh2); + } else if (strcmp(*argv, "thresh3") == 0) { + __u32 thresh3; + + NEXT_ARG(); + threshsp = *argv; + + if (get_u32(&thresh3, *argv, 0)) + invarg("\"thresh3\" value is invalid", *argv); + + addattr32(&req.n, sizeof(req), NDTA_THRESH3, thresh3); + } else if (strcmp(*argv, "gc_int") == 0) { + __u64 gc_int; + + NEXT_ARG(); + gc_intp = *argv; + + if (get_u64(&gc_int, *argv, 0)) + invarg("\"gc_int\" value is invalid", *argv); + + addattr_l(&req.n, sizeof(req), NDTA_GC_INTERVAL, + &gc_int, sizeof(gc_int)); + } else if (strcmp(*argv, "dev") == 0) { + __u32 ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + return nodev(*argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_IFINDEX, ifindex); + } else if (strcmp(*argv, "base_reachable") == 0) { + __u64 breachable; + + NEXT_ARG(); + + if (get_u64(&breachable, *argv, 0)) + invarg("\"base_reachable\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_BASE_REACHABLE_TIME, + &breachable, sizeof(breachable)); + parms_change = 1; + } else if (strcmp(*argv, "retrans") == 0) { + __u64 retrans; + + NEXT_ARG(); + + if (get_u64(&retrans, *argv, 0)) + invarg("\"retrans\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_RETRANS_TIME, + &retrans, sizeof(retrans)); + parms_change = 1; + } else if (strcmp(*argv, "gc_stale") == 0) { + __u64 gc_stale; + + NEXT_ARG(); + + if (get_u64(&gc_stale, *argv, 0)) + invarg("\"gc_stale\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_GC_STALETIME, + &gc_stale, sizeof(gc_stale)); + parms_change = 1; + } else if (strcmp(*argv, "delay_probe") == 0) { + __u64 delay_probe; + + NEXT_ARG(); + + if (get_u64(&delay_probe, *argv, 0)) + invarg("\"delay_probe\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_DELAY_PROBE_TIME, + &delay_probe, sizeof(delay_probe)); + parms_change = 1; + } else if (strcmp(*argv, "queue") == 0) { + __u32 queue; + + NEXT_ARG(); + + if (get_u32(&queue, *argv, 0)) + invarg("\"queue\" value is invalid", *argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_QUEUE_LEN, queue); + parms_change = 1; + } else if (strcmp(*argv, "app_probes") == 0) { + __u32 aprobe; + + NEXT_ARG(); + + if (get_u32(&aprobe, *argv, 0)) + invarg("\"app_probes\" value is invalid", *argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_APP_PROBES, aprobe); + parms_change = 1; + } else if (strcmp(*argv, "ucast_probes") == 0) { + __u32 uprobe; + + NEXT_ARG(); + + if (get_u32(&uprobe, *argv, 0)) + invarg("\"ucast_probes\" value is invalid", *argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_UCAST_PROBES, uprobe); + parms_change = 1; + } else if (strcmp(*argv, "mcast_probes") == 0) { + __u32 mprobe; + + NEXT_ARG(); + + if (get_u32(&mprobe, *argv, 0)) + invarg("\"mcast_probes\" value is invalid", *argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_MCAST_PROBES, mprobe); + parms_change = 1; + } else if (strcmp(*argv, "anycast_delay") == 0) { + __u64 anycast_delay; + + NEXT_ARG(); + + if (get_u64(&anycast_delay, *argv, 0)) + invarg("\"anycast_delay\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_ANYCAST_DELAY, + &anycast_delay, sizeof(anycast_delay)); + parms_change = 1; + } else if (strcmp(*argv, "proxy_delay") == 0) { + __u64 proxy_delay; + + NEXT_ARG(); + + if (get_u64(&proxy_delay, *argv, 0)) + invarg("\"proxy_delay\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_PROXY_DELAY, + &proxy_delay, sizeof(proxy_delay)); + parms_change = 1; + } else if (strcmp(*argv, "proxy_queue") == 0) { + __u32 pqueue; + + NEXT_ARG(); + + if (get_u32(&pqueue, *argv, 0)) + invarg("\"proxy_queue\" value is invalid", *argv); + + rta_addattr32(parms_rta, sizeof(parms_buf), + NDTPA_PROXY_QLEN, pqueue); + parms_change = 1; + } else if (strcmp(*argv, "locktime") == 0) { + __u64 locktime; + + NEXT_ARG(); + + if (get_u64(&locktime, *argv, 0)) + invarg("\"locktime\" value is invalid", *argv); + + rta_addattr_l(parms_rta, sizeof(parms_buf), + NDTPA_LOCKTIME, + &locktime, sizeof(locktime)); + parms_change = 1; + } else { + invarg("unknown", *argv); + } + + argc--; argv++; + } + + if (!namep) + missarg("NAME"); + if (!threshsp && !gc_intp && !parms_change) { + fprintf(stderr, "Not enough information: changeable attributes required.\n"); + exit(-1); + } + + if (parms_rta->rta_len > RTA_LENGTH(0)) { + addattr_l(&req.n, sizeof(req), NDTA_PARMS, RTA_DATA(parms_rta), + RTA_PAYLOAD(parms_rta)); + } + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + return 0; +} + +static const char *ntable_strtime_delta(__u32 msec) +{ + static char str[32]; + struct timeval now = {}; + time_t t; + struct tm *tp; + + if (msec == 0) + goto error; + + if (gettimeofday(&now, NULL) < 0) { + perror("gettimeofday"); + goto error; + } + + t = now.tv_sec - (msec / 1000); + tp = localtime(&t); + if (!tp) + goto error; + + strftime(str, sizeof(str), "%Y-%m-%d %T", tp); + + return str; + error: + strcpy(str, "(error)"); + return str; +} + +static void print_ndtconfig(const struct ndt_config *ndtc) +{ + + print_uint(PRINT_ANY, "key_length", + " config key_len %u ", ndtc->ndtc_key_len); + print_uint(PRINT_ANY, "entry_size", + "entry_size %u ", ndtc->ndtc_entry_size); + print_uint(PRINT_ANY, "entries", "entries %u ", ndtc->ndtc_entries); + + print_nl(); + + print_string(PRINT_ANY, "last_flush", + " last_flush %s ", + ntable_strtime_delta(ndtc->ndtc_last_flush)); + print_string(PRINT_ANY, "last_rand", + "last_rand %s ", + ntable_strtime_delta(ndtc->ndtc_last_rand)); + + print_nl(); + + print_uint(PRINT_ANY, "hash_rnd", + " hash_rnd %u ", ndtc->ndtc_hash_rnd); + print_0xhex(PRINT_ANY, "hash_mask", + "hash_mask %08llx ", ndtc->ndtc_hash_mask); + + print_uint(PRINT_ANY, "hash_chain_gc", + "hash_chain_gc %u ", ndtc->ndtc_hash_chain_gc); + print_uint(PRINT_ANY, "proxy_qlen", + "proxy_qlen %u ", ndtc->ndtc_proxy_qlen); + + print_nl(); +} + +static void print_ndtparams(struct rtattr *tpb[]) +{ + + if (tpb[NDTPA_IFINDEX]) { + __u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]); + + print_string(PRINT_FP, NULL, " dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "dev", "%s ", ll_index_to_name(ifindex)); + print_nl(); + } + + print_string(PRINT_FP, NULL, " ", NULL); + if (tpb[NDTPA_REFCNT]) { + __u32 refcnt = rta_getattr_u32(tpb[NDTPA_REFCNT]); + + print_uint(PRINT_ANY, "refcnt", "refcnt %u ", refcnt); + } + + if (tpb[NDTPA_REACHABLE_TIME]) { + __u64 reachable = rta_getattr_u64(tpb[NDTPA_REACHABLE_TIME]); + + print_u64(PRINT_ANY, "reachable", + "reachable %llu ", reachable); + } + + if (tpb[NDTPA_BASE_REACHABLE_TIME]) { + __u64 breachable + = rta_getattr_u64(tpb[NDTPA_BASE_REACHABLE_TIME]); + + print_u64(PRINT_ANY, "base_reachable", + "base_reachable %llu ", breachable); + } + + if (tpb[NDTPA_RETRANS_TIME]) { + __u64 retrans = rta_getattr_u64(tpb[NDTPA_RETRANS_TIME]); + + print_u64(PRINT_ANY, "retrans", "retrans %llu ", retrans); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_GC_STALETIME]) { + __u64 gc_stale = rta_getattr_u64(tpb[NDTPA_GC_STALETIME]); + + print_u64(PRINT_ANY, "gc_stale", "gc_stale %llu ", gc_stale); + } + + if (tpb[NDTPA_DELAY_PROBE_TIME]) { + __u64 delay_probe + = rta_getattr_u64(tpb[NDTPA_DELAY_PROBE_TIME]); + + print_u64(PRINT_ANY, "delay_probe", + "delay_probe %llu ", delay_probe); + } + + if (tpb[NDTPA_QUEUE_LEN]) { + __u32 queue = rta_getattr_u32(tpb[NDTPA_QUEUE_LEN]); + + print_uint(PRINT_ANY, "queue", "queue %u ", queue); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_APP_PROBES]) { + __u32 aprobe = rta_getattr_u32(tpb[NDTPA_APP_PROBES]); + + print_uint(PRINT_ANY, "app_probes", "app_probes %u ", aprobe); + } + + if (tpb[NDTPA_UCAST_PROBES]) { + __u32 uprobe = rta_getattr_u32(tpb[NDTPA_UCAST_PROBES]); + + print_uint(PRINT_ANY, "ucast_probes", + "ucast_probes %u ", uprobe); + } + + if (tpb[NDTPA_MCAST_PROBES]) { + __u32 mprobe = rta_getattr_u32(tpb[NDTPA_MCAST_PROBES]); + + print_uint(PRINT_ANY, "mcast_probes", + "mcast_probes %u ", mprobe); + } + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + if (tpb[NDTPA_ANYCAST_DELAY]) { + __u64 anycast_delay = rta_getattr_u64(tpb[NDTPA_ANYCAST_DELAY]); + + print_u64(PRINT_ANY, "anycast_delay", + "anycast_delay %llu ", anycast_delay); + } + + if (tpb[NDTPA_PROXY_DELAY]) { + __u64 proxy_delay = rta_getattr_u64(tpb[NDTPA_PROXY_DELAY]); + + print_u64(PRINT_ANY, "proxy_delay", + "proxy_delay %llu ", proxy_delay); + } + + if (tpb[NDTPA_PROXY_QLEN]) { + __u32 pqueue = rta_getattr_u32(tpb[NDTPA_PROXY_QLEN]); + + print_uint(PRINT_ANY, "proxy_queue", "proxy_queue %u ", pqueue); + } + + if (tpb[NDTPA_LOCKTIME]) { + __u64 locktime = rta_getattr_u64(tpb[NDTPA_LOCKTIME]); + + print_u64(PRINT_ANY, "locktime", "locktime %llu ", locktime); + } + + print_nl(); +} + +static void print_ndtstats(const struct ndt_stats *ndts) +{ + + print_string(PRINT_FP, NULL, " stats ", NULL); + + print_u64(PRINT_ANY, "allocs", "allocs %llu ", ndts->ndts_allocs); + print_u64(PRINT_ANY, "destroys", "destroys %llu ", + ndts->ndts_destroys); + print_u64(PRINT_ANY, "hash_grows", "hash_grows %llu ", + ndts->ndts_hash_grows); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_u64(PRINT_ANY, "res_failed", "res_failed %llu ", + ndts->ndts_res_failed); + print_u64(PRINT_ANY, "lookups", "lookups %llu ", ndts->ndts_lookups); + print_u64(PRINT_ANY, "hits", "hits %llu ", ndts->ndts_hits); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_u64(PRINT_ANY, "rcv_probes_mcast", "rcv_probes_mcast %llu ", + ndts->ndts_rcv_probes_mcast); + print_u64(PRINT_ANY, "rcv_probes_ucast", "rcv_probes_ucast %llu ", + ndts->ndts_rcv_probes_ucast); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_u64(PRINT_ANY, "periodic_gc_runs", "periodic_gc_runs %llu ", + ndts->ndts_periodic_gc_runs); + print_u64(PRINT_ANY, "forced_gc_runs", "forced_gc_runs %llu ", + ndts->ndts_forced_gc_runs); + + print_string(PRINT_FP, NULL, "%s ", _SL_); + + print_u64(PRINT_ANY, "table_fulls", "table_fulls %llu ", + ndts->ndts_table_fulls); + + print_nl(); +} + +static int print_ntable(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct ndtmsg *ndtm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[NDTA_MAX+1]; + struct rtattr *tpb[NDTPA_MAX+1]; + int ret; + + if (n->nlmsg_type != RTM_NEWNEIGHTBL) { + fprintf(stderr, "Not NEIGHTBL: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*ndtm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (preferred_family && preferred_family != ndtm->ndtm_family) + return 0; + + parse_rtattr(tb, NDTA_MAX, NDTA_RTA(ndtm), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*ndtm))); + + if (tb[NDTA_NAME]) { + const char *name = rta_getattr_str(tb[NDTA_NAME]); + + if (filter.name && strcmp(filter.name, name)) + return 0; + } + + if (tb[NDTA_PARMS]) { + parse_rtattr(tpb, NDTPA_MAX, RTA_DATA(tb[NDTA_PARMS]), + RTA_PAYLOAD(tb[NDTA_PARMS])); + + if (tpb[NDTPA_IFINDEX]) { + __u32 ifindex = rta_getattr_u32(tpb[NDTPA_IFINDEX]); + + if (filter.index && filter.index != ifindex) + return 0; + } else { + if (filter.index && filter.index != NONE_DEV) + return 0; + } + } + + open_json_object(NULL); + print_string(PRINT_ANY, "family", + "%s ", family_name(ndtm->ndtm_family)); + + if (tb[NDTA_NAME]) { + const char *name = rta_getattr_str(tb[NDTA_NAME]); + + print_string(PRINT_ANY, "name", "%s ", name); + } + + print_nl(); + + ret = (tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || tb[NDTA_THRESH3] || + tb[NDTA_GC_INTERVAL]); + if (ret) + print_string(PRINT_FP, NULL, " ", NULL); + + if (tb[NDTA_THRESH1]) { + __u32 thresh1 = rta_getattr_u32(tb[NDTA_THRESH1]); + + print_uint(PRINT_ANY, "thresh1", "thresh1 %u ", thresh1); + } + + if (tb[NDTA_THRESH2]) { + __u32 thresh2 = rta_getattr_u32(tb[NDTA_THRESH2]); + + print_uint(PRINT_ANY, "thresh2", "thresh2 %u ", thresh2); + } + + if (tb[NDTA_THRESH3]) { + __u32 thresh3 = rta_getattr_u32(tb[NDTA_THRESH3]); + + print_uint(PRINT_ANY, "thresh3", "thresh3 %u ", thresh3); + } + + if (tb[NDTA_GC_INTERVAL]) { + __u64 gc_int = rta_getattr_u64(tb[NDTA_GC_INTERVAL]); + + print_u64(PRINT_ANY, "gc_interval", "gc_int %llu ", gc_int); + } + + if (ret) + print_nl(); + + if (tb[NDTA_CONFIG] && show_stats) + print_ndtconfig(RTA_DATA(tb[NDTA_CONFIG])); + + if (tb[NDTA_PARMS]) + print_ndtparams(tpb); + + if (tb[NDTA_STATS] && show_stats) + print_ndtstats(RTA_DATA(tb[NDTA_STATS])); + + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + fflush(fp); + + return 0; +} + +static void ipntable_reset_filter(void) +{ + memset(&filter, 0, sizeof(filter)); +} + +static int ipntable_show(int argc, char **argv) +{ + ipntable_reset_filter(); + + filter.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + + if (strcmp("none", *argv) == 0) + filter.index = NONE_DEV; + else if ((filter.index = ll_name_to_index(*argv)) == 0) + invarg("\"DEV\" is invalid", *argv); + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + + filter.name = *argv; + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (rtnl_neightbldump_req(&rth, preferred_family) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_ntable, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + + return 0; +} + +int do_ipntable(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc > 0) { + if (matches(*argv, "change") == 0 || + matches(*argv, "chg") == 0) + return ipntable_modify(RTM_SETNEIGHTBL, + NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipntable_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return ipntable_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip ntable help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipprefix.c b/ip/ipprefix.c new file mode 100644 index 0000000..466af20 --- /dev/null +++ b/ip/ipprefix.c @@ -0,0 +1,98 @@ +/* + * Copyright (C)2005 USAGI/WIDE Project + * + * 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, see . + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" + +/* prefix flags; see kernel's net/ipv6/addrconf.c and include/net/if_inet6.h */ +#define IF_PREFIX_ONLINK 0x01 +#define IF_PREFIX_AUTOCONF 0x02 + +int print_prefix(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct prefixmsg *prefix = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[RTA_MAX+1]; + int family = preferred_family; + + if (n->nlmsg_type != RTM_NEWPREFIX) { + fprintf(stderr, "Not a prefix: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*prefix)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (family == AF_UNSPEC) + family = AF_INET6; + if (family != AF_INET6) + return 0; + + if (prefix->prefix_family != AF_INET6) { + fprintf(stderr, "incorrect protocol family: %d\n", prefix->prefix_family); + return 0; + } + if (prefix->prefix_type != ND_OPT_PREFIX_INFORMATION) { + fprintf(stderr, "wrong ND type %d\n", prefix->prefix_type); + return 0; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len); + + if (tb[PREFIX_ADDRESS]) { + fprintf(fp, "prefix %s/%u", + rt_addr_n2a_rta(family, tb[PREFIX_ADDRESS]), + prefix->prefix_len); + } + fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex)); + + if (prefix->prefix_flags & IF_PREFIX_ONLINK) + fprintf(fp, "onlink "); + if (prefix->prefix_flags & IF_PREFIX_AUTOCONF) + fprintf(fp, "autoconf "); + + if (tb[PREFIX_CACHEINFO]) { + const struct prefix_cacheinfo *pc + = RTA_DATA(tb[PREFIX_CACHEINFO]); + + fprintf(fp, "valid %u ", pc->valid_time); + fprintf(fp, "preferred %u ", pc->preferred_time); + } + + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} diff --git a/ip/iproute.c b/ip/iproute.c new file mode 100644 index 0000000..05ec2c2 --- /dev/null +++ b/ip/iproute.c @@ -0,0 +1,2342 @@ +/* + * iproute.c "ip route". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + +enum list_action { + IPROUTE_LIST, + IPROUTE_FLUSH, + IPROUTE_SAVE, +}; +static const char *mx_names[RTAX_MAX+1] = { + [RTAX_MTU] = "mtu", + [RTAX_WINDOW] = "window", + [RTAX_RTT] = "rtt", + [RTAX_RTTVAR] = "rttvar", + [RTAX_SSTHRESH] = "ssthresh", + [RTAX_CWND] = "cwnd", + [RTAX_ADVMSS] = "advmss", + [RTAX_REORDERING] = "reordering", + [RTAX_HOPLIMIT] = "hoplimit", + [RTAX_INITCWND] = "initcwnd", + [RTAX_FEATURES] = "features", + [RTAX_RTO_MIN] = "rto_min", + [RTAX_INITRWND] = "initrwnd", + [RTAX_QUICKACK] = "quickack", + [RTAX_CC_ALGO] = "congctl", + [RTAX_FASTOPEN_NO_COOKIE] = "fastopen_no_cookie" +}; +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip route { list | flush } SELECTOR\n" + " ip route save SELECTOR\n" + " ip route restore\n" + " ip route showdump\n" + " ip route get [ ROUTE_GET_FLAGS ] ADDRESS\n" + " [ from ADDRESS iif STRING ]\n" + " [ oif STRING ] [ tos TOS ]\n" + " [ mark NUMBER ] [ vrf NAME ]\n" + " [ uid NUMBER ] [ ipproto PROTOCOL ]\n" + " [ sport NUMBER ] [ dport NUMBER ]\n" + " ip route { add | del | change | append | replace } ROUTE\n" + "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n" + " [ table TABLE_ID ] [ vrf NAME ] [ proto RTPROTO ]\n" + " [ type TYPE ] [ scope SCOPE ]\n" + "ROUTE := NODE_SPEC [ INFO_SPEC ]\n" + "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n" + " [ table TABLE_ID ] [ proto RTPROTO ]\n" + " [ scope SCOPE ] [ metric METRIC ]\n" + " [ ttl-propagate { enabled | disabled } ]\n" + "INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]...\n" + "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n" + " [ dev STRING ] [ weight NUMBER ] NHFLAGS\n" + "FAMILY := [ inet | inet6 | mpls | bridge | link ]\n" + "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n" + " [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n" + " [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n" + " [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n" + " [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n" + " [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n" + " [ pref PREF ] [ expires TIME ] [ fastopen_no_cookie BOOL ]\n" + "TYPE := { unicast | local | broadcast | multicast | throw |\n" + " unreachable | prohibit | blackhole | nat }\n" + "TABLE_ID := [ local | main | default | all | NUMBER ]\n" + "SCOPE := [ host | link | global | NUMBER ]\n" + "NHFLAGS := [ onlink | pervasive ]\n" + "RTPROTO := [ kernel | boot | static | NUMBER ]\n" + "PREF := [ low | medium | high ]\n" + "TIME := NUMBER[s|ms]\n" + "BOOL := [1|0]\n" + "FEATURES := ecn\n" + "ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl ]\n" + "ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n" + "SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n" + "SEGMODE := [ encap | inline ]\n" + "ROUTE_GET_FLAGS := [ fibmatch ]\n"); + exit(-1); +} + + +static struct +{ + unsigned int tb; + int cloned; + int flushed; + char *flushb; + int flushp; + int flushe; + int protocol, protocolmask; + int scope, scopemask; + __u64 typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int mark, markmask; + int realm, realmmask; + __u32 metric, metricmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request"); + return -2; + } + filter.flushp = 0; + return 0; +} + +static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) +{ + struct rtmsg *r = NLMSG_DATA(n); + inet_prefix dst = { .family = r->rtm_family }; + inet_prefix src = { .family = r->rtm_family }; + inet_prefix via = { .family = r->rtm_family }; + inet_prefix prefsrc = { .family = r->rtm_family }; + __u32 table; + static int ip6_multiple_tables; + + table = rtm_get_table(r, tb); + + if (preferred_family != AF_UNSPEC && r->rtm_family != preferred_family) + return 0; + + if (r->rtm_family == AF_INET6 && table != RT_TABLE_MAIN) + ip6_multiple_tables = 1; + + if (filter.cloned == !(r->rtm_flags & RTM_F_CLONED)) + return 0; + + if (r->rtm_family == AF_INET6 && !ip6_multiple_tables) { + if (filter.tb) { + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) + return 0; + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) + return 0; + } else { + return 0; + } + } + } else { + if (filter.tb > 0 && filter.tb != table) + return 0; + } + if ((filter.protocol^r->rtm_protocol)&filter.protocolmask) + return 0; + if ((filter.scope^r->rtm_scope)&filter.scopemask) + return 0; + + if (filter.typemask && !(filter.typemask & (1 << r->rtm_type))) + return 0; + if ((filter.tos^r->rtm_tos)&filter.tosmask) + return 0; + if (filter.rdst.family) { + if (r->rtm_family != filter.rdst.family || + filter.rdst.bitlen > r->rtm_dst_len) + return 0; + } else if (filter.rdst.flags & PREFIXLEN_SPECIFIED) { + if (filter.rdst.bitlen > r->rtm_dst_len) + return 0; + } + if (filter.mdst.family) { + if (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && + filter.mdst.bitlen < r->rtm_dst_len)) + return 0; + } else if (filter.mdst.flags & PREFIXLEN_SPECIFIED) { + if (filter.mdst.bitlen >= 0 && + filter.mdst.bitlen < r->rtm_dst_len) + return 0; + } + if (filter.rsrc.family) { + if (r->rtm_family != filter.rsrc.family || + filter.rsrc.bitlen > r->rtm_src_len) + return 0; + } else if (filter.rsrc.flags & PREFIXLEN_SPECIFIED) { + if (filter.rsrc.bitlen > r->rtm_src_len) + return 0; + } + if (filter.msrc.family) { + if (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && + filter.msrc.bitlen < r->rtm_src_len)) + return 0; + } else if (filter.msrc.flags & PREFIXLEN_SPECIFIED) { + if (filter.msrc.bitlen >= 0 && + filter.msrc.bitlen < r->rtm_src_len) + return 0; + } + if (filter.rvia.family) { + int family = r->rtm_family; + + if (tb[RTA_VIA]) { + struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + + family = via->rtvia_family; + } + if (family != filter.rvia.family) + return 0; + } + if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) + return 0; + + if (tb[RTA_DST]) + memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); + if (filter.rsrc.family || filter.msrc.family || + filter.rsrc.flags & PREFIXLEN_SPECIFIED || + filter.msrc.flags & PREFIXLEN_SPECIFIED) { + if (tb[RTA_SRC]) + memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); + } + if (filter.rvia.bitlen > 0) { + if (tb[RTA_GATEWAY]) + memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8); + if (tb[RTA_VIA]) { + size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; + struct rtvia *rtvia = RTA_DATA(tb[RTA_VIA]); + + via.family = rtvia->rtvia_family; + memcpy(&via.data, rtvia->rtvia_addr, len); + } + } + if (filter.rprefsrc.bitlen > 0) { + if (tb[RTA_PREFSRC]) + memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8); + } + + if ((filter.rdst.family || filter.rdst.flags & PREFIXLEN_SPECIFIED) && + inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if ((filter.mdst.family || filter.mdst.flags & PREFIXLEN_SPECIFIED) && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if ((filter.rsrc.family || filter.rsrc.flags & PREFIXLEN_SPECIFIED) && + inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if ((filter.msrc.family || filter.msrc.flags & PREFIXLEN_SPECIFIED) && + filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen)) + return 0; + if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen)) + return 0; + if (filter.realmmask) { + __u32 realms = 0; + + if (tb[RTA_FLOW]) + realms = rta_getattr_u32(tb[RTA_FLOW]); + if ((realms^filter.realm)&filter.realmmask) + return 0; + } + if (filter.iifmask) { + int iif = 0; + + if (tb[RTA_IIF]) + iif = rta_getattr_u32(tb[RTA_IIF]); + if ((iif^filter.iif)&filter.iifmask) + return 0; + } + if (filter.oifmask) { + int oif = 0; + + if (tb[RTA_OIF]) + oif = rta_getattr_u32(tb[RTA_OIF]); + if ((oif^filter.oif)&filter.oifmask) + return 0; + } + if (filter.markmask) { + int mark = 0; + + if (tb[RTA_MARK]) + mark = rta_getattr_u32(tb[RTA_MARK]); + if ((mark ^ filter.mark) & filter.markmask) + return 0; + } + if (filter.metricmask) { + __u32 metric = 0; + + if (tb[RTA_PRIORITY]) + metric = rta_getattr_u32(tb[RTA_PRIORITY]); + if ((metric ^ filter.metric) & filter.metricmask) + return 0; + } + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + rta_getattr_u32(tb[RTA_PRIORITY]) == -1) + return 0; + + return 1; +} + +static void print_rtax_features(FILE *fp, unsigned int features) +{ + unsigned int of = features; + + if (features & RTAX_FEATURE_ECN) { + print_null(PRINT_ANY, "ecn", "ecn ", NULL); + features &= ~RTAX_FEATURE_ECN; + } + + if (features) + print_0xhex(PRINT_ANY, + "features", "%#llx ", of); +} + +void print_rt_flags(FILE *fp, unsigned int flags) +{ + open_json_array(PRINT_JSON, + is_json_context() ? "flags" : ""); + + if (flags & RTNH_F_DEAD) + print_string(PRINT_ANY, NULL, "%s ", "dead"); + if (flags & RTNH_F_ONLINK) + print_string(PRINT_ANY, NULL, "%s ", "onlink"); + if (flags & RTNH_F_PERVASIVE) + print_string(PRINT_ANY, NULL, "%s ", "pervasive"); + if (flags & RTNH_F_OFFLOAD) + print_string(PRINT_ANY, NULL, "%s ", "offload"); + if (flags & RTM_F_NOTIFY) + print_string(PRINT_ANY, NULL, "%s ", "notify"); + if (flags & RTNH_F_LINKDOWN) + print_string(PRINT_ANY, NULL, "%s ", "linkdown"); + if (flags & RTNH_F_UNRESOLVED) + print_string(PRINT_ANY, NULL, "%s ", "unresolved"); + if (flags & RTM_F_OFFLOAD) + print_string(PRINT_ANY, NULL, "%s ", "rt_offload"); + if (flags & RTM_F_TRAP) + print_string(PRINT_ANY, NULL, "%s ", "rt_trap"); + + close_json_array(PRINT_JSON, NULL); +} + +static void print_rt_pref(FILE *fp, unsigned int pref) +{ + + switch (pref) { + case ICMPV6_ROUTER_PREF_LOW: + print_string(PRINT_ANY, + "pref", "pref %s", "low"); + break; + case ICMPV6_ROUTER_PREF_MEDIUM: + print_string(PRINT_ANY, + "pref", "pref %s", "medium"); + break; + case ICMPV6_ROUTER_PREF_HIGH: + print_string(PRINT_ANY, + "pref", "pref %s", "high"); + break; + default: + print_uint(PRINT_ANY, + "pref", "%u", pref); + } +} + +void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix) +{ + const char *ifname = ll_index_to_name(rta_getattr_u32(rta)); + + if (is_json_context()) + print_string(PRINT_JSON, prefix, NULL, ifname); + else { + fprintf(fp, "%s ", prefix); + color_fprintf(fp, COLOR_IFNAME, "%s ", ifname); + } +} + +static void print_cache_flags(FILE *fp, __u32 flags) +{ + json_writer_t *jw = get_json_writer(); + flags &= ~0xFFFF; + + if (jw) { + jsonw_name(jw, "cache"); + jsonw_start_array(jw); + } else { + fprintf(fp, "%s cache ", _SL_); + if (flags == 0) + return; + putc('<', fp); + } + +#define PRTFL(fl, flname) \ + if (flags & RTCF_##fl) { \ + flags &= ~RTCF_##fl; \ + if (jw) \ + jsonw_string(jw, flname); \ + else \ + fprintf(fp, "%s%s", flname, flags ? "," : "> "); \ + } + + PRTFL(LOCAL, "local"); + PRTFL(REJECT, "reject"); + PRTFL(MULTICAST, "mc"); + PRTFL(BROADCAST, "brd"); + PRTFL(DNAT, "dst-nat"); + PRTFL(SNAT, "src-nat"); + PRTFL(MASQ, "masq"); + PRTFL(DIRECTDST, "dst-direct"); + PRTFL(DIRECTSRC, "src-direct"); + PRTFL(REDIRECTED, "redirected"); + PRTFL(DOREDIRECT, "redirect"); + PRTFL(FAST, "fastroute"); + PRTFL(NOTIFY, "notify"); + PRTFL(TPROXY, "proxy"); +#undef PRTFL + + if (flags) + print_hex(PRINT_ANY, "flags", "%x>", flags); + + if (jw) + jsonw_end_array(jw); +} + +static void print_rta_cacheinfo(FILE *fp, const struct rta_cacheinfo *ci) +{ + static int hz; + + if (!hz) + hz = get_user_hz(); + + if (ci->rta_expires != 0) + print_int(PRINT_ANY, "expires", + "expires %dsec ", ci->rta_expires/hz); + if (ci->rta_error != 0) + print_uint(PRINT_ANY, "error", + "error %u ", ci->rta_error); + + if (show_stats) { + if (ci->rta_clntref) + print_uint(PRINT_ANY, "users", + "users %u ", ci->rta_clntref); + if (ci->rta_used != 0) + print_uint(PRINT_ANY, "used", + "used %u ", ci->rta_used); + if (ci->rta_lastuse != 0) + print_uint(PRINT_ANY, "age", + "age %usec ", ci->rta_lastuse/hz); + } + if (ci->rta_id) + print_0xhex(PRINT_ANY, "ipid", + "ipid 0x%04llx ", ci->rta_id); + if (ci->rta_ts || ci->rta_tsage) { + print_0xhex(PRINT_ANY, "ts", + "ts 0x%llx", ci->rta_ts); + print_uint(PRINT_ANY, "tsage", + "tsage %usec ", ci->rta_tsage); + } +} + +static void print_rta_flow(FILE *fp, const struct rtattr *rta) +{ + __u32 to = rta_getattr_u32(rta); + __u32 from = to >> 16; + SPRINT_BUF(b1); + + to &= 0xFFFF; + if (is_json_context()) { + open_json_object("flow"); + + if (from) + print_string(PRINT_JSON, "from", NULL, + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + print_string(PRINT_JSON, "to", NULL, + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + close_json_object(); + } else { + fprintf(fp, "realm%s ", from ? "s" : ""); + + if (from) + print_string(PRINT_FP, NULL, "%s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + print_string(PRINT_FP, NULL, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } +} + +static void print_rta_newdst(FILE *fp, const struct rtmsg *r, + const struct rtattr *rta) +{ + const char *newdst = format_host_rta(r->rtm_family, rta); + + if (is_json_context()) + print_string(PRINT_JSON, "to", NULL, newdst); + else { + fprintf(fp, "as to "); + print_color_string(PRINT_FP, + ifa_family_color(r->rtm_family), + NULL, "%s ", newdst); + } +} + +void print_rta_gateway(FILE *fp, unsigned char family, const struct rtattr *rta) +{ + const char *gateway = format_host_rta(family, rta); + + if (is_json_context()) + print_string(PRINT_JSON, "gateway", NULL, gateway); + else { + fprintf(fp, "via "); + print_color_string(PRINT_FP, + ifa_family_color(family), + NULL, "%s ", gateway); + } +} + +static void print_rta_via(FILE *fp, const struct rtattr *rta) +{ + size_t len = RTA_PAYLOAD(rta) - 2; + const struct rtvia *via = RTA_DATA(rta); + + if (is_json_context()) { + open_json_object("via"); + print_string(PRINT_JSON, "family", NULL, + family_name(via->rtvia_family)); + print_string(PRINT_JSON, "host", NULL, + format_host(via->rtvia_family, len, + via->rtvia_addr)); + close_json_object(); + } else { + print_string(PRINT_FP, NULL, "via %s ", + family_name(via->rtvia_family)); + print_color_string(PRINT_FP, + ifa_family_color(via->rtvia_family), + NULL, "%s ", + format_host(via->rtvia_family, + len, via->rtvia_addr)); + } +} + +static void print_rta_metrics(FILE *fp, const struct rtattr *rta) +{ + struct rtattr *mxrta[RTAX_MAX+1]; + unsigned int mxlock = 0; + int i; + + open_json_array(PRINT_JSON, "metrics"); + open_json_object(NULL); + + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)); + + if (mxrta[RTAX_LOCK]) + mxlock = rta_getattr_u32(mxrta[RTAX_LOCK]); + + for (i = 2; i <= RTAX_MAX; i++) { + __u32 val = 0U; + + if (mxrta[i] == NULL && !(mxlock & (1 << i))) + continue; + + if (mxrta[i] != NULL && i != RTAX_CC_ALGO) + val = rta_getattr_u32(mxrta[i]); + + if (i == RTAX_HOPLIMIT && (int)val == -1) + continue; + + if (!is_json_context()) { + if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i]) + fprintf(fp, "%s ", mx_names[i]); + else + fprintf(fp, "metric %d ", i); + + if (mxlock & (1<= 1000) + fprintf(fp, "%gs ", val/1e3); + else + fprintf(fp, "%ums ", val); + } + break; + case RTAX_CC_ALGO: + print_string(PRINT_ANY, "congestion", + "%s ", rta_getattr_str(mxrta[i])); + break; + } + } + + close_json_object(); + close_json_array(PRINT_JSON, NULL); +} + +static void print_rta_multipath(FILE *fp, const struct rtmsg *r, + struct rtattr *rta) +{ + const struct rtnexthop *nh = RTA_DATA(rta); + int len = RTA_PAYLOAD(rta); + int first = 1; + + open_json_array(PRINT_JSON, "nexthops"); + + while (len >= sizeof(*nh)) { + struct rtattr *tb[RTA_MAX + 1]; + + if (nh->rtnh_len > len) + break; + + open_json_object(NULL); + + if ((r->rtm_flags & RTM_F_CLONED) && + r->rtm_type == RTN_MULTICAST) { + if (first) { + print_string(PRINT_FP, NULL, "Oifs: ", NULL); + first = 0; + } else { + print_string(PRINT_FP, NULL, " ", NULL); + } + } else + print_string(PRINT_FP, NULL, "%s\tnexthop ", _SL_); + + if (nh->rtnh_len > sizeof(*nh)) { + parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), + nh->rtnh_len - sizeof(*nh)); + + if (tb[RTA_ENCAP]) + lwt_print_encap(fp, + tb[RTA_ENCAP_TYPE], + tb[RTA_ENCAP]); + if (tb[RTA_NEWDST]) + print_rta_newdst(fp, r, tb[RTA_NEWDST]); + if (tb[RTA_GATEWAY]) + print_rta_gateway(fp, r->rtm_family, + tb[RTA_GATEWAY]); + if (tb[RTA_VIA]) + print_rta_via(fp, tb[RTA_VIA]); + if (tb[RTA_FLOW]) + print_rta_flow(fp, tb[RTA_FLOW]); + } + + if ((r->rtm_flags & RTM_F_CLONED) && + r->rtm_type == RTN_MULTICAST) { + print_string(PRINT_ANY, "dev", + "%s", ll_index_to_name(nh->rtnh_ifindex)); + + if (nh->rtnh_hops != 1) + print_int(PRINT_ANY, "ttl", "(ttl>%d)", nh->rtnh_hops); + + print_string(PRINT_FP, NULL, " ", NULL); + } else { + print_string(PRINT_ANY, "dev", + "dev %s ", ll_index_to_name(nh->rtnh_ifindex)); + + if (r->rtm_family != AF_MPLS) + print_int(PRINT_ANY, "weight", + "weight %d ", nh->rtnh_hops + 1); + } + + print_rt_flags(fp, nh->rtnh_flags); + + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); +} + +int print_route(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[RTA_MAX+1]; + int family, color, host_len; + __u32 table; + int ret; + + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return -1; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + host_len = af_bit_len(r->rtm_family); + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + table = rtm_get_table(r, tb); + + if (!filter_nlmsg(n, tb, host_len)) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + ret = flush_update(); + if (ret < 0) + return ret; + } + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++rth.seq; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELROUTE) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if ((r->rtm_type != RTN_UNICAST || show_details > 0) && + (!filter.typemask || (filter.typemask & (1 << r->rtm_type)))) + print_string(PRINT_ANY, "type", "%s ", + rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + color = COLOR_NONE; + if (tb[RTA_DST]) { + family = get_real_family(r->rtm_type, r->rtm_family); + color = ifa_family_color(family); + + if (r->rtm_dst_len != host_len) { + snprintf(b1, sizeof(b1), + "%s/%u", rt_addr_n2a_rta(family, tb[RTA_DST]), + r->rtm_dst_len); + } else { + format_host_rta_r(family, tb[RTA_DST], + b1, sizeof(b1)); + + } + } else if (r->rtm_dst_len) { + snprintf(b1, sizeof(b1), "0/%d ", r->rtm_dst_len); + } else { + strncpy(b1, "default", sizeof(b1)); + } + print_color_string(PRINT_ANY, color, + "dst", "%s ", b1); + + if (tb[RTA_SRC]) { + family = get_real_family(r->rtm_type, r->rtm_family); + color = ifa_family_color(family); + + if (r->rtm_src_len != host_len) { + snprintf(b1, sizeof(b1), + "%s/%u", + rt_addr_n2a_rta(family, tb[RTA_SRC]), + r->rtm_src_len); + } else { + format_host_rta_r(family, tb[RTA_SRC], + b1, sizeof(b1)); + } + print_color_string(PRINT_ANY, color, + "from", "from %s ", b1); + } else if (r->rtm_src_len) { + snprintf(b1, sizeof(b1), "0/%u", r->rtm_src_len); + + print_string(PRINT_ANY, "src", "from %s ", b1); + } + + if (tb[RTA_NH_ID]) + print_uint(PRINT_ANY, "nhid", "nhid %u ", + rta_getattr_u32(tb[RTA_NH_ID])); + + if (tb[RTA_NEWDST]) + print_rta_newdst(fp, r, tb[RTA_NEWDST]); + + if (tb[RTA_ENCAP]) + lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]); + + if (r->rtm_tos && filter.tosmask != -1) { + print_string(PRINT_ANY, "tos", "tos %s ", + rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) + print_rta_gateway(fp, r->rtm_family, tb[RTA_GATEWAY]); + + if (tb[RTA_VIA]) + print_rta_via(fp, tb[RTA_VIA]); + + if (tb[RTA_OIF] && filter.oifmask != -1) + print_rta_if(fp, tb[RTA_OIF], "dev"); + + if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) + print_string(PRINT_ANY, + "table", "table %s ", + rtnl_rttable_n2a(table, b1, sizeof(b1))); + + if (!(r->rtm_flags & RTM_F_CLONED)) { + if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) && + filter.protocolmask != -1) + print_string(PRINT_ANY, + "protocol", "proto %s ", + rtnl_rtprot_n2a(r->rtm_protocol, + b1, sizeof(b1))); + + if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) && + filter.scopemask != -1) + print_string(PRINT_ANY, + "scope", "scope %s ", + rtnl_rtscope_n2a(r->rtm_scope, + b1, sizeof(b1))); + } + + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + const char *psrc + = rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC]); + + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + if (is_json_context()) + print_string(PRINT_JSON, "prefsrc", NULL, psrc); + else { + fprintf(fp, "src "); + print_color_string(PRINT_FP, + ifa_family_color(r->rtm_family), + NULL, "%s ", psrc); + } + + } + + if (tb[RTA_PRIORITY] && filter.metricmask != -1) + print_uint(PRINT_ANY, "metric", "metric %u ", + rta_getattr_u32(tb[RTA_PRIORITY])); + + print_rt_flags(fp, r->rtm_flags); + + if (tb[RTA_MARK]) { + unsigned int mark = rta_getattr_u32(tb[RTA_MARK]); + + if (mark) { + if (is_json_context()) + print_uint(PRINT_JSON, "mark", NULL, mark); + else if (mark >= 16) + print_0xhex(PRINT_FP, NULL, + "mark 0x%llx ", mark); + else + print_uint(PRINT_FP, NULL, + "mark %u ", mark); + } + } + + if (tb[RTA_FLOW] && filter.realmmask != ~0U) + print_rta_flow(fp, tb[RTA_FLOW]); + + if (tb[RTA_UID]) + print_uint(PRINT_ANY, "uid", "uid %u ", + rta_getattr_u32(tb[RTA_UID])); + + if (r->rtm_family == AF_INET) { + if (r->rtm_flags & RTM_F_CLONED) + print_cache_flags(fp, r->rtm_flags); + + if (tb[RTA_CACHEINFO]) + print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + } else if (r->rtm_family == AF_INET6) { + if (tb[RTA_CACHEINFO]) + print_rta_cacheinfo(fp, RTA_DATA(tb[RTA_CACHEINFO])); + } + + if (tb[RTA_METRICS]) + print_rta_metrics(fp, tb[RTA_METRICS]); + + if (tb[RTA_IIF] && filter.iifmask != -1) + print_rta_if(fp, tb[RTA_IIF], "iif"); + + if (tb[RTA_PREF]) + print_rt_pref(fp, rta_getattr_u8(tb[RTA_PREF])); + + if (tb[RTA_TTL_PROPAGATE]) { + bool propagate = rta_getattr_u8(tb[RTA_TTL_PROPAGATE]); + + if (is_json_context()) + print_bool(PRINT_JSON, "ttl-propogate", NULL, + propagate); + else + print_string(PRINT_FP, NULL, + "ttl-propogate %s", + propagate ? "enabled" : "disabled"); + } + + if (tb[RTA_MULTIPATH]) + print_rta_multipath(fp, r, tb[RTA_MULTIPATH]); + + /* If you are adding new route RTA_XXXX then place it above + * the RTA_MULTIPATH else it will appear that the last nexthop + * in the ECMP has new attributes + */ + + print_string(PRINT_FP, NULL, "\n", NULL); + close_json_object(); + fflush(fp); + return 0; +} + +static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, + struct rtattr *rta, size_t len, struct rtnexthop *rtnh, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (++argv, --argc > 0) { + if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + int family; + + NEXT_ARG(); + family = read_family(*argv); + if (family == AF_UNSPEC) + family = r->rtm_family; + else + NEXT_ARG(); + get_addr(&addr, *argv, family); + if (r->rtm_family == AF_UNSPEC) + r->rtm_family = addr.family; + if (addr.family == r->rtm_family) { + if (rta_addattr_l(rta, len, RTA_GATEWAY, + &addr.data, addr.bytelen)) + return -1; + rtnh->rtnh_len += sizeof(struct rtattr) + + addr.bytelen; + } else { + if (rta_addattr_l(rta, len, RTA_VIA, + &addr.family, addr.bytelen + 2)) + return -1; + rtnh->rtnh_len += RTA_SPACE(addr.bytelen + 2); + } + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + rtnh->rtnh_ifindex = ll_name_to_index(*argv); + if (!rtnh->rtnh_ifindex) + return nodev(*argv); + } else if (strcmp(*argv, "weight") == 0) { + unsigned int w; + + NEXT_ARG(); + if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", *argv); + rtnh->rtnh_hops = w - 1; + } else if (strcmp(*argv, "onlink") == 0) { + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + + NEXT_ARG(); + if (get_rt_realms_or_raw(&realm, *argv)) + invarg("\"realm\" value is invalid\n", *argv); + if (rta_addattr32(rta, len, RTA_FLOW, realm)) + return -1; + rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "encap") == 0) { + int old_len = rta->rta_len; + + if (lwt_parse_encap(rta, len, &argc, &argv, + RTA_ENCAP, RTA_ENCAP_TYPE)) + return -1; + rtnh->rtnh_len += rta->rta_len - old_len; + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + get_addr(&addr, *argv, r->rtm_family); + if (rta_addattr_l(rta, len, RTA_NEWDST, + &addr.data, addr.bytelen)) + return -1; + rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen; + } else + break; + } + *argcp = argc; + *argvp = argv; + return 0; +} + +static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, + int argc, char **argv) +{ + char buf[4096]; + struct rtattr *rta = (void *)buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + while (argc > 0) { + if (strcmp(*argv, "nexthop") != 0) { + fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv); + exit(-1); + } + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); + exit(-1); + } + memset(rtnh, 0, sizeof(*rtnh)); + rtnh->rtnh_len = sizeof(*rtnh); + rta->rta_len += rtnh->rtnh_len; + if (parse_one_nh(n, r, rta, 4096, rtnh, &argc, &argv)) { + fprintf(stderr, "Error: cannot parse nexthop\n"); + exit(-1); + } + rtnh = RTNH_NEXT(rtnh); + } + + if (rta->rta_len > RTA_LENGTH(0)) + return addattr_l(n, 4096, RTA_MULTIPATH, + RTA_DATA(rta), RTA_PAYLOAD(rta)); + return 0; +} + +static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[4096]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .r.rtm_family = preferred_family, + .r.rtm_table = RT_TABLE_MAIN, + .r.rtm_scope = RT_SCOPE_NOWHERE, + }; + char mxbuf[256]; + struct rtattr *mxrta = (void *)mxbuf; + unsigned int mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int nhs_ok = 0; + int scope_ok = 0; + int table_ok = 0; + int raw = 0; + int type_ok = 0; + __u32 nhid = 0; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), + RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), + RTA_NEWDST, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + int family; + + if (gw_ok) { + invarg("use nexthop syntax to specify multiple via\n", + *argv); + } + gw_ok = 1; + NEXT_ARG(); + family = read_family(*argv); + if (family == AF_UNSPEC) + family = req.r.rtm_family; + else + NEXT_ARG(); + get_addr(&addr, *argv, family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.family == req.r.rtm_family) + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, + &addr.data, addr.bytelen); + else + addattr_l(&req.n, sizeof(req), RTA_VIA, + &addr.family, addr.bytelen+2); + } else if (strcmp(*argv, "from") == 0) { + inet_prefix addr; + + NEXT_ARG(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (strcmp(*argv, "expires") == 0) { + __u32 expires; + + NEXT_ARG(); + if (get_u32(&expires, *argv, 0)) + invarg("\"expires\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires); + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + strcmp(*argv, "preference") == 0) { + __u32 metric; + + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); + } else if (strcmp(*argv, "scope") == 0) { + __u32 scope = 0; + + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid \"scope\" value\n", *argv); + req.r.rtm_scope = scope; + scope_ok = 1; + } else if (strcmp(*argv, "mtu") == 0) { + unsigned int mtu; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1< 255) + invarg("\"hoplimit\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit); + } else if (strcmp(*argv, "advmss") == 0) { + unsigned int mss; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1< 0) { + NEXT_ARG(); + + if (strcmp(*argv, "ecn") == 0) + features |= RTAX_FEATURE_ECN; + else + invarg("\"features\" value not valid\n", *argv); + break; + } + + rta_addattr32(mxrta, sizeof(mxbuf), + RTAX_FEATURES, features); + } else if (matches(*argv, "quickack") == 0) { + unsigned int quickack; + + NEXT_ARG(); + if (get_unsigned(&quickack, *argv, 0)) + invarg("\"quickack\" value is invalid\n", *argv); + if (quickack != 1 && quickack != 0) + invarg("\"quickack\" value should be 0 or 1\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), + RTAX_QUICKACK, quickack); + } else if (matches(*argv, "congctl") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= 1 << RTAX_CC_ALGO; + NEXT_ARG(); + } + rta_addattr_l(mxrta, sizeof(mxbuf), RTAX_CC_ALGO, *argv, + strlen(*argv)); + } else if (matches(*argv, "rttvar") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<rta_type = RTA_ENCAP; + rta->rta_len = RTA_LENGTH(0); + + lwt_parse_encap(rta, sizeof(buf), &argc, &argv, + RTA_ENCAP, RTA_ENCAP_TYPE); + + if (rta->rta_len > RTA_LENGTH(0)) + addraw_l(&req.n, 1024 + , RTA_DATA(rta), RTA_PAYLOAD(rta)); + } else if (strcmp(*argv, "ttl-propagate") == 0) { + __u8 ttl_prop; + + NEXT_ARG(); + if (matches(*argv, "enabled") == 0) + ttl_prop = 1; + else if (matches(*argv, "disabled") == 0) + ttl_prop = 0; + else + invarg("\"ttl-propagate\" value is invalid\n", + *argv); + + addattr8(&req.n, sizeof(req), RTA_TTL_PROPAGATE, + ttl_prop); + } else if (matches(*argv, "fastopen_no_cookie") == 0) { + unsigned int fastopen_no_cookie; + + NEXT_ARG(); + if (get_unsigned(&fastopen_no_cookie, *argv, 0)) + invarg("\"fastopen_no_cookie\" value is invalid\n", *argv); + if (fastopen_no_cookie != 1 && fastopen_no_cookie != 0) + invarg("\"fastopen_no_cookie\" value should be 0 or 1\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FASTOPEN_NO_COOKIE, fastopen_no_cookie); + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = dst.family; + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) + addattr_l(&req.n, sizeof(req), + RTA_DST, &dst.data, dst.bytelen); + } + argc--; argv++; + } + + if (!dst_ok) + usage(); + + if (d) { + int idx = ll_name_to_index(d); + + if (!idx) + return nodev(d); + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (nhs_ok && parse_nexthops(&req.n, &req.r, argc, argv)) + return -1; + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (!table_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_NAT || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_table = RT_TABLE_LOCAL; + } + if (!scope_ok) { + if (req.r.rtm_family == AF_INET6 || + req.r.rtm_family == AF_MPLS) + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + else if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_NAT) + req.r.rtm_scope = RT_SCOPE_HOST; + else if (req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_MULTICAST || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_scope = RT_SCOPE_LINK; + else if (req.r.rtm_type == RTN_UNICAST || + req.r.rtm_type == RTN_UNSPEC) { + if (cmd == RTM_DELROUTE) + req.r.rtm_scope = RT_SCOPE_NOWHERE; + else if (!gw_ok && !nhs_ok && !nhid) + req.r.rtm_scope = RT_SCOPE_LINK; + } + } + + if (!type_ok && req.r.rtm_family == AF_MPLS) + req.r.rtm_type = RTN_UNICAST; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open(ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf(stderr, "Cannot open \"%s\": %s\n", + ROUTE_FLUSH_PATH, strerror(errno)); + return -1; + } + + len = strlen(buffer); + + if ((write(flush_fd, (void *)buffer, len)) < len) { + fprintf(stderr, "Cannot flush routing cache\n"); + close(flush_fd); + return -1; + } + close(flush_fd); + return 0; +} + +static __u32 route_dump_magic = 0x45311224; + +static int save_route(struct nlmsghdr *n, void *arg) +{ + int ret; + int len = n->nlmsg_len; + struct rtmsg *r = NLMSG_DATA(n); + struct rtattr *tb[RTA_MAX+1]; + int host_len; + + host_len = af_bit_len(r->rtm_family); + len -= NLMSG_LENGTH(sizeof(*r)); + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (!filter_nlmsg(n, tb, host_len)) + return 0; + + ret = write(STDOUT_FILENO, n, n->nlmsg_len); + if ((ret > 0) && (ret != n->nlmsg_len)) { + fprintf(stderr, "Short write while saving nlmsg\n"); + ret = -EIO; + } + + return ret == n->nlmsg_len ? 0 : ret; +} + +static int save_route_prep(void) +{ + int ret; + + if (isatty(STDOUT_FILENO)) { + fprintf(stderr, "Not sending a binary stream to stdout\n"); + return -1; + } + + ret = write(STDOUT_FILENO, &route_dump_magic, sizeof(route_dump_magic)); + if (ret != sizeof(route_dump_magic)) { + fprintf(stderr, "Can't write magic to dump file\n"); + return -1; + } + + return 0; +} + +static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + struct rtmsg *rtm = NLMSG_DATA(nlh); + int err; + + rtm->rtm_protocol = filter.protocol; + if (filter.cloned) + rtm->rtm_flags |= RTM_F_CLONED; + + if (filter.tb) { + err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb); + if (err) + return err; + } + + if (filter.oif) { + err = addattr32(nlh, reqlen, RTA_OIF, filter.oif); + if (err) + return err; + } + + return 0; +} + +static int iproute_flush(int family, rtnl_filter_t filter_fn) +{ + time_t start = time(0); + char flushb[4096-512]; + int round = 0; + int ret; + + if (filter.cloned) { + if (family != AF_INET6) { + iproute_flush_cache(); + if (show_stats) + printf("*** IPv4 routing cache is flushed.\n"); + } + if (family == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + + for (;;) { + if (rtnl_routedump_req(&rth, family, iproute_dump_filter) < 0) { + perror("Cannot send dump request"); + return -2; + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { + fprintf(stderr, "Flush terminated\n"); + return -2; + } + if (filter.flushed == 0) { + if (show_stats) { + if (round == 0 && + (!filter.cloned || family == AF_INET6)) + printf("Nothing to flush.\n"); + else + printf("*** Flush is complete after %d round%s ***\n", + round, round > 1 ? "s" : ""); + } + fflush(stdout); + return 0; + } + round++; + ret = flush_update(); + if (ret < 0) + return ret; + + if (time(0) - start > 30) { + printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", + (long)(time(0) - start), filter.flushed); + return -1; + } + + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", + round, filter.flushed); + fflush(stdout); + } + } +} + +static int iproute_list_flush_or_save(int argc, char **argv, int action) +{ + int dump_family = preferred_family; + char *id = NULL; + char *od = NULL; + unsigned int mark = 0; + rtnl_filter_t filter_fn; + + if (action == IPROUTE_SAVE) { + if (save_route_prep()) + return -1; + + filter_fn = save_route; + } else + filter_fn = print_route; + + iproute_reset_filter(0); + filter.tb = RT_TABLE_MAIN; + + if ((action == IPROUTE_FLUSH) && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + filter.tb = 0; + } else if (strcmp(*argv, "cache") == 0) { + filter.cloned = 1; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } else + filter.tb = tid; + } else if (matches(*argv, "vrf") == 0) { + __u32 tid; + + NEXT_ARG(); + tid = ipvrf_get_table(*argv); + if (tid == 0) + invarg("Invalid VRF\n", *argv); + filter.tb = tid; + filter.typemask = ~(1 << RTN_LOCAL | 1< 0) { + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "from") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, + &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (matches(*argv, "iif") == 0) { + NEXT_ARG(); + idev = *argv; + } else if (matches(*argv, "mark") == 0) { + NEXT_ARG(); + if (get_unsigned(&mark, *argv, 0)) + invarg("invalid mark value", *argv); + } else if (matches(*argv, "oif") == 0 || + strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + odev = *argv; + } else if (matches(*argv, "notify") == 0) { + req.r.rtm_flags |= RTM_F_NOTIFY; + } else if (matches(*argv, "connected") == 0) { + connected = 1; + } else if (matches(*argv, "vrf") == 0) { + NEXT_ARG(); + if (!name_is_vrf(*argv)) + invarg("Invalid VRF\n", *argv); + odev = *argv; + } else if (matches(*argv, "uid") == 0) { + uid_t uid; + + NEXT_ARG(); + if (get_unsigned(&uid, *argv, 0)) + invarg("invalid UID\n", *argv); + addattr32(&req.n, sizeof(req), RTA_UID, uid); + } else if (matches(*argv, "fibmatch") == 0) { + fib_match = 1; + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_NEWDST, + &addr.data, addr.bytelen); + } else if (matches(*argv, "sport") == 0) { + __be16 sport; + + NEXT_ARG(); + if (get_be16(&sport, *argv, 0)) + invarg("invalid sport\n", *argv); + addattr16(&req.n, sizeof(req), RTA_SPORT, sport); + } else if (matches(*argv, "dport") == 0) { + __be16 dport; + + NEXT_ARG(); + if (get_be16(&dport, *argv, 0)) + invarg("invalid dport\n", *argv); + addattr16(&req.n, sizeof(req), RTA_DPORT, dport); + } else if (matches(*argv, "ipproto") == 0) { + int ipproto; + + NEXT_ARG(); + ipproto = inet_proto_a2n(*argv); + if (ipproto < 0) + invarg("Invalid \"ipproto\" value\n", + *argv); + addattr8(&req.n, sizeof(req), RTA_IP_PROTO, ipproto); + } else { + inet_prefix addr; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), + RTA_DST, &addr.data, addr.bytelen); + req.r.rtm_dst_len = addr.bitlen; + address_found = true; + } + argc--; argv++; + } + + if (!address_found) { + fprintf(stderr, "need at least a destination address\n"); + return -1; + } + + if (idev || odev) { + int idx; + + if (idev) { + idx = ll_name_to_index(idev); + if (!idx) + return nodev(idev); + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + idx = ll_name_to_index(odev); + if (!idx) + return nodev(odev); + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + if (mark) + addattr32(&req.n, sizeof(req), RTA_MARK, mark); + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + /* Only IPv4 supports the RTM_F_LOOKUP_TABLE flag */ + if (req.r.rtm_family == AF_INET) + req.r.rtm_flags |= RTM_F_LOOKUP_TABLE; + if (fib_match) + req.r.rtm_flags |= RTM_F_FIB_MATCH; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + new_json_obj(json); + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(answer); + int len = answer->nlmsg_len; + struct rtattr *tb[RTA_MAX+1]; + + if (print_route(answer, (void *)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + free(answer); + return -1; + } + + if (answer->nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "Not a route?\n"); + free(answer); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + free(answer); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + fprintf(stderr, "Failed to connect the route\n"); + free(answer); + return -1; + } + if (!odev && tb[RTA_OIF]) + tb[RTA_OIF]->rta_type = 0; + if (tb[RTA_GATEWAY]) + tb[RTA_GATEWAY]->rta_type = 0; + if (tb[RTA_VIA]) + tb[RTA_VIA]->rta_type = 0; + if (!idev && tb[RTA_IIF]) + tb[RTA_IIF]->rta_type = 0; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + delete_json_obj(); + free(answer); + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + } + + if (print_route(answer, (void *)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + free(answer); + return -1; + } + + delete_json_obj(); + free(answer); + return 0; +} + +static int rtattr_cmp(const struct rtattr *rta1, const struct rtattr *rta2) +{ + if (!rta1 || !rta2 || rta1->rta_len != rta2->rta_len) + return 1; + + return memcmp(RTA_DATA(rta1), RTA_DATA(rta2), RTA_PAYLOAD(rta1)); +} + +static int restore_handler(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + struct rtmsg *r = NLMSG_DATA(n); + struct rtattr *tb[RTA_MAX+1]; + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + int ret, prio = *(int *)arg; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + /* Restore routes in correct order: + * 0. ones for local addresses, + * 1. ones for local networks, + * 2. others (remote networks/hosts). + */ + if (!prio && !tb[RTA_GATEWAY] && (!tb[RTA_PREFSRC] || + !rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST]))) + goto restore; + else if (prio == 1 && !tb[RTA_GATEWAY] && tb[RTA_PREFSRC] && + rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST])) + goto restore; + else if (prio == 2 && tb[RTA_GATEWAY]) + goto restore; + + return 0; + +restore: + n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + + ll_init_map(&rth); + + ret = rtnl_talk(&rth, n, NULL); + if ((ret < 0) && (errno == EEXIST)) + ret = 0; + + return ret; +} + +static int route_dump_check_magic(void) +{ + int ret; + __u32 magic = 0; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "Can't restore route dump from a terminal\n"); + return -1; + } + + ret = fread(&magic, sizeof(magic), 1, stdin); + if (magic != route_dump_magic) { + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic); + return -1; + } + + return 0; +} + +static int iproute_restore(void) +{ + int pos, prio; + + if (route_dump_check_magic()) + return -1; + + pos = ftell(stdin); + if (pos == -1) { + perror("Failed to restore: ftell"); + return -1; + } + + for (prio = 0; prio < 3; prio++) { + int err; + + err = rtnl_from_file(stdin, &restore_handler, &prio); + if (err) + return -2; + + if (fseek(stdin, pos, SEEK_SET) == -1) { + perror("Failed to restore: fseek"); + return -1; + } + } + + return 0; +} + +static int show_handler(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + print_route(n, stdout); + return 0; +} + +static int iproute_showdump(void) +{ + if (route_dump_check_magic()) + return -1; + + if (rtnl_from_file(stdin, &show_handler, NULL)) + return -2; + + return 0; +} + +void iproute_reset_filter(int ifindex) +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; + filter.oif = ifindex; + if (filter.oif > 0) + filter.oifmask = -1; +} + +int do_iproute(int argc, char **argv) +{ + if (argc < 1) + return iproute_list_flush_or_save(0, NULL, IPROUTE_LIST); + + if (matches(*argv, "add") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "prepend") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, + argc-1, argv+1); + if (matches(*argv, "append") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, + argc-1, argv+1); + if (matches(*argv, "test") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iproute_modify(RTM_DELROUTE, 0, + argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_LIST); + if (matches(*argv, "get") == 0) + return iproute_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_FLUSH); + if (matches(*argv, "save") == 0) + return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_SAVE); + if (matches(*argv, "restore") == 0) + return iproute_restore(); + if (matches(*argv, "showdump") == 0) + return iproute_showdump(); + if (matches(*argv, "help") == 0) + usage(); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); + exit(-1); +} diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c new file mode 100644 index 0000000..9b4f088 --- /dev/null +++ b/ip/iproute_lwtunnel.c @@ -0,0 +1,1676 @@ +/* + * iproute_lwtunnel.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Roopa Prabhu, + * Thomas Graf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "bpf_util.h" +#include "utils.h" +#include "ip_common.h" +#include "ila_common.h" + +#include +#include +#include +#include +#include +#include +#include + +static const char *format_encap_type(int type) +{ + switch (type) { + case LWTUNNEL_ENCAP_MPLS: + return "mpls"; + case LWTUNNEL_ENCAP_IP: + return "ip"; + case LWTUNNEL_ENCAP_IP6: + return "ip6"; + case LWTUNNEL_ENCAP_ILA: + return "ila"; + case LWTUNNEL_ENCAP_BPF: + return "bpf"; + case LWTUNNEL_ENCAP_SEG6: + return "seg6"; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + return "seg6local"; + case LWTUNNEL_ENCAP_RPL: + return "rpl"; + default: + return "unknown"; + } +} + +static void encap_type_usage(void) +{ + int i; + + fprintf(stderr, "Usage: ip route ... encap TYPE [ OPTIONS ] [...]\n"); + + for (i = 1; i <= LWTUNNEL_ENCAP_MAX; i++) + fprintf(stderr, "%s %s\n", format_encap_type(i), + i == 1 ? "TYPE := " : " "); + + exit(-1); +} + +static int read_encap_type(const char *name) +{ + if (strcmp(name, "mpls") == 0) + return LWTUNNEL_ENCAP_MPLS; + else if (strcmp(name, "ip") == 0) + return LWTUNNEL_ENCAP_IP; + else if (strcmp(name, "ip6") == 0) + return LWTUNNEL_ENCAP_IP6; + else if (strcmp(name, "ila") == 0) + return LWTUNNEL_ENCAP_ILA; + else if (strcmp(name, "bpf") == 0) + return LWTUNNEL_ENCAP_BPF; + else if (strcmp(name, "seg6") == 0) + return LWTUNNEL_ENCAP_SEG6; + else if (strcmp(name, "seg6local") == 0) + return LWTUNNEL_ENCAP_SEG6_LOCAL; + else if (strcmp(name, "rpl") == 0) + return LWTUNNEL_ENCAP_RPL; + else if (strcmp(name, "help") == 0) + encap_type_usage(); + + return LWTUNNEL_ENCAP_NONE; +} + +static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh) +{ + int i; + + if (is_json_context()) + open_json_array(PRINT_JSON, "segs"); + else + fprintf(fp, "segs %d [ ", srh->first_segment + 1); + + for (i = srh->first_segment; i >= 0; i--) + print_color_string(PRINT_ANY, COLOR_INET6, + NULL, "%s ", + rt_addr_n2a(AF_INET6, 16, &srh->segments[i])); + + if (is_json_context()) + close_json_array(PRINT_JSON, NULL); + else + fprintf(fp, "] "); + + if (sr_has_hmac(srh)) { + unsigned int offset = ((srh->hdrlen + 1) << 3) - 40; + struct sr6_tlv_hmac *tlv; + + tlv = (struct sr6_tlv_hmac *)((char *)srh + offset); + print_0xhex(PRINT_ANY, "hmac", + "hmac %llX ", ntohl(tlv->hmackeyid)); + } +} + +static const char *seg6_mode_types[] = { + [SEG6_IPTUN_MODE_INLINE] = "inline", + [SEG6_IPTUN_MODE_ENCAP] = "encap", + [SEG6_IPTUN_MODE_L2ENCAP] = "l2encap", +}; + +static const char *format_seg6mode_type(int mode) +{ + if (mode < 0 || mode > ARRAY_SIZE(seg6_mode_types)) + return ""; + + return seg6_mode_types[mode]; +} + +static int read_seg6mode_type(const char *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg6_mode_types); i++) { + if (strcmp(mode, seg6_mode_types[i]) == 0) + return i; + } + + return -1; +} + +static void print_encap_seg6(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[SEG6_IPTUNNEL_MAX+1]; + struct seg6_iptunnel_encap *tuninfo; + + parse_rtattr_nested(tb, SEG6_IPTUNNEL_MAX, encap); + + if (!tb[SEG6_IPTUNNEL_SRH]) + return; + + tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]); + print_string(PRINT_ANY, "mode", + "mode %s ", format_seg6mode_type(tuninfo->mode)); + + print_srh(fp, tuninfo->srh); +} + +static void print_rpl_srh(FILE *fp, struct ipv6_rpl_sr_hdr *srh) +{ + int i; + + if (is_json_context()) + open_json_array(PRINT_JSON, "segs"); + else + fprintf(fp, "segs %d [ ", srh->segments_left); + + for (i = srh->segments_left - 1; i >= 0; i--) { + print_color_string(PRINT_ANY, COLOR_INET6, + NULL, "%s ", + rt_addr_n2a(AF_INET6, 16, &srh->rpl_segaddr[i])); + } + + if (is_json_context()) + close_json_array(PRINT_JSON, NULL); + else + fprintf(fp, "] "); +} + +static void print_encap_rpl(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[RPL_IPTUNNEL_MAX + 1]; + struct ipv6_rpl_sr_hdr *srh; + + parse_rtattr_nested(tb, RPL_IPTUNNEL_MAX, encap); + + if (!tb[RPL_IPTUNNEL_SRH]) + return; + + srh = RTA_DATA(tb[RPL_IPTUNNEL_SRH]); + + print_rpl_srh(fp, srh); +} + +static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = { + [SEG6_LOCAL_ACTION_END] = "End", + [SEG6_LOCAL_ACTION_END_X] = "End.X", + [SEG6_LOCAL_ACTION_END_T] = "End.T", + [SEG6_LOCAL_ACTION_END_DX2] = "End.DX2", + [SEG6_LOCAL_ACTION_END_DX6] = "End.DX6", + [SEG6_LOCAL_ACTION_END_DX4] = "End.DX4", + [SEG6_LOCAL_ACTION_END_DT6] = "End.DT6", + [SEG6_LOCAL_ACTION_END_DT4] = "End.DT4", + [SEG6_LOCAL_ACTION_END_B6] = "End.B6", + [SEG6_LOCAL_ACTION_END_B6_ENCAP] = "End.B6.Encaps", + [SEG6_LOCAL_ACTION_END_BM] = "End.BM", + [SEG6_LOCAL_ACTION_END_S] = "End.S", + [SEG6_LOCAL_ACTION_END_AS] = "End.AS", + [SEG6_LOCAL_ACTION_END_AM] = "End.AM", + [SEG6_LOCAL_ACTION_END_BPF] = "End.BPF", +}; + +static const char *format_action_type(int action) +{ + if (action < 0 || action > SEG6_LOCAL_ACTION_MAX) + return ""; + + return seg6_action_names[action] ?: ""; +} + +static int read_action_type(const char *name) +{ + int i; + + for (i = 0; i < SEG6_LOCAL_ACTION_MAX + 1; i++) { + if (!seg6_action_names[i]) + continue; + + if (strcmp(seg6_action_names[i], name) == 0) + return i; + } + + return SEG6_LOCAL_ACTION_UNSPEC; +} + +static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap, + const char *str) +{ + struct rtattr *tb[LWT_BPF_PROG_MAX+1]; + const char *progname = NULL; + + parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap); + + if (tb[LWT_BPF_PROG_NAME]) + progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]); + + if (is_json_context()) + print_string(PRINT_JSON, str, NULL, + progname ? : ""); + else { + fprintf(fp, "%s ", str); + if (progname) + fprintf(fp, "%s ", progname); + } +} + +static void print_encap_seg6local(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[SEG6_LOCAL_MAX + 1]; + int action; + + SPRINT_BUF(b1); + + parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap); + + if (!tb[SEG6_LOCAL_ACTION]) + return; + + action = rta_getattr_u32(tb[SEG6_LOCAL_ACTION]); + + print_string(PRINT_ANY, "action", + "action %s ", format_action_type(action)); + + if (tb[SEG6_LOCAL_SRH]) { + open_json_object("srh"); + print_srh(fp, RTA_DATA(tb[SEG6_LOCAL_SRH])); + close_json_object(); + } + + if (tb[SEG6_LOCAL_TABLE]) + print_string(PRINT_ANY, "table", "table %s ", + rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_TABLE]), + b1, sizeof(b1))); + + if (tb[SEG6_LOCAL_NH4]) { + print_string(PRINT_ANY, "nh4", + "nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4])); + } + + if (tb[SEG6_LOCAL_NH6]) { + print_string(PRINT_ANY, "nh6", + "nh6 %s ", rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6])); + } + + if (tb[SEG6_LOCAL_IIF]) { + int iif = rta_getattr_u32(tb[SEG6_LOCAL_IIF]); + + print_string(PRINT_ANY, "iif", + "iif %s ", ll_index_to_name(iif)); + } + + if (tb[SEG6_LOCAL_OIF]) { + int oif = rta_getattr_u32(tb[SEG6_LOCAL_OIF]); + + print_string(PRINT_ANY, "oif", + "oif %s ", ll_index_to_name(oif)); + } + + if (tb[SEG6_LOCAL_BPF]) + print_encap_bpf_prog(fp, tb[SEG6_LOCAL_BPF], "endpoint"); +} + +static void print_encap_mpls(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; + + parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap); + + if (tb[MPLS_IPTUNNEL_DST]) + print_string(PRINT_ANY, "dst", " %s ", + format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST])); + if (tb[MPLS_IPTUNNEL_TTL]) + print_uint(PRINT_ANY, "ttl", "ttl %u ", + rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL])); +} + +static void lwtunnel_print_geneve_opts(struct rtattr *attr) +{ + struct rtattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1]; + struct rtattr *i = RTA_DATA(attr); + int rem = RTA_PAYLOAD(attr); + char *name = "geneve_opts"; + int data_len, offset = 0; + char data[rem * 2 + 1]; + __u16 class; + __u8 type; + + print_nl(); + print_string(PRINT_FP, name, "\t%s ", name); + open_json_array(PRINT_JSON, name); + + while (rem) { + parse_rtattr(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, i, rem); + class = rta_getattr_be16(tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]); + type = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]); + data_len = RTA_PAYLOAD(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]); + hexstring_n2a(RTA_DATA(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]), + data_len, data, sizeof(data)); + offset += data_len + 20; + rem -= data_len + 20; + i = RTA_DATA(attr) + offset; + + open_json_object(NULL); + print_uint(PRINT_ANY, "class", "%u", class); + print_uint(PRINT_ANY, "type", ":%u", type); + if (rem) + print_string(PRINT_ANY, "data", ":%s,", data); + else + print_string(PRINT_ANY, "data", ":%s ", data); + close_json_object(); + } + + close_json_array(PRINT_JSON, name); +} + +static void lwtunnel_print_vxlan_opts(struct rtattr *attr) +{ + struct rtattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1]; + struct rtattr *i = RTA_DATA(attr); + int rem = RTA_PAYLOAD(attr); + char *name = "vxlan_opts"; + __u32 gbp; + + parse_rtattr(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, i, rem); + gbp = rta_getattr_u32(tb[LWTUNNEL_IP_OPT_VXLAN_GBP]); + + print_nl(); + print_string(PRINT_FP, name, "\t%s ", name); + open_json_array(PRINT_JSON, name); + open_json_object(NULL); + print_uint(PRINT_ANY, "gbp", "%u ", gbp); + close_json_object(); + close_json_array(PRINT_JSON, name); +} + +static void lwtunnel_print_erspan_opts(struct rtattr *attr) +{ + struct rtattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1]; + struct rtattr *i = RTA_DATA(attr); + char *name = "erspan_opts"; + __u8 ver, hwid, dir; + __u32 idx; + + parse_rtattr(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, i, RTA_PAYLOAD(attr)); + ver = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_VER]); + if (ver == 1) { + idx = rta_getattr_be32(tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]); + dir = 0; + hwid = 0; + } else { + idx = 0; + dir = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_DIR]); + hwid = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]); + } + + print_nl(); + print_string(PRINT_FP, name, "\t%s ", name); + open_json_array(PRINT_JSON, name); + open_json_object(NULL); + print_uint(PRINT_ANY, "ver", "%u", ver); + print_uint(PRINT_ANY, "index", ":%u", idx); + print_uint(PRINT_ANY, "dir", ":%u", dir); + print_uint(PRINT_ANY, "hwid", ":%u ", hwid); + close_json_object(); + close_json_array(PRINT_JSON, name); +} + +static void lwtunnel_print_opts(struct rtattr *attr) +{ + struct rtattr *tb_opt[LWTUNNEL_IP_OPTS_MAX + 1]; + + parse_rtattr_nested(tb_opt, LWTUNNEL_IP_OPTS_MAX, attr); + if (tb_opt[LWTUNNEL_IP_OPTS_GENEVE]) + lwtunnel_print_geneve_opts(tb_opt[LWTUNNEL_IP_OPTS_GENEVE]); + else if (tb_opt[LWTUNNEL_IP_OPTS_VXLAN]) + lwtunnel_print_vxlan_opts(tb_opt[LWTUNNEL_IP_OPTS_VXLAN]); + else if (tb_opt[LWTUNNEL_IP_OPTS_ERSPAN]) + lwtunnel_print_erspan_opts(tb_opt[LWTUNNEL_IP_OPTS_ERSPAN]); +} + +static void print_encap_ip(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[LWTUNNEL_IP_MAX+1]; + __u16 flags; + + parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap); + + if (tb[LWTUNNEL_IP_ID]) + print_u64(PRINT_ANY, "id", "id %llu ", + ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); + + if (tb[LWTUNNEL_IP_SRC]) + print_color_string(PRINT_ANY, COLOR_INET, + "src", "src %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); + + if (tb[LWTUNNEL_IP_DST]) + print_color_string(PRINT_ANY, COLOR_INET, + "dst", "dst %s ", + rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); + + if (tb[LWTUNNEL_IP_TTL]) + print_uint(PRINT_ANY, "ttl", + "ttl %u ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); + + if (tb[LWTUNNEL_IP_TOS]) + print_uint(PRINT_ANY, "tos", + "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); + + if (tb[LWTUNNEL_IP_FLAGS]) { + flags = rta_getattr_u16(tb[LWTUNNEL_IP_FLAGS]); + if (flags & TUNNEL_KEY) + print_bool(PRINT_ANY, "key", "key ", true); + if (flags & TUNNEL_CSUM) + print_bool(PRINT_ANY, "csum", "csum ", true); + if (flags & TUNNEL_SEQ) + print_bool(PRINT_ANY, "seq", "seq ", true); + } + + if (tb[LWTUNNEL_IP_OPTS]) + lwtunnel_print_opts(tb[LWTUNNEL_IP_OPTS]); +} + +static void print_encap_ila(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[ILA_ATTR_MAX+1]; + + parse_rtattr_nested(tb, ILA_ATTR_MAX, encap); + + if (tb[ILA_ATTR_LOCATOR]) { + char abuf[ADDR64_BUF_SIZE]; + + addr64_n2a(rta_getattr_u64(tb[ILA_ATTR_LOCATOR]), + abuf, sizeof(abuf)); + print_string(PRINT_ANY, "locator", + " %s ", abuf); + } + + if (tb[ILA_ATTR_CSUM_MODE]) + print_string(PRINT_ANY, "csum_mode", + " csum-mode %s ", + ila_csum_mode2name(rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]))); + + if (tb[ILA_ATTR_IDENT_TYPE]) + print_string(PRINT_ANY, "ident_type", + " ident-type %s ", + ila_ident_type2name(rta_getattr_u8(tb[ILA_ATTR_IDENT_TYPE]))); + + if (tb[ILA_ATTR_HOOK_TYPE]) + print_string(PRINT_ANY, "hook_type", + " hook-type %s ", + ila_hook_type2name(rta_getattr_u8(tb[ILA_ATTR_HOOK_TYPE]))); +} + +static void print_encap_ip6(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[LWTUNNEL_IP6_MAX+1]; + __u16 flags; + + parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap); + + if (tb[LWTUNNEL_IP6_ID]) + print_u64(PRINT_ANY, "id", "id %llu ", + ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); + + if (tb[LWTUNNEL_IP6_SRC]) + print_color_string(PRINT_ANY, COLOR_INET6, + "src", "src %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); + + if (tb[LWTUNNEL_IP6_DST]) + print_color_string(PRINT_ANY, COLOR_INET6, + "dst", "dst %s ", + rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); + + if (tb[LWTUNNEL_IP6_HOPLIMIT]) + print_u64(PRINT_ANY, "hoplimit", + "hoplimit %u ", + rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); + + if (tb[LWTUNNEL_IP6_TC]) + print_uint(PRINT_ANY, "tc", + "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); + + if (tb[LWTUNNEL_IP6_FLAGS]) { + flags = rta_getattr_u16(tb[LWTUNNEL_IP6_FLAGS]); + if (flags & TUNNEL_KEY) + print_bool(PRINT_ANY, "key", "key ", true); + if (flags & TUNNEL_CSUM) + print_bool(PRINT_ANY, "csum", "csum ", true); + if (flags & TUNNEL_SEQ) + print_bool(PRINT_ANY, "seq", "seq ", true); + } + + if (tb[LWTUNNEL_IP6_OPTS]) + lwtunnel_print_opts(tb[LWTUNNEL_IP6_OPTS]); +} + +static void print_encap_bpf(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[LWT_BPF_MAX+1]; + + parse_rtattr_nested(tb, LWT_BPF_MAX, encap); + + if (tb[LWT_BPF_IN]) + print_encap_bpf_prog(fp, tb[LWT_BPF_IN], "in"); + if (tb[LWT_BPF_OUT]) + print_encap_bpf_prog(fp, tb[LWT_BPF_OUT], "out"); + if (tb[LWT_BPF_XMIT]) + print_encap_bpf_prog(fp, tb[LWT_BPF_XMIT], "xmit"); + if (tb[LWT_BPF_XMIT_HEADROOM]) + print_uint(PRINT_ANY, "headroom", + " %u ", rta_getattr_u32(tb[LWT_BPF_XMIT_HEADROOM])); +} + +void lwt_print_encap(FILE *fp, struct rtattr *encap_type, + struct rtattr *encap) +{ + int et; + + if (!encap_type) + return; + + et = rta_getattr_u16(encap_type); + + print_string(PRINT_ANY, "encap", " encap %s ", format_encap_type(et)); + + switch (et) { + case LWTUNNEL_ENCAP_MPLS: + print_encap_mpls(fp, encap); + break; + case LWTUNNEL_ENCAP_IP: + print_encap_ip(fp, encap); + break; + case LWTUNNEL_ENCAP_ILA: + print_encap_ila(fp, encap); + break; + case LWTUNNEL_ENCAP_IP6: + print_encap_ip6(fp, encap); + break; + case LWTUNNEL_ENCAP_BPF: + print_encap_bpf(fp, encap); + break; + case LWTUNNEL_ENCAP_SEG6: + print_encap_seg6(fp, encap); + break; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + print_encap_seg6local(fp, encap); + break; + case LWTUNNEL_ENCAP_RPL: + print_encap_rpl(fp, encap); + break; + } +} + +static struct ipv6_sr_hdr *parse_srh(char *segbuf, int hmac, bool encap) +{ + struct ipv6_sr_hdr *srh; + int nsegs = 0; + int srhlen; + char *s; + int i; + + s = segbuf; + for (i = 0; *s; *s++ == ',' ? i++ : *s); + nsegs = i + 1; + + if (!encap) + nsegs++; + + srhlen = 8 + 16*nsegs; + + if (hmac) + srhlen += 40; + + srh = malloc(srhlen); + memset(srh, 0, srhlen); + + srh->hdrlen = (srhlen >> 3) - 1; + srh->type = 4; + srh->segments_left = nsegs - 1; + srh->first_segment = nsegs - 1; + + if (hmac) + srh->flags |= SR6_FLAG1_HMAC; + + i = srh->first_segment; + for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) { + inet_prefix addr; + + get_addr(&addr, s, AF_INET6); + memcpy(&srh->segments[i], addr.data, sizeof(struct in6_addr)); + i--; + } + + if (hmac) { + struct sr6_tlv_hmac *tlv; + + tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40); + tlv->tlvhdr.type = SR6_TLV_HMAC; + tlv->tlvhdr.len = 38; + tlv->hmackeyid = htonl(hmac); + } + + return srh; +} + +static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + int mode_ok = 0, segs_ok = 0, hmac_ok = 0; + struct seg6_iptunnel_encap *tuninfo; + struct ipv6_sr_hdr *srh; + char **argv = *argvp; + char segbuf[1024] = ""; + int argc = *argcp; + int encap = -1; + __u32 hmac = 0; + int ret = 0; + int srhlen; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (mode_ok++) + duparg2("mode", *argv); + encap = read_seg6mode_type(*argv); + if (encap < 0) + invarg("\"mode\" value is invalid\n", *argv); + } else if (strcmp(*argv, "segs") == 0) { + NEXT_ARG(); + if (segs_ok++) + duparg2("segs", *argv); + if (encap == -1) + invarg("\"segs\" provided before \"mode\"\n", + *argv); + + strlcpy(segbuf, *argv, 1024); + } else if (strcmp(*argv, "hmac") == 0) { + NEXT_ARG(); + if (hmac_ok++) + duparg2("hmac", *argv); + get_u32(&hmac, *argv, 0); + } else { + break; + } + argc--; argv++; + } + + srh = parse_srh(segbuf, hmac, encap); + srhlen = (srh->hdrlen + 1) << 3; + + tuninfo = malloc(sizeof(*tuninfo) + srhlen); + memset(tuninfo, 0, sizeof(*tuninfo) + srhlen); + + tuninfo->mode = encap; + + memcpy(tuninfo->srh, srh, srhlen); + + if (rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo, + sizeof(*tuninfo) + srhlen)) { + ret = -1; + goto out; + } + + *argcp = argc + 1; + *argvp = argv - 1; + +out: + free(tuninfo); + free(srh); + + return ret; +} + +static struct ipv6_rpl_sr_hdr *parse_rpl_srh(char *segbuf) +{ + struct ipv6_rpl_sr_hdr *srh; + int nsegs = 0; + int srhlen; + char *s; + int i; + + s = segbuf; + for (i = 0; *s; *s++ == ',' ? i++ : *s); + nsegs = i + 1; + + srhlen = 8 + 16 * nsegs; + + srh = calloc(1, srhlen); + + srh->hdrlen = (srhlen >> 3) - 1; + srh->type = 3; + srh->segments_left = nsegs; + + for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) { + inet_prefix addr; + + get_addr(&addr, s, AF_INET6); + memcpy(&srh->rpl_segaddr[i], addr.data, sizeof(struct in6_addr)); + i--; + } + + return srh; +} + +static int parse_encap_rpl(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + struct ipv6_rpl_sr_hdr *srh; + char **argv = *argvp; + char segbuf[1024] = ""; + int argc = *argcp; + int segs_ok = 0; + int ret = 0; + int srhlen; + + while (argc > 0) { + if (strcmp(*argv, "segs") == 0) { + NEXT_ARG(); + if (segs_ok++) + duparg2("segs", *argv); + + strlcpy(segbuf, *argv, 1024); + } else { + break; + } + argc--; argv++; + } + + srh = parse_rpl_srh(segbuf); + srhlen = (srh->hdrlen + 1) << 3; + + if (rta_addattr_l(rta, len, RPL_IPTUNNEL_SRH, srh, + srhlen)) { + ret = -1; + goto out; + } + + *argcp = argc + 1; + *argvp = argv - 1; + +out: + free(srh); + + return ret; +} + +struct lwt_x { + struct rtattr *rta; + size_t len; +}; + +static void bpf_lwt_cb(void *lwt_ptr, int fd, const char *annotation) +{ + struct lwt_x *x = lwt_ptr; + + rta_addattr32(x->rta, x->len, LWT_BPF_PROG_FD, fd); + rta_addattr_l(x->rta, x->len, LWT_BPF_PROG_NAME, annotation, + strlen(annotation) + 1); +} + +static const struct bpf_cfg_ops bpf_cb_ops = { + .ebpf_cb = bpf_lwt_cb, +}; + +static int lwt_parse_bpf(struct rtattr *rta, size_t len, + int *argcp, char ***argvp, + int attr, const enum bpf_prog_type bpf_type) +{ + struct bpf_cfg_in cfg = { + .type = bpf_type, + .argc = *argcp, + .argv = *argvp, + }; + struct lwt_x x = { + .rta = rta, + .len = len, + }; + struct rtattr *nest; + int err; + + nest = rta_nest(rta, len, attr); + err = bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &x); + if (err < 0) { + fprintf(stderr, "Failed to parse eBPF program: %s\n", + strerror(-err)); + return -1; + } + rta_nest_end(rta, nest); + + *argcp = cfg.argc; + *argvp = cfg.argv; + + return 0; +} + +static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0; + int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0; + __u32 action = 0, table, iif, oif; + struct ipv6_sr_hdr *srh; + char **argv = *argvp; + int argc = *argcp; + char segbuf[1024]; + inet_prefix addr; + __u32 hmac = 0; + int ret = 0; + + while (argc > 0) { + if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (action_ok++) + duparg2("action", *argv); + action = read_action_type(*argv); + if (!action) + invarg("\"action\" value is invalid\n", *argv); + ret = rta_addattr32(rta, len, SEG6_LOCAL_ACTION, + action); + } else if (strcmp(*argv, "table") == 0) { + NEXT_ARG(); + if (table_ok++) + duparg2("table", *argv); + rtnl_rttable_a2n(&table, *argv); + ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table); + } else if (strcmp(*argv, "nh4") == 0) { + NEXT_ARG(); + if (nh4_ok++) + duparg2("nh4", *argv); + get_addr(&addr, *argv, AF_INET); + ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH4, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "nh6") == 0) { + NEXT_ARG(); + if (nh6_ok++) + duparg2("nh6", *argv); + get_addr(&addr, *argv, AF_INET6); + ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH6, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + if (iif_ok++) + duparg2("iif", *argv); + iif = ll_name_to_index(*argv); + if (!iif) + exit(nodev(*argv)); + ret = rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif); + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + if (oif_ok++) + duparg2("oif", *argv); + oif = ll_name_to_index(*argv); + if (!oif) + exit(nodev(*argv)); + ret = rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif); + } else if (strcmp(*argv, "srh") == 0) { + NEXT_ARG(); + if (srh_ok++) + duparg2("srh", *argv); + if (strcmp(*argv, "segs") != 0) + invarg("missing \"segs\" attribute for srh\n", + *argv); + NEXT_ARG(); + if (segs_ok++) + duparg2("segs", *argv); + strncpy(segbuf, *argv, 1024); + segbuf[1023] = 0; + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + if (strcmp(*argv, "hmac") == 0) { + NEXT_ARG(); + if (hmac_ok++) + duparg2("hmac", *argv); + get_u32(&hmac, *argv, 0); + } else { + continue; + } + } else if (strcmp(*argv, "endpoint") == 0) { + NEXT_ARG(); + if (bpf_ok++) + duparg2("endpoint", *argv); + + if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF, + BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0) + exit(-1); + } else { + break; + } + if (ret) + return ret; + argc--; argv++; + } + + if (!action) { + fprintf(stderr, "Missing action type\n"); + exit(-1); + } + + if (srh_ok) { + int srhlen; + + srh = parse_srh(segbuf, hmac, + action == SEG6_LOCAL_ACTION_END_B6_ENCAP); + srhlen = (srh->hdrlen + 1) << 3; + ret = rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen); + free(srh); + } + + *argcp = argc + 1; + *argvp = argv - 1; + + return ret; +} + +static int parse_encap_mpls(struct rtattr *rta, size_t len, + int *argcp, char ***argvp) +{ + inet_prefix addr; + int argc = *argcp; + char **argv = *argvp; + int ttl_ok = 0; + + if (get_addr(&addr, *argv, AF_MPLS)) { + fprintf(stderr, + "Error: an inet address is expected rather than \"%s\".\n", + *argv); + exit(1); + } + + if (rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, + &addr.data, addr.bytelen)) + return -1; + + argc--; + argv++; + + while (argc > 0) { + if (strcmp(*argv, "ttl") == 0) { + __u8 ttl; + + NEXT_ARG(); + if (ttl_ok++) + duparg2("ttl", *argv); + if (get_u8(&ttl, *argv, 0)) + invarg("\"ttl\" value is invalid\n", *argv); + if (rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl)) + return -1; + } else { + break; + } + argc--; argv++; + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +static int lwtunnel_parse_geneve_opt(char *str, size_t len, struct rtattr *rta) +{ + struct rtattr *nest; + char *token; + int i, err; + + nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_GENEVE | NLA_F_NESTED); + i = 1; + token = strsep(&str, ":"); + while (token) { + switch (i) { + case LWTUNNEL_IP_OPT_GENEVE_CLASS: + { + __be16 opt_class; + + if (!strlen(token)) + break; + err = get_be16(&opt_class, token, 0); + if (err) + return err; + + rta_addattr16(rta, len, i, opt_class); + break; + } + case LWTUNNEL_IP_OPT_GENEVE_TYPE: + { + __u8 opt_type; + + if (!strlen(token)) + break; + err = get_u8(&opt_type, token, 0); + if (err) + return err; + + rta_addattr8(rta, len, i, opt_type); + break; + } + case LWTUNNEL_IP_OPT_GENEVE_DATA: + { + size_t token_len = strlen(token); + __u8 *opts; + + if (!token_len) + break; + opts = malloc(token_len / 2); + if (!opts) + return -1; + if (hex2mem(token, opts, token_len / 2) < 0) { + free(opts); + return -1; + } + rta_addattr_l(rta, len, i, opts, token_len / 2); + free(opts); + + break; + } + default: + fprintf(stderr, "Unknown \"geneve_opts\" type\n"); + return -1; + } + + token = strsep(&str, ":"); + i++; + } + rta_nest_end(rta, nest); + + return 0; +} + +static int lwtunnel_parse_geneve_opts(char *str, size_t len, struct rtattr *rta) +{ + char *token; + int err; + + token = strsep(&str, ","); + while (token) { + err = lwtunnel_parse_geneve_opt(token, len, rta); + if (err) + return err; + + token = strsep(&str, ","); + } + + return 0; +} + +static int lwtunnel_parse_vxlan_opts(char *str, size_t len, struct rtattr *rta) +{ + struct rtattr *nest; + __u32 gbp; + int err; + + nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_VXLAN | NLA_F_NESTED); + err = get_u32(&gbp, str, 0); + if (err) + return err; + rta_addattr32(rta, len, LWTUNNEL_IP_OPT_VXLAN_GBP, gbp); + + rta_nest_end(rta, nest); + return 0; +} + +static int lwtunnel_parse_erspan_opts(char *str, size_t len, struct rtattr *rta) +{ + struct rtattr *nest; + char *token; + int i, err; + + nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_ERSPAN | NLA_F_NESTED); + i = 1; + token = strsep(&str, ":"); + while (token) { + switch (i) { + case LWTUNNEL_IP_OPT_ERSPAN_VER: + { + __u8 opt_type; + + if (!strlen(token)) + break; + err = get_u8(&opt_type, token, 0); + if (err) + return err; + + rta_addattr8(rta, len, i, opt_type); + break; + } + case LWTUNNEL_IP_OPT_ERSPAN_INDEX: + { + __be32 opt_class; + + if (!strlen(token)) + break; + err = get_be32(&opt_class, token, 0); + if (err) + return err; + + rta_addattr32(rta, len, i, opt_class); + break; + } + case LWTUNNEL_IP_OPT_ERSPAN_DIR: + { + __u8 opt_type; + + if (!strlen(token)) + break; + err = get_u8(&opt_type, token, 0); + if (err) + return err; + + rta_addattr8(rta, len, i, opt_type); + break; + } + case LWTUNNEL_IP_OPT_ERSPAN_HWID: + { + __u8 opt_type; + + if (!strlen(token)) + break; + err = get_u8(&opt_type, token, 0); + if (err) + return err; + + rta_addattr8(rta, len, i, opt_type); + break; + } + default: + fprintf(stderr, "Unknown \"geneve_opts\" type\n"); + return -1; + } + + token = strsep(&str, ":"); + i++; + } + + rta_nest_end(rta, nest); + return 0; +} + +static int parse_encap_ip(struct rtattr *rta, size_t len, + int *argcp, char ***argvp) +{ + int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0; + int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0; + char **argv = *argvp; + int argc = *argcp; + int ret = 0; + __u16 flags = 0; + + while (argc > 0) { + if (strcmp(*argv, "id") == 0) { + __u64 id; + + NEXT_ARG(); + if (id_ok++) + duparg2("id", *argv); + if (get_be64(&id, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + ret = rta_addattr64(rta, len, LWTUNNEL_IP_ID, id); + } else if (strcmp(*argv, "dst") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (dst_ok++) + duparg2("dst", *argv); + get_addr(&addr, *argv, AF_INET); + ret = rta_addattr_l(rta, len, LWTUNNEL_IP_DST, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (src_ok++) + duparg2("src", *argv); + get_addr(&addr, *argv, AF_INET); + ret = rta_addattr_l(rta, len, LWTUNNEL_IP_SRC, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + + NEXT_ARG(); + if (tos_ok++) + duparg2("tos", *argv); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + ret = rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos); + } else if (strcmp(*argv, "ttl") == 0) { + __u8 ttl; + + NEXT_ARG(); + if (ttl_ok++) + duparg2("ttl", *argv); + if (get_u8(&ttl, *argv, 0)) + invarg("\"ttl\" value is invalid\n", *argv); + ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); + } else if (strcmp(*argv, "geneve_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_geneve_opts(*argv, len, rta); + if (ret) + invarg("\"geneve_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "vxlan_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_vxlan_opts(*argv, len, rta); + if (ret) + invarg("\"vxlan_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "erspan_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_erspan_opts(*argv, len, rta); + if (ret) + invarg("\"erspan_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "key") == 0) { + if (key_ok++) + duparg2("key", *argv); + flags |= TUNNEL_KEY; + } else if (strcmp(*argv, "csum") == 0) { + if (csum_ok++) + duparg2("csum", *argv); + flags |= TUNNEL_CSUM; + } else if (strcmp(*argv, "seq") == 0) { + if (seq_ok++) + duparg2("seq", *argv); + flags |= TUNNEL_SEQ; + } else { + break; + } + if (ret) + break; + argc--; argv++; + } + + if (flags) + ret = rta_addattr16(rta, len, LWTUNNEL_IP_FLAGS, flags); + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return ret; +} + +static int parse_encap_ila(struct rtattr *rta, size_t len, + int *argcp, char ***argvp) +{ + __u64 locator; + int argc = *argcp; + char **argv = *argvp; + int ret = 0; + + if (get_addr64(&locator, *argv) < 0) { + fprintf(stderr, "Bad locator: %s\n", *argv); + exit(1); + } + + argc--; argv++; + + if (rta_addattr64(rta, len, ILA_ATTR_LOCATOR, locator)) + return -1; + + while (argc > 0) { + if (strcmp(*argv, "csum-mode") == 0) { + int csum_mode; + + NEXT_ARG(); + + csum_mode = ila_csum_name2mode(*argv); + if (csum_mode < 0) + invarg("\"csum-mode\" value is invalid\n", + *argv); + + ret = rta_addattr8(rta, len, ILA_ATTR_CSUM_MODE, + (__u8)csum_mode); + + argc--; argv++; + } else if (strcmp(*argv, "ident-type") == 0) { + int ident_type; + + NEXT_ARG(); + + ident_type = ila_ident_name2type(*argv); + if (ident_type < 0) + invarg("\"ident-type\" value is invalid\n", + *argv); + + ret = rta_addattr8(rta, len, ILA_ATTR_IDENT_TYPE, + (__u8)ident_type); + + argc--; argv++; + } else if (strcmp(*argv, "hook-type") == 0) { + int hook_type; + + NEXT_ARG(); + + hook_type = ila_hook_name2type(*argv); + if (hook_type < 0) + invarg("\"hook-type\" value is invalid\n", + *argv); + + ret = rta_addattr8(rta, len, ILA_ATTR_HOOK_TYPE, + (__u8)hook_type); + + argc--; argv++; + } else { + break; + } + if (ret) + break; + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return ret; +} + +static int parse_encap_ip6(struct rtattr *rta, size_t len, + int *argcp, char ***argvp) +{ + int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0; + int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0; + char **argv = *argvp; + int argc = *argcp; + int ret = 0; + __u16 flags = 0; + + while (argc > 0) { + if (strcmp(*argv, "id") == 0) { + __u64 id; + + NEXT_ARG(); + if (id_ok++) + duparg2("id", *argv); + if (get_be64(&id, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + ret = rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id); + } else if (strcmp(*argv, "dst") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (dst_ok++) + duparg2("dst", *argv); + get_addr(&addr, *argv, AF_INET6); + ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (src_ok++) + duparg2("src", *argv); + get_addr(&addr, *argv, AF_INET6); + ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_SRC, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "tc") == 0) { + __u32 tc; + + NEXT_ARG(); + if (tos_ok++) + duparg2("tc", *argv); + if (rtnl_dsfield_a2n(&tc, *argv)) + invarg("\"tc\" value is invalid\n", *argv); + ret = rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc); + } else if (strcmp(*argv, "hoplimit") == 0) { + __u8 hoplimit; + + NEXT_ARG(); + if (ttl_ok++) + duparg2("hoplimit", *argv); + if (get_u8(&hoplimit, *argv, 0)) + invarg("\"hoplimit\" value is invalid\n", + *argv); + ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, + hoplimit); + } else if (strcmp(*argv, "geneve_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_geneve_opts(*argv, len, rta); + if (ret) + invarg("\"geneve_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "vxlan_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_vxlan_opts(*argv, len, rta); + if (ret) + invarg("\"vxlan_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "erspan_opts") == 0) { + struct rtattr *nest; + + if (opts_ok++) + duparg2("opts", *argv); + + NEXT_ARG(); + + nest = rta_nest(rta, len, + LWTUNNEL_IP_OPTS | NLA_F_NESTED); + ret = lwtunnel_parse_erspan_opts(*argv, len, rta); + if (ret) + invarg("\"erspan_opts\" value is invalid\n", + *argv); + rta_nest_end(rta, nest); + } else if (strcmp(*argv, "key") == 0) { + if (key_ok++) + duparg2("key", *argv); + flags |= TUNNEL_KEY; + } else if (strcmp(*argv, "csum") == 0) { + if (csum_ok++) + duparg2("csum", *argv); + flags |= TUNNEL_CSUM; + } else if (strcmp(*argv, "seq") == 0) { + if (seq_ok++) + duparg2("seq", *argv); + flags |= TUNNEL_SEQ; + } else { + break; + } + if (ret) + break; + argc--; argv++; + } + + if (flags) + ret = rta_addattr16(rta, len, LWTUNNEL_IP6_FLAGS, flags); + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return ret; +} + +static void lwt_bpf_usage(void) +{ + fprintf(stderr, "Usage: ip route ... encap bpf [ in BPF ] [ out BPF ] [ xmit BPF ] [...]\n"); + fprintf(stderr, "BPF := obj FILE [ section NAME ] [ verbose ]\n"); + exit(-1); +} + +static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + char **argv = *argvp; + int argc = *argcp; + int headroom_set = 0; + + while (argc > 0) { + if (strcmp(*argv, "in") == 0) { + NEXT_ARG(); + if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_IN, + BPF_PROG_TYPE_LWT_IN) < 0) + return -1; + } else if (strcmp(*argv, "out") == 0) { + NEXT_ARG(); + if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_OUT, + BPF_PROG_TYPE_LWT_OUT) < 0) + return -1; + } else if (strcmp(*argv, "xmit") == 0) { + NEXT_ARG(); + if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_XMIT, + BPF_PROG_TYPE_LWT_XMIT) < 0) + return -1; + } else if (strcmp(*argv, "headroom") == 0) { + unsigned int headroom; + + NEXT_ARG(); + if (get_unsigned(&headroom, *argv, 0) || headroom == 0) + invarg("headroom is invalid\n", *argv); + if (!headroom_set) + rta_addattr32(rta, len, LWT_BPF_XMIT_HEADROOM, + headroom); + headroom_set = 1; + } else if (strcmp(*argv, "help") == 0) { + lwt_bpf_usage(); + } else { + break; + } + NEXT_ARG_FWD(); + } + + /* argv is currently the first unparsed argument, + * but the lwt_parse_encap() caller will move to the next, + * so step back + */ + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp, + int encap_attr, int encap_type_attr) +{ + struct rtattr *nest; + int argc = *argcp; + char **argv = *argvp; + __u16 type; + int ret = 0; + + NEXT_ARG(); + type = read_encap_type(*argv); + if (!type) + invarg("\"encap type\" value is invalid\n", *argv); + + NEXT_ARG(); + if (argc <= 1) { + fprintf(stderr, + "Error: unexpected end of line after \"encap\"\n"); + exit(-1); + } + + nest = rta_nest(rta, len, encap_attr); + switch (type) { + case LWTUNNEL_ENCAP_MPLS: + ret = parse_encap_mpls(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_IP: + ret = parse_encap_ip(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_ILA: + ret = parse_encap_ila(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_IP6: + ret = parse_encap_ip6(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_BPF: + if (parse_encap_bpf(rta, len, &argc, &argv) < 0) + exit(-1); + break; + case LWTUNNEL_ENCAP_SEG6: + ret = parse_encap_seg6(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + ret = parse_encap_seg6local(rta, len, &argc, &argv); + break; + case LWTUNNEL_ENCAP_RPL: + ret = parse_encap_rpl(rta, len, &argc, &argv); + break; + default: + fprintf(stderr, "Error: unsupported encap type\n"); + break; + } + if (ret) + return ret; + + rta_nest_end(rta, nest); + + ret = rta_addattr16(rta, len, encap_type_attr, type); + + *argcp = argc; + *argvp = argv; + + return ret; +} diff --git a/ip/iprule.c b/ip/iprule.c new file mode 100644 index 0000000..9f5d998 --- /dev/null +++ b/ip/iprule.c @@ -0,0 +1,1070 @@ +/* + * iprule.c "ip rule". + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +enum list_action { + IPRULE_LIST, + IPRULE_FLUSH, + IPRULE_SAVE, +}; + +extern struct rtnl_handle rth; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip rule { add | del } SELECTOR ACTION\n" + " ip rule { flush | save | restore }\n" + " ip rule [ list [ SELECTOR ]]\n" + "SELECTOR := [ not ] [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK[/MASK] ]\n" + " [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n" + " [ uidrange NUMBER-NUMBER ]\n" + " [ ipproto PROTOCOL ]\n" + " [ sport [ NUMBER | NUMBER-NUMBER ]\n" + " [ dport [ NUMBER | NUMBER-NUMBER ] ]\n" + "ACTION := [ table TABLE_ID ]\n" + " [ protocol PROTO ]\n" + " [ nat ADDRESS ]\n" + " [ realms [SRCREALM/]DSTREALM ]\n" + " [ goto NUMBER ]\n" + " SUPPRESSOR\n" + "SUPPRESSOR := [ suppress_prefixlength NUMBER ]\n" + " [ suppress_ifgroup DEVGROUP ]\n" + "TABLE_ID := [ local | main | default | NUMBER ]\n"); + exit(-1); +} + +static struct +{ + int not; + int l3mdev; + int iifmask, oifmask, uidrange; + unsigned int tb; + unsigned int tos, tosmask; + unsigned int pref, prefmask; + unsigned int fwmark, fwmask; + uint64_t tun_id; + char iif[IFNAMSIZ]; + char oif[IFNAMSIZ]; + struct fib_rule_uid_range range; + inet_prefix src; + inet_prefix dst; + int protocol; + int protocolmask; + struct fib_rule_port_range sport; + struct fib_rule_port_range dport; + __u8 ipproto; +} filter; + +static inline int frh_get_table(struct fib_rule_hdr *frh, struct rtattr **tb) +{ + __u32 table = frh->table; + if (tb[RTA_TABLE]) + table = rta_getattr_u32(tb[RTA_TABLE]); + return table; +} + +static bool filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) +{ + struct fib_rule_hdr *frh = NLMSG_DATA(n); + __u32 table; + + if (preferred_family != AF_UNSPEC && frh->family != preferred_family) + return false; + + if (filter.prefmask && + filter.pref ^ (tb[FRA_PRIORITY] ? rta_getattr_u32(tb[FRA_PRIORITY]) : 0)) + return false; + if (filter.not && !(frh->flags & FIB_RULE_INVERT)) + return false; + + if (filter.src.family) { + inet_prefix *f_src = &filter.src; + + if (f_src->family != frh->family || + f_src->bitlen > frh->src_len) + return false; + + if (inet_addr_match_rta(f_src, tb[FRA_SRC])) + return false; + } + + if (filter.dst.family) { + inet_prefix *f_dst = &filter.dst; + + if (f_dst->family != frh->family || + f_dst->bitlen > frh->dst_len) + return false; + + if (inet_addr_match_rta(f_dst, tb[FRA_DST])) + return false; + } + + if (filter.tosmask && filter.tos ^ frh->tos) + return false; + + if (filter.fwmark) { + __u32 mark = 0; + + if (tb[FRA_FWMARK]) + mark = rta_getattr_u32(tb[FRA_FWMARK]); + if (filter.fwmark ^ mark) + return false; + } + if (filter.fwmask) { + __u32 mask = 0; + + if (tb[FRA_FWMASK]) + mask = rta_getattr_u32(tb[FRA_FWMASK]); + if (filter.fwmask ^ mask) + return false; + } + + if (filter.iifmask) { + if (tb[FRA_IFNAME]) { + if (strcmp(filter.iif, rta_getattr_str(tb[FRA_IFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.oifmask) { + if (tb[FRA_OIFNAME]) { + if (strcmp(filter.oif, rta_getattr_str(tb[FRA_OIFNAME])) != 0) + return false; + } else { + return false; + } + } + + if (filter.l3mdev && !(tb[FRA_L3MDEV] && rta_getattr_u8(tb[FRA_L3MDEV]))) + return false; + + if (filter.uidrange) { + struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); + + if (!tb[FRA_UID_RANGE] || + r->start != filter.range.start || + r->end != filter.range.end) + return false; + } + + if (filter.ipproto) { + __u8 ipproto = 0; + + if (tb[FRA_IP_PROTO]) + ipproto = rta_getattr_u8(tb[FRA_IP_PROTO]); + if (filter.ipproto != ipproto) + return false; + } + + if (filter.sport.start) { + const struct fib_rule_port_range *r; + + if (!tb[FRA_SPORT_RANGE]) + return false; + + r = RTA_DATA(tb[FRA_SPORT_RANGE]); + if (r->start != filter.sport.start || + r->end != filter.sport.end) + return false; + } + + if (filter.dport.start) { + const struct fib_rule_port_range *r; + + if (!tb[FRA_DPORT_RANGE]) + return false; + + r = RTA_DATA(tb[FRA_DPORT_RANGE]); + if (r->start != filter.dport.start || + r->end != filter.dport.end) + return false; + } + + if (filter.tun_id) { + __u64 tun_id = 0; + + if (tb[FRA_TUN_ID]) { + tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID])); + if (filter.tun_id != tun_id) + return false; + } else { + return false; + } + } + + table = frh_get_table(frh, tb); + if (filter.tb > 0 && filter.tb ^ table) + return false; + + return true; +} + +int print_rule(struct nlmsghdr *n, void *arg) +{ + FILE *fp = arg; + struct fib_rule_hdr *frh = NLMSG_DATA(n); + int len = n->nlmsg_len; + int host_len = -1; + __u32 table, prio = 0; + struct rtattr *tb[FRA_MAX+1]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWRULE && n->nlmsg_type != RTM_DELRULE) + return 0; + + len -= NLMSG_LENGTH(sizeof(*frh)); + if (len < 0) + return -1; + + parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); + + host_len = af_bit_len(frh->family); + + if (!filter_nlmsg(n, tb, host_len)) + return 0; + + open_json_object(NULL); + if (n->nlmsg_type == RTM_DELRULE) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[FRA_PRIORITY]) + prio = rta_getattr_u32(tb[FRA_PRIORITY]); + + print_uint(PRINT_ANY, "priority", "%u:\t", prio); + + if (frh->flags & FIB_RULE_INVERT) + print_null(PRINT_ANY, "not", "not ", NULL); + + if (tb[FRA_SRC]) { + const char *src = rt_addr_n2a_rta(frh->family, tb[FRA_SRC]); + + print_string(PRINT_FP, NULL, "from ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "src", "%s", src); + if (frh->src_len != host_len) + print_uint(PRINT_ANY, "srclen", "/%u", frh->src_len); + } else if (frh->src_len) { + print_string(PRINT_ANY, "src", "from %s", "0"); + print_uint(PRINT_ANY, "srclen", "/%u", frh->src_len); + } else { + print_string(PRINT_ANY, "src", "from %s", "all"); + } + + if (tb[FRA_DST]) { + const char *dst = rt_addr_n2a_rta(frh->family, tb[FRA_DST]); + + print_string(PRINT_FP, NULL, " to ", NULL); + print_color_string(PRINT_ANY, ifa_family_color(frh->family), + "dst", "%s", dst); + if (frh->dst_len != host_len) + print_uint(PRINT_ANY, "dstlen", "/%u", frh->dst_len); + } else if (frh->dst_len) { + print_string(PRINT_ANY, "dst", " to %s", "0"); + print_uint(PRINT_ANY, "dstlen", "/%u", frh->dst_len); + } + + if (frh->tos) { + print_string(PRINT_ANY, "tos", + " tos %s", + rtnl_dsfield_n2a(frh->tos, b1, sizeof(b1))); + } + + if (tb[FRA_FWMARK] || tb[FRA_FWMASK]) { + __u32 mark = 0, mask = 0; + + if (tb[FRA_FWMARK]) + mark = rta_getattr_u32(tb[FRA_FWMARK]); + + if (tb[FRA_FWMASK] && + (mask = rta_getattr_u32(tb[FRA_FWMASK])) != 0xFFFFFFFF) { + print_0xhex(PRINT_ANY, "fwmark", " fwmark %#llx", mark); + print_0xhex(PRINT_ANY, "fwmask", "/%#llx", mask); + } else { + print_0xhex(PRINT_ANY, "fwmark", " fwmark %#llx", mark); + } + } + + if (tb[FRA_IFNAME]) { + if (!is_json_context()) + fprintf(fp, " iif "); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "iif", "%s", + rta_getattr_str(tb[FRA_IFNAME])); + + if (frh->flags & FIB_RULE_IIF_DETACHED) + print_null(PRINT_ANY, "iif_detached", " [detached]", + NULL); + } + + if (tb[FRA_OIFNAME]) { + if (!is_json_context()) + fprintf(fp, " oif "); + + print_color_string(PRINT_ANY, COLOR_IFNAME, "oif", "%s", + rta_getattr_str(tb[FRA_OIFNAME])); + + if (frh->flags & FIB_RULE_OIF_DETACHED) + print_null(PRINT_ANY, "oif_detached", " [detached]", + NULL); + } + + if (tb[FRA_L3MDEV]) { + __u8 mdev = rta_getattr_u8(tb[FRA_L3MDEV]); + + if (mdev) + print_null(PRINT_ANY, "l3mdev", + " lookup [l3mdev-table]", NULL); + } + + if (tb[FRA_UID_RANGE]) { + struct fib_rule_uid_range *r = RTA_DATA(tb[FRA_UID_RANGE]); + + print_uint(PRINT_ANY, "uid_start", " uidrange %u", r->start); + print_uint(PRINT_ANY, "uid_end", "-%u", r->end); + } + + if (tb[FRA_IP_PROTO]) { + SPRINT_BUF(pbuf); + print_string(PRINT_ANY, "ipproto", " ipproto %s", + inet_proto_n2a(rta_getattr_u8(tb[FRA_IP_PROTO]), + pbuf, sizeof(pbuf))); + } + + if (tb[FRA_SPORT_RANGE]) { + struct fib_rule_port_range *r = RTA_DATA(tb[FRA_SPORT_RANGE]); + + if (r->start == r->end) { + print_uint(PRINT_ANY, "sport", " sport %u", r->start); + } else { + print_uint(PRINT_ANY, "sport_start", " sport %u", + r->start); + print_uint(PRINT_ANY, "sport_end", "-%u", r->end); + } + } + + if (tb[FRA_DPORT_RANGE]) { + struct fib_rule_port_range *r = RTA_DATA(tb[FRA_DPORT_RANGE]); + + if (r->start == r->end) { + print_uint(PRINT_ANY, "dport", " dport %u", r->start); + } else { + print_uint(PRINT_ANY, "dport_start", " dport %u", + r->start); + print_uint(PRINT_ANY, "dport_end", "-%u", r->end); + } + } + + if (tb[FRA_TUN_ID]) { + __u64 tun_id = ntohll(rta_getattr_u64(tb[FRA_TUN_ID])); + + print_u64(PRINT_ANY, "tun_id", " tun_id %llu", tun_id); + } + + table = frh_get_table(frh, tb); + if (table) { + print_string(PRINT_ANY, "table", + " lookup %s", + rtnl_rttable_n2a(table, b1, sizeof(b1))); + + if (tb[FRA_SUPPRESS_PREFIXLEN]) { + int pl = rta_getattr_u32(tb[FRA_SUPPRESS_PREFIXLEN]); + + if (pl != -1) + print_int(PRINT_ANY, "suppress_prefixlen", + " suppress_prefixlength %d", pl); + } + + if (tb[FRA_SUPPRESS_IFGROUP]) { + int group = rta_getattr_u32(tb[FRA_SUPPRESS_IFGROUP]); + + if (group != -1) { + const char *grname + = rtnl_group_n2a(group, b1, sizeof(b1)); + + print_string(PRINT_ANY, "suppress_ifgroup", + " suppress_ifgroup %s", grname); + } + } + } + + if (tb[FRA_FLOW]) { + __u32 to = rta_getattr_u32(tb[FRA_FLOW]); + __u32 from = to>>16; + + to &= 0xFFFF; + if (from) + print_string(PRINT_ANY, + "flow_from", " realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + else + print_string(PRINT_FP, NULL, " realms ", NULL); + + print_string(PRINT_ANY, "flow_to", "%s", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + + if (frh->action == RTN_NAT) { + if (tb[RTA_GATEWAY]) { + const char *gateway; + + gateway = format_host_rta(frh->family, tb[RTA_GATEWAY]); + + print_string(PRINT_ANY, "nat_gateway", + " map-to %s", gateway); + } else { + print_null(PRINT_ANY, "masquerade", " masquerade", NULL); + } + } else if (frh->action == FR_ACT_GOTO) { + if (tb[FRA_GOTO]) + print_uint(PRINT_ANY, "goto", " goto %u", + rta_getattr_u32(tb[FRA_GOTO])); + else + print_string(PRINT_ANY, "goto", " goto %s", "none"); + + if (frh->flags & FIB_RULE_UNRESOLVED) + print_null(PRINT_ANY, "unresolved", + " [unresolved]", NULL); + } else if (frh->action == FR_ACT_NOP) { + print_null(PRINT_ANY, "nop", " nop", NULL); + } else if (frh->action != FR_ACT_TO_TBL) { + print_string(PRINT_ANY, "action", " %s", + rtnl_rtntype_n2a(frh->action, b1, sizeof(b1))); + } + + if (tb[FRA_PROTOCOL]) { + __u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]); + + if ((protocol && protocol != RTPROT_KERNEL) || show_details > 0) { + print_string(PRINT_ANY, "protocol", " proto %s", + rtnl_rtprot_n2a(protocol, b1, sizeof(b1))); + } + } + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + fflush(fp); + return 0; +} + +static __u32 rule_dump_magic = 0x71706986; + +static int save_rule_prep(void) +{ + int ret; + + if (isatty(STDOUT_FILENO)) { + fprintf(stderr, "Not sending a binary stream to stdout\n"); + return -1; + } + + ret = write(STDOUT_FILENO, &rule_dump_magic, sizeof(rule_dump_magic)); + if (ret != sizeof(rule_dump_magic)) { + fprintf(stderr, "Can't write magic to dump file\n"); + return -1; + } + + return 0; +} + +static int save_rule(struct nlmsghdr *n, void *arg) +{ + int ret; + + ret = write(STDOUT_FILENO, n, n->nlmsg_len); + if ((ret > 0) && (ret != n->nlmsg_len)) { + fprintf(stderr, "Short write while saving nlmsg\n"); + ret = -EIO; + } + + return ret == n->nlmsg_len ? 0 : ret; +} + +static int flush_rule(struct nlmsghdr *n, void *arg) +{ + struct rtnl_handle rth2; + struct fib_rule_hdr *frh = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[FRA_MAX+1]; + int host_len = -1; + + len -= NLMSG_LENGTH(sizeof(*frh)); + if (len < 0) + return -1; + + parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); + + host_len = af_bit_len(frh->family); + if (!filter_nlmsg(n, tb, host_len)) + return 0; + + if (tb[FRA_PROTOCOL]) { + __u8 protocol = rta_getattr_u8(tb[FRA_PROTOCOL]); + + if ((filter.protocol ^ protocol) & filter.protocolmask) + return 0; + } + + if (tb[FRA_PRIORITY]) { + n->nlmsg_type = RTM_DELRULE; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth2, 0) < 0) + return -1; + + if (rtnl_talk(&rth2, n, NULL) < 0) + return -2; + + rtnl_close(&rth2); + } + + return 0; +} + +static int iprule_list_flush_or_save(int argc, char **argv, int action) +{ + rtnl_filter_t filter_fn; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (action == IPRULE_SAVE && argc > 0) { + fprintf(stderr, "\"ip rule save\" does not take any arguments.\n"); + return -1; + } + + switch (action) { + case IPRULE_SAVE: + if (save_rule_prep()) + return -1; + filter_fn = save_rule; + break; + case IPRULE_FLUSH: + filter_fn = flush_rule; + break; + default: + filter_fn = print_rule; + } + + memset(&filter, 0, sizeof(filter)); + + while (argc > 0) { + if (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value is invalid\n", *argv); + filter.pref = pref; + filter.prefmask = 1; + } else if (strcmp(*argv, "not") == 0) { + filter.not = 1; + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = 1; + } else if (strcmp(*argv, "fwmark") == 0) { + char *slash; + __u32 fwmark, fwmask; + + NEXT_ARG(); + slash = strchr(*argv, '/'); + if (slash != NULL) + *slash = '\0'; + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + filter.fwmark = fwmark; + if (slash) { + if (get_u32(&fwmask, slash+1, 0)) + invarg("fwmask value is invalid\n", + slash+1); + filter.fwmask = fwmask; + } + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + if (get_ifname(filter.iif, *argv)) + invarg("\"iif\"/\"dev\" not a valid ifname", *argv); + filter.iifmask = 1; + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + if (get_ifname(filter.oif, *argv)) + invarg("\"oif\" not a valid ifname", *argv); + filter.oifmask = 1; + } else if (strcmp(*argv, "l3mdev") == 0) { + filter.l3mdev = 1; + } else if (strcmp(*argv, "uidrange") == 0) { + NEXT_ARG(); + filter.uidrange = 1; + if (sscanf(*argv, "%u-%u", + &filter.range.start, + &filter.range.end) != 2) + invarg("invalid UID range\n", *argv); + + } else if (matches(*argv, "tun_id") == 0) { + __u64 tun_id; + + NEXT_ARG(); + if (get_u64(&tun_id, *argv, 0)) + invarg("\"tun_id\" value is invalid\n", *argv); + filter.tun_id = tun_id; + } else if (matches(*argv, "lookup") == 0 || + matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("table id value is invalid\n", *argv); + filter.tb = tid; + } else if (matches(*argv, "from") == 0 || + matches(*argv, "src") == 0) { + NEXT_ARG(); + if (get_prefix(&filter.src, *argv, af)) + invarg("from value is invalid\n", *argv); + } else if (matches(*argv, "protocol") == 0) { + __u32 prot; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (strcmp(*argv, "ipproto") == 0) { + int ipproto; + + NEXT_ARG(); + ipproto = inet_proto_a2n(*argv); + if (ipproto < 0) + invarg("Invalid \"ipproto\" value\n", *argv); + filter.ipproto = ipproto; + } else if (strcmp(*argv, "sport") == 0) { + struct fib_rule_port_range r; + int ret; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid port range\n", *argv); + filter.sport = r; + } else if (strcmp(*argv, "dport") == 0) { + struct fib_rule_port_range r; + int ret; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid dport range\n", *argv); + filter.dport = r; + } else{ + if (matches(*argv, "dst") == 0 || + matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (get_prefix(&filter.dst, *argv, af)) + invarg("to value is invalid\n", *argv); + } + argc--; argv++; + } + + if (rtnl_ruledump_req(&rth, af) < 0) { + perror("Cannot send dump request"); + return 1; + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + delete_json_obj(); + + return 0; +} + +static int rule_dump_check_magic(void) +{ + int ret; + __u32 magic = 0; + + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "Can't restore rule dump from a terminal\n"); + return -1; + } + + ret = fread(&magic, sizeof(magic), 1, stdin); + if (magic != rule_dump_magic) { + fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", + ret, magic); + return -1; + } + + return 0; +} + +static int restore_handler(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + int ret; + + n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + + ll_init_map(&rth); + + ret = rtnl_talk(&rth, n, NULL); + if ((ret < 0) && (errno == EEXIST)) + ret = 0; + + return ret; +} + + +static int iprule_restore(void) +{ + if (rule_dump_check_magic()) + exit(-1); + + exit(rtnl_from_file(stdin, &restore_handler, NULL)); +} + +static int iprule_modify(int cmd, int argc, char **argv) +{ + int l3mdev_rule = 0; + int table_ok = 0; + __u32 tid = 0; + struct { + struct nlmsghdr n; + struct fib_rule_hdr frh; + char buf[1024]; + } req = { + .n.nlmsg_type = cmd, + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)), + .n.nlmsg_flags = NLM_F_REQUEST, + .frh.family = preferred_family, + .frh.action = FR_ACT_UNSPEC, + }; + + if (cmd == RTM_NEWRULE) { + if (argc == 0) { + fprintf(stderr, + "\"ip rule add\" requires arguments.\n"); + return -1; + } + req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; + req.frh.action = FR_ACT_TO_TBL; + } + + if (cmd == RTM_DELRULE && argc == 0) { + fprintf(stderr, "\"ip rule del\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (strcmp(*argv, "not") == 0) { + req.frh.flags |= FIB_RULE_INVERT; + } else if (strcmp(*argv, "from") == 0) { + inet_prefix dst; + + NEXT_ARG(); + get_prefix(&dst, *argv, req.frh.family); + req.frh.src_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), FRA_SRC, + &dst.data, dst.bytelen); + } else if (strcmp(*argv, "to") == 0) { + inet_prefix dst; + + NEXT_ARG(); + get_prefix(&dst, *argv, req.frh.family); + req.frh.dst_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), FRA_DST, + &dst.data, dst.bytelen); + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), FRA_PRIORITY, pref); + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.frh.tos = tos; + } else if (strcmp(*argv, "fwmark") == 0) { + char *slash; + __u32 fwmark, fwmask; + + NEXT_ARG(); + + slash = strchr(*argv, '/'); + if (slash != NULL) + *slash = '\0'; + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), FRA_FWMARK, fwmark); + if (slash) { + if (get_u32(&fwmask, slash+1, 0)) + invarg("fwmask value is invalid\n", + slash+1); + addattr32(&req.n, sizeof(req), + FRA_FWMASK, fwmask); + } + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + + NEXT_ARG(); + if (get_rt_realms_or_raw(&realm, *argv)) + invarg("invalid realms\n", *argv); + addattr32(&req.n, sizeof(req), FRA_FLOW, realm); + } else if (matches(*argv, "protocol") == 0) { + __u32 proto; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&proto, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + addattr8(&req.n, sizeof(req), FRA_PROTOCOL, proto); + } else if (matches(*argv, "tun_id") == 0) { + __u64 tun_id; + + NEXT_ARG(); + if (get_be64(&tun_id, *argv, 0)) + invarg("\"tun_id\" value is invalid\n", *argv); + addattr64(&req.n, sizeof(req), FRA_TUN_ID, tun_id); + } else if (matches(*argv, "table") == 0 || + strcmp(*argv, "lookup") == 0) { + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("invalid table ID\n", *argv); + if (tid < 256) + req.frh.table = tid; + else { + req.frh.table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), FRA_TABLE, tid); + } + table_ok = 1; + } else if (matches(*argv, "suppress_prefixlength") == 0 || + strcmp(*argv, "sup_pl") == 0) { + int pl; + + NEXT_ARG(); + if (get_s32(&pl, *argv, 0) || pl < 0) + invarg("suppress_prefixlength value is invalid\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_PREFIXLEN, pl); + } else if (matches(*argv, "suppress_ifgroup") == 0 || + strcmp(*argv, "sup_group") == 0) { + NEXT_ARG(); + int group; + + if (rtnl_group_a2n(&group, *argv)) + invarg("Invalid \"suppress_ifgroup\" value\n", + *argv); + addattr32(&req.n, sizeof(req), + FRA_SUPPRESS_IFGROUP, group); + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"iif\"/\"dev\" not a valid ifname", *argv); + addattr_l(&req.n, sizeof(req), FRA_IFNAME, + *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"oif\" not a valid ifname", *argv); + addattr_l(&req.n, sizeof(req), FRA_OIFNAME, + *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "l3mdev") == 0) { + addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1); + table_ok = 1; + l3mdev_rule = 1; + } else if (strcmp(*argv, "uidrange") == 0) { + struct fib_rule_uid_range r; + + NEXT_ARG(); + if (sscanf(*argv, "%u-%u", &r.start, &r.end) != 2) + invarg("invalid UID range\n", *argv); + addattr_l(&req.n, sizeof(req), FRA_UID_RANGE, &r, + sizeof(r)); + } else if (strcmp(*argv, "nat") == 0 || + matches(*argv, "map-to") == 0) { + NEXT_ARG(); + fprintf(stderr, "Warning: route NAT is deprecated\n"); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, + get_addr32(*argv)); + req.frh.action = RTN_NAT; + } else if (strcmp(*argv, "ipproto") == 0) { + int ipproto; + + NEXT_ARG(); + ipproto = inet_proto_a2n(*argv); + if (ipproto < 0) + invarg("Invalid \"ipproto\" value\n", + *argv); + addattr8(&req.n, sizeof(req), FRA_IP_PROTO, ipproto); + } else if (strcmp(*argv, "sport") == 0) { + struct fib_rule_port_range r; + int ret = 0; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid port range\n", *argv); + addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &r, + sizeof(r)); + } else if (strcmp(*argv, "dport") == 0) { + struct fib_rule_port_range r; + int ret = 0; + + NEXT_ARG(); + ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end); + if (ret == 1) + r.end = r.start; + else if (ret != 2) + invarg("invalid dport range\n", *argv); + addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r, + sizeof(r)); + } else { + int type; + + if (strcmp(*argv, "type") == 0) + NEXT_ARG(); + + if (matches(*argv, "help") == 0) + usage(); + else if (matches(*argv, "goto") == 0) { + __u32 target; + + type = FR_ACT_GOTO; + NEXT_ARG(); + if (get_u32(&target, *argv, 0)) + invarg("invalid target\n", *argv); + addattr32(&req.n, sizeof(req), + FRA_GOTO, target); + } else if (matches(*argv, "nop") == 0) + type = FR_ACT_NOP; + else if (rtnl_rtntype_a2n(&type, *argv)) + invarg("Failed to parse rule type", *argv); + req.frh.action = type; + table_ok = 1; + } + argc--; + argv++; + } + + if (l3mdev_rule && tid != 0) { + fprintf(stderr, + "table can not be specified for l3mdev rules\n"); + return -EINVAL; + } + + if (req.frh.family == AF_UNSPEC) + req.frh.family = AF_INET; + + if (!table_ok && cmd == RTM_NEWRULE) + req.frh.table = RT_TABLE_MAIN; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +int do_iprule(int argc, char **argv) +{ + if (argc < 1) { + return iprule_list_flush_or_save(0, NULL, IPRULE_LIST); + } else if (matches(argv[0], "list") == 0 || + matches(argv[0], "lst") == 0 || + matches(argv[0], "show") == 0) { + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_LIST); + } else if (matches(argv[0], "save") == 0) { + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_SAVE); + } else if (matches(argv[0], "restore") == 0) { + return iprule_restore(); + } else if (matches(argv[0], "add") == 0) { + return iprule_modify(RTM_NEWRULE, argc-1, argv+1); + } else if (matches(argv[0], "delete") == 0) { + return iprule_modify(RTM_DELRULE, argc-1, argv+1); + } else if (matches(argv[0], "flush") == 0) { + return iprule_list_flush_or_save(argc-1, argv+1, IPRULE_FLUSH); + } else if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); + exit(-1); +} + +int do_multirule(int argc, char **argv) +{ + switch (preferred_family) { + case AF_UNSPEC: + case AF_INET: + preferred_family = RTNL_FAMILY_IPMR; + break; + case AF_INET6: + preferred_family = RTNL_FAMILY_IP6MR; + break; + case RTNL_FAMILY_IPMR: + case RTNL_FAMILY_IP6MR: + break; + default: + fprintf(stderr, + "Multicast rules are only supported for IPv4/IPv6, was: %i\n", + preferred_family); + exit(-1); + } + + return do_iprule(argc, argv); +} diff --git a/ip/ipseg6.c b/ip/ipseg6.c new file mode 100644 index 0000000..56a7699 --- /dev/null +++ b/ip/ipseg6.c @@ -0,0 +1,253 @@ +/* + * seg6.c "ip sr/seg6" + * + * 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; + * + * Author: David Lebrun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" +#include "ip_common.h" +#include "libgenl.h" +#include "json_print.h" + +#define HMAC_KEY_PROMPT "Enter secret for HMAC key ID (blank to delete): " + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip sr { COMMAND | help }\n" + " ip sr hmac show\n" + " ip sr hmac set KEYID ALGO\n" + " ip sr tunsrc show\n" + " ip sr tunsrc set ADDRESS\n" + "where ALGO := { sha1 | sha256 }\n"); + exit(-1); +} + +static struct rtnl_handle grth = { .fd = -1 }; +static int genl_family = -1; + +#define SEG6_REQUEST(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ + SEG6_GENL_VERSION, _cmd, _flags) + +static struct { + unsigned int cmd; + inet_prefix addr; + __u32 keyid; + const char *pass; + __u8 alg_id; +} opts; + +static void print_dumphmac(struct rtattr *attrs[]) +{ + char secret[64]; + char *algstr; + __u8 slen = rta_getattr_u8(attrs[SEG6_ATTR_SECRETLEN]); + __u8 alg_id = rta_getattr_u8(attrs[SEG6_ATTR_ALGID]); + + memset(secret, 0, 64); + + if (slen > 63) { + fprintf(stderr, "HMAC secret length %d > 63, truncated\n", slen); + slen = 63; + } + + memcpy(secret, RTA_DATA(attrs[SEG6_ATTR_SECRET]), slen); + + switch (alg_id) { + case SEG6_HMAC_ALGO_SHA1: + algstr = "sha1"; + break; + case SEG6_HMAC_ALGO_SHA256: + algstr = "sha256"; + break; + default: + algstr = ""; + } + + print_uint(PRINT_ANY, "hmac", "hmac %u ", + rta_getattr_u32(attrs[SEG6_ATTR_HMACKEYID])); + print_string(PRINT_ANY, "algo", "algo %s ", algstr); + print_string(PRINT_ANY, "secret", "secret \"%s\"\n", secret); +} + +static void print_tunsrc(struct rtattr *attrs[]) +{ + const char *dst + = rt_addr_n2a(AF_INET6, 16, + RTA_DATA(attrs[SEG6_ATTR_DST])); + + print_string(PRINT_ANY, "tunsrc", + "tunsrc addr %s\n", dst); +} + +static int process_msg(struct nlmsghdr *n, void *arg) +{ + struct rtattr *attrs[SEG6_ATTR_MAX + 1]; + struct genlmsghdr *ghdr; + int len = n->nlmsg_len; + + if (n->nlmsg_type != genl_family) + return -1; + + len -= NLMSG_LENGTH(GENL_HDRLEN); + if (len < 0) + return -1; + + ghdr = NLMSG_DATA(n); + + parse_rtattr(attrs, SEG6_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len); + + open_json_object(NULL); + switch (ghdr->cmd) { + case SEG6_CMD_DUMPHMAC: + print_dumphmac(attrs); + break; + + case SEG6_CMD_GET_TUNSRC: + print_tunsrc(attrs); + break; + } + close_json_object(); + + return 0; +} + +static int seg6_do_cmd(void) +{ + SEG6_REQUEST(req, 1024, opts.cmd, NLM_F_REQUEST); + struct nlmsghdr *answer; + int repl = 0, dump = 0; + + if (genl_family < 0) { + if (rtnl_open_byproto(&grth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + exit(1); + } + genl_family = genl_resolve_family(&grth, SEG6_GENL_NAME); + if (genl_family < 0) + exit(1); + req.n.nlmsg_type = genl_family; + } + + switch (opts.cmd) { + case SEG6_CMD_SETHMAC: + { + addattr32(&req.n, sizeof(req), SEG6_ATTR_HMACKEYID, opts.keyid); + addattr8(&req.n, sizeof(req), SEG6_ATTR_SECRETLEN, + strlen(opts.pass)); + addattr8(&req.n, sizeof(req), SEG6_ATTR_ALGID, opts.alg_id); + if (strlen(opts.pass)) + addattr_l(&req.n, sizeof(req), SEG6_ATTR_SECRET, + opts.pass, strlen(opts.pass)); + break; + } + case SEG6_CMD_SET_TUNSRC: + addattr_l(&req.n, sizeof(req), SEG6_ATTR_DST, opts.addr.data, + sizeof(struct in6_addr)); + break; + case SEG6_CMD_DUMPHMAC: + dump = 1; + break; + case SEG6_CMD_GET_TUNSRC: + repl = 1; + break; + } + + if (!repl && !dump) { + if (rtnl_talk(&grth, &req.n, NULL) < 0) + return -1; + } else if (repl) { + if (rtnl_talk(&grth, &req.n, &answer) < 0) + return -2; + new_json_obj(json); + if (process_msg(answer, stdout) < 0) { + fprintf(stderr, "Error parsing reply\n"); + exit(1); + } + delete_json_obj(); + free(answer); + } else { + req.n.nlmsg_flags |= NLM_F_DUMP; + req.n.nlmsg_seq = grth.dump = ++grth.seq; + if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) { + perror("Failed to send dump request"); + exit(1); + } + + new_json_obj(json); + if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + delete_json_obj(); + fflush(stdout); + } + + return 0; +} + +int do_seg6(int argc, char **argv) +{ + if (argc < 1 || matches(*argv, "help") == 0) + usage(); + + memset(&opts, 0, sizeof(opts)); + + if (matches(*argv, "hmac") == 0) { + NEXT_ARG(); + if (matches(*argv, "show") == 0) { + opts.cmd = SEG6_CMD_DUMPHMAC; + } else if (matches(*argv, "set") == 0) { + NEXT_ARG(); + if (get_u32(&opts.keyid, *argv, 0) || opts.keyid == 0) + invarg("hmac KEYID value is invalid", *argv); + NEXT_ARG(); + if (strcmp(*argv, "sha1") == 0) { + opts.alg_id = SEG6_HMAC_ALGO_SHA1; + } else if (strcmp(*argv, "sha256") == 0) { + opts.alg_id = SEG6_HMAC_ALGO_SHA256; + } else { + invarg("hmac ALGO value is invalid", *argv); + } + opts.cmd = SEG6_CMD_SETHMAC; + opts.pass = getpass(HMAC_KEY_PROMPT); + } else { + invarg("unknown", *argv); + } + } else if (matches(*argv, "tunsrc") == 0) { + NEXT_ARG(); + if (matches(*argv, "show") == 0) { + opts.cmd = SEG6_CMD_GET_TUNSRC; + } else if (matches(*argv, "set") == 0) { + NEXT_ARG(); + opts.cmd = SEG6_CMD_SET_TUNSRC; + get_addr(&opts.addr, *argv, AF_INET6); + } else { + invarg("unknown", *argv); + } + } else { + invarg("unknown", *argv); + } + + return seg6_do_cmd(); +} diff --git a/ip/iptoken.c b/ip/iptoken.c new file mode 100644 index 0000000..9f35689 --- /dev/null +++ b/ip/iptoken.c @@ -0,0 +1,208 @@ +/* + * iptoken.c "ip token" + * + * 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. + * + * Authors: Daniel Borkmann, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "json_print.h" + +extern struct rtnl_handle rth; + +struct rtnl_dump_args { + FILE *fp; + int ifindex; +}; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip token [ list | set | del | get ] [ TOKEN ] [ dev DEV ]\n"); + exit(-1); +} + +static int print_token(struct nlmsghdr *n, void *arg) +{ + struct rtnl_dump_args *args = arg; + FILE *fp = args->fp; + int ifindex = args->ifindex; + struct ifinfomsg *ifi = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *ltb[IFLA_INET6_MAX + 1]; + + if (n->nlmsg_type != RTM_NEWLINK) + return -1; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (ifi->ifi_family != AF_INET6) + return 0; + if (ifi->ifi_index == 0) + return 0; + if (ifindex > 0 && ifi->ifi_index != ifindex) + return 0; + if (ifi->ifi_flags & (IFF_LOOPBACK | IFF_NOARP)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (!tb[IFLA_PROTINFO]) + return -1; + + parse_rtattr_nested(ltb, IFLA_INET6_MAX, tb[IFLA_PROTINFO]); + if (!ltb[IFLA_INET6_TOKEN]) { + fprintf(stderr, "Seems there's no support for IPv6 token!\n"); + return -1; + } + + open_json_object(NULL); + print_string(PRINT_FP, NULL, "token ", NULL); + print_color_string(PRINT_ANY, + ifa_family_color(ifi->ifi_family), + "token", "%s", + format_host_rta(ifi->ifi_family, ltb[IFLA_INET6_TOKEN])); + print_string(PRINT_FP, NULL, " dev ", NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s\n", + ll_index_to_name(ifi->ifi_index)); + close_json_object(); + fflush(fp); + + return 0; +} + +static int iptoken_list(int argc, char **argv) +{ + int af = AF_INET6; + struct rtnl_dump_args da = { .fp = stdout }; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((da.ifindex = ll_name_to_index(*argv)) == 0) + invarg("dev is invalid\n", *argv); + break; + } + argc--; argv++; + } + + if (rtnl_linkdump_req(&rth, af) < 0) { + perror("Cannot send dump request"); + return -1; + } + + new_json_obj(json); + if (rtnl_dump_filter(&rth, print_token, &da) < 0) { + delete_json_obj(); + fprintf(stderr, "Dump terminated\n"); + return -1; + } + delete_json_obj(); + + return 0; +} + +static int iptoken_set(int argc, char **argv, bool delete) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg ifi; + char buf[512]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_SETLINK, + .ifi.ifi_family = AF_INET6, + }; + struct rtattr *afs, *afs6; + bool have_token = delete, have_dev = false; + inet_prefix addr = { .bytelen = 16, }; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (!have_dev) { + if ((req.ifi.ifi_index = + ll_name_to_index(*argv)) == 0) + invarg("dev is invalid\n", *argv); + have_dev = true; + } + } else { + if (matches(*argv, "help") == 0) + usage(); + if (!have_token) { + get_prefix(&addr, *argv, req.ifi.ifi_family); + have_token = true; + } + } + argc--; argv++; + } + + if (!have_token) { + fprintf(stderr, "Not enough information: token is required.\n"); + return -1; + } + if (!have_dev) { + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); + return -1; + } + + afs = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); + afs6 = addattr_nest(&req.n, sizeof(req), AF_INET6); + addattr_l(&req.n, sizeof(req), IFLA_INET6_TOKEN, + &addr.data, addr.bytelen); + addattr_nest_end(&req.n, afs6); + addattr_nest_end(&req.n, afs); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +int do_iptoken(int argc, char **argv) +{ + ll_init_map(&rth); + + if (argc < 1) { + return iptoken_list(0, NULL); + } else if (matches(argv[0], "list") == 0 || + matches(argv[0], "lst") == 0 || + matches(argv[0], "show") == 0) { + return iptoken_list(argc - 1, argv + 1); + } else if (matches(argv[0], "set") == 0 || + matches(argv[0], "add") == 0) { + return iptoken_set(argc - 1, argv + 1, false); + } else if (matches(argv[0], "delete") == 0) { + return iptoken_set(argc - 1, argv + 1, true); + } else if (matches(argv[0], "get") == 0) { + return iptoken_list(argc - 1, argv + 1); + } else if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip token help\".\n", *argv); + exit(-1); +} diff --git a/ip/iptunnel.c b/ip/iptunnel.c new file mode 100644 index 0000000..696f3b9 --- /dev/null +++ b/ip/iptunnel.c @@ -0,0 +1,596 @@ +/* + * iptunnel.c "ip tunnel" + * + * 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. + * + * Authors: Alexey Kuznetsov, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]\n" + " [ mode { ipip | gre | sit | isatap | vti } ] [ remote ADDR ] [ local ADDR ]\n" + " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" + " [ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]\n" + " [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n" + " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n" + "\n" + "Where: NAME := STRING\n" + " ADDR := { IP_ADDRESS | any }\n" + " TOS := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }\n" + " TTL := { 1..255 | inherit }\n" + " KEY := { DOTTED_QUAD | NUMBER }\n"); + exit(-1); +} + +static void set_tunnel_proto(struct ip_tunnel_parm *p, int proto) +{ + if (p->iph.protocol && p->iph.protocol != proto) { + fprintf(stderr, + "You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = proto; +} + +static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) +{ + int count = 0; + const char *medium = NULL; + int isatap = 0; + + memset(p, 0, sizeof(*p)); + p->iph.version = 4; + p->iph.ihl = 5; +#ifndef IP_DF +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#endif + p->iph.frag_off = htons(IP_DF); + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip/ip") == 0) { + set_tunnel_proto(p, IPPROTO_IPIP); + } else if (strcmp(*argv, "gre") == 0 || + strcmp(*argv, "gre/ip") == 0) { + set_tunnel_proto(p, IPPROTO_GRE); + } else if (strcmp(*argv, "sit") == 0 || + strcmp(*argv, "ipv6/ip") == 0) { + set_tunnel_proto(p, IPPROTO_IPV6); + } else if (strcmp(*argv, "isatap") == 0) { + set_tunnel_proto(p, IPPROTO_IPV6); + isatap++; + } else if (strcmp(*argv, "vti") == 0) { + set_tunnel_proto(p, IPPROTO_IPIP); + p->i_flags |= VTI_ISVTI; + } else { + fprintf(stderr, + "Unknown tunnel mode \"%s\"\n", *argv); + exit(-1); + } + } else if (strcmp(*argv, "key") == 0) { + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->o_flags |= GRE_KEY; + p->i_key = p->o_key = tnl_parse_key("key", *argv); + } else if (strcmp(*argv, "ikey") == 0) { + NEXT_ARG(); + p->i_flags |= GRE_KEY; + p->i_key = tnl_parse_key("ikey", *argv); + } else if (strcmp(*argv, "okey") == 0) { + NEXT_ARG(); + p->o_flags |= GRE_KEY; + p->o_key = tnl_parse_key("okey", *argv); + } else if (strcmp(*argv, "seq") == 0) { + p->i_flags |= GRE_SEQ; + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "iseq") == 0) { + p->i_flags |= GRE_SEQ; + } else if (strcmp(*argv, "oseq") == 0) { + p->o_flags |= GRE_SEQ; + } else if (strcmp(*argv, "csum") == 0) { + p->i_flags |= GRE_CSUM; + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "icsum") == 0) { + p->i_flags |= GRE_CSUM; + } else if (strcmp(*argv, "ocsum") == 0) { + p->o_flags |= GRE_CSUM; + } else if (strcmp(*argv, "nopmtudisc") == 0) { + p->iph.frag_off = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + p->iph.frag_off = htons(IP_DF); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + p->iph.daddr = get_addr32(*argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + p->iph.saddr = get_addr32(*argv); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + medium = *argv; + } else if (strcmp(*argv, "ttl") == 0 || + strcmp(*argv, "hoplimit") == 0 || + strcmp(*argv, "hlim") == 0) { + __u8 uval; + + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_u8(&uval, *argv, 0)) + invarg("invalid TTL\n", *argv); + p->iph.ttl = uval; + } + } else if (strcmp(*argv, "tos") == 0 || + strcmp(*argv, "tclass") == 0 || + matches(*argv, "dsfield") == 0) { + char *dsfield; + __u32 uval; + + NEXT_ARG(); + dsfield = *argv; + strsep(&dsfield, "/"); + if (strcmp(*argv, "inherit") != 0) { + dsfield = *argv; + p->iph.tos = 0; + } else + p->iph.tos = 1; + if (dsfield) { + if (rtnl_dsfield_a2n(&uval, dsfield)) + invarg("bad TOS value", *argv); + p->iph.tos |= uval; + } + } else { + if (strcmp(*argv, "name") == 0) + NEXT_ARG(); + else if (matches(*argv, "help") == 0) + usage(); + + if (p->name[0]) + duparg2("name", *argv); + if (get_ifname(p->name, *argv)) + invarg("\"name\" not a valid ifname", *argv); + if (cmd == SIOCCHGTUNNEL && count == 0) { + struct ip_tunnel_parm old_p = {}; + + if (tnl_get_ioctl(*argv, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + + if (p->iph.protocol == 0) { + if (memcmp(p->name, "gre", 3) == 0) + p->iph.protocol = IPPROTO_GRE; + else if (memcmp(p->name, "ipip", 4) == 0) + p->iph.protocol = IPPROTO_IPIP; + else if (memcmp(p->name, "sit", 3) == 0) + p->iph.protocol = IPPROTO_IPV6; + else if (memcmp(p->name, "isatap", 6) == 0) { + p->iph.protocol = IPPROTO_IPV6; + isatap++; + } else if (memcmp(p->name, "vti", 3) == 0) { + p->iph.protocol = IPPROTO_IPIP; + p->i_flags |= VTI_ISVTI; + } + } + + if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { + if (!(p->i_flags & VTI_ISVTI) && + (p->iph.protocol != IPPROTO_GRE)) { + fprintf(stderr, "Keys are not allowed with ipip and sit tunnels\n"); + return -1; + } + } + + if (medium) { + p->link = ll_name_to_index(medium); + if (!p->link) + return nodev(medium); + } + + if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->i_key = p->iph.daddr; + p->i_flags |= GRE_KEY; + } + if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->o_key = p->iph.daddr; + p->o_flags |= GRE_KEY; + } + if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { + fprintf(stderr, "A broadcast tunnel requires a source address\n"); + return -1; + } + if (isatap) + p->i_flags |= SIT_ISATAP; + + return 0; +} + +static const char *tnl_defname(const struct ip_tunnel_parm *p) +{ + switch (p->iph.protocol) { + case IPPROTO_IPIP: + if (p->i_flags & VTI_ISVTI) + return "ip_vti0"; + else + return "tunl0"; + case IPPROTO_GRE: + return "gre0"; + case IPPROTO_IPV6: + return "sit0"; + } + return NULL; +} + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip_tunnel_parm p; + const char *basedev; + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (p.iph.ttl && p.iph.frag_off == 0) { + fprintf(stderr, "ttl != 0 and nopmtudisc are incompatible\n"); + return -1; + } + + basedev = tnl_defname(&p); + if (!basedev) { + fprintf(stderr, + "cannot determine tunnel mode (ipip, gre, vti or sit)\n"); + return -1; + } + + return tnl_add_ioctl(cmd, basedev, p.name, &p); +} + +static int do_del(int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + return tnl_del_ioctl(tnl_defname(&p) ? : p.name, p.name, &p); +} + +static void print_tunnel(const void *t) +{ + const struct ip_tunnel_parm *p = t; + struct ip_tunnel_6rd ip6rd = {}; + SPRINT_BUF(b1); + + /* Do not use format_host() for local addr, + * symbolic name will not be useful. + */ + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name); + snprintf(b1, sizeof(b1), "%s/ip", tnl_strproto(p->iph.protocol)); + print_string(PRINT_ANY, "mode", "%s ", b1); + print_null(PRINT_FP, NULL, "remote ", NULL); + print_color_string(PRINT_ANY, COLOR_INET, "remote", "%s ", + p->iph.daddr || is_json_context() + ? format_host_r(AF_INET, 4, &p->iph.daddr, b1, sizeof(b1)) + : "any"); + print_null(PRINT_FP, NULL, "local ", NULL); + print_color_string(PRINT_ANY, COLOR_INET, "local", "%s", + p->iph.saddr || is_json_context() + ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, b1, sizeof(b1)) + : "any"); + + if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) { + struct ip_tunnel_prl prl[16] = {}; + int i; + + prl[0].datalen = sizeof(prl) - sizeof(prl[0]); + prl[0].addr = htonl(INADDR_ANY); + + if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) { + for (i = 1; i < ARRAY_SIZE(prl); i++) { + if (prl[i].addr == htonl(INADDR_ANY)) + continue; + if (prl[i].flags & PRL_DEFAULT) + print_string(PRINT_ANY, "pdr", + " pdr %s", + format_host(AF_INET, 4, &prl[i].addr)); + else + print_string(PRINT_ANY, "pr", " pr %s", + format_host(AF_INET, 4, &prl[i].addr)); + } + } + } + + if (p->link) { + const char *n = ll_index_to_name(p->link); + + if (n) + print_string(PRINT_ANY, "dev", " dev %s", n); + } + + if (p->iph.ttl) + print_uint(PRINT_ANY, "ttl", " ttl %u", p->iph.ttl); + else + print_string(PRINT_FP, "ttl", " ttl %s", "inherit"); + + if (p->iph.tos) { + SPRINT_BUF(b2); + + if (p->iph.tos != 1) { + if (!is_json_context() && p->iph.tos & 1) + snprintf(b2, sizeof(b2), "%s%s", + p->iph.tos & 1 ? "inherit/" : "", + rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1))); + else + snprintf(b2, sizeof(b2), "%s", + rtnl_dsfield_n2a(p->iph.tos, b1, sizeof(b1))); + print_string(PRINT_ANY, "tos", " tos %s", b2); + } else { + print_string(PRINT_FP, NULL, " tos %s", "inherit"); + } + } + + if (!(p->iph.frag_off & htons(IP_DF))) + print_null(PRINT_ANY, "nopmtudisc", " nopmtudisc", NULL); + + if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) { + print_string(PRINT_ANY, "6rd-prefix", " 6rd-prefix %s", + inet_ntop(AF_INET6, &ip6rd.prefix, b1, sizeof(b1))); + print_uint(PRINT_ANY, "6rd-prefixlen", "/%u", ip6rd.prefixlen); + if (ip6rd.relay_prefix) { + print_string(PRINT_ANY, "6rd-relay_prefix", + " 6rd-relay_prefix %s", + format_host(AF_INET, 4, &ip6rd.relay_prefix)); + print_uint(PRINT_ANY, "6rd-relay_prefixlen", "/%u", + ip6rd.relay_prefixlen); + } + } + + tnl_print_gre_flags(p->iph.protocol, p->i_flags, p->o_flags, + p->i_key, p->o_key); + + close_json_object(); +} + + +static void ip_tunnel_parm_initialize(const struct tnl_print_nlmsg_info *info) +{ + struct ip_tunnel_parm *p2 = info->p2; + + memset(p2, 0, sizeof(*p2)); +} + +static bool ip_tunnel_parm_match(const struct tnl_print_nlmsg_info *info) +{ + const struct ip_tunnel_parm *p1 = info->p1; + const struct ip_tunnel_parm *p2 = info->p2; + + return ((!p1->link || p1->link == p2->link) && + (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && + (!p1->iph.daddr || p1->iph.daddr == p2->iph.daddr) && + (!p1->iph.saddr || p1->iph.saddr == p2->iph.saddr) && + (!p1->i_key || p1->i_key == p2->i_key)); +} + +static int do_show(int argc, char **argv) +{ + struct ip_tunnel_parm p, p1; + const char *basedev; + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + basedev = tnl_defname(&p); + if (!basedev) { + struct tnl_print_nlmsg_info info = { + .p1 = &p, + .p2 = &p1, + .init = ip_tunnel_parm_initialize, + .match = ip_tunnel_parm_match, + .print = print_tunnel, + }; + + return do_tunnels_list(&info); + } + + if (tnl_get_ioctl(p.name[0] ? p.name : basedev, &p)) + return -1; + + print_tunnel(&p); + fputc('\n', stdout); + return 0; +} + +static int do_prl(int argc, char **argv) +{ + struct ip_tunnel_prl p = {}; + int count = 0; + int cmd = 0; + const char *medium = NULL; + + while (argc > 0) { + if (strcmp(*argv, "prl-default") == 0) { + NEXT_ARG(); + cmd = SIOCADDPRL; + p.addr = get_addr32(*argv); + p.flags |= PRL_DEFAULT; + count++; + } else if (strcmp(*argv, "prl-nodefault") == 0) { + NEXT_ARG(); + cmd = SIOCADDPRL; + p.addr = get_addr32(*argv); + count++; + } else if (strcmp(*argv, "prl-delete") == 0) { + NEXT_ARG(); + cmd = SIOCDELPRL; + p.addr = get_addr32(*argv); + count++; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + medium = *argv; + } else { + fprintf(stderr, + "Invalid PRL parameter \"%s\"\n", *argv); + exit(-1); + } + if (count > 1) { + fprintf(stderr, + "One PRL entry at a time\n"); + exit(-1); + } + argc--; argv++; + } + if (!medium) { + fprintf(stderr, "Must specify device\n"); + exit(-1); + } + + return tnl_prl_ioctl(cmd, medium, &p); +} + +static int do_6rd(int argc, char **argv) +{ + struct ip_tunnel_6rd ip6rd = {}; + int cmd = 0; + const char *medium = NULL; + inet_prefix prefix; + + while (argc > 0) { + if (strcmp(*argv, "6rd-prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET6)) + invarg("invalid 6rd_prefix\n", *argv); + cmd = SIOCADD6RD; + memcpy(&ip6rd.prefix, prefix.data, 16); + ip6rd.prefixlen = prefix.bitlen; + } else if (strcmp(*argv, "6rd-relay_prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&prefix, *argv, AF_INET)) + invarg("invalid 6rd-relay_prefix\n", *argv); + cmd = SIOCADD6RD; + memcpy(&ip6rd.relay_prefix, prefix.data, 4); + ip6rd.relay_prefixlen = prefix.bitlen; + } else if (strcmp(*argv, "6rd-reset") == 0) { + cmd = SIOCDEL6RD; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (check_ifname(*argv)) + invarg("\"dev\" not a valid ifname", *argv); + medium = *argv; + } else { + fprintf(stderr, + "Invalid 6RD parameter \"%s\"\n", *argv); + exit(-1); + } + argc--; argv++; + } + if (!medium) { + fprintf(stderr, "Must specify device\n"); + exit(-1); + } + + return tnl_6rd_ioctl(cmd, medium, &ip6rd); +} + +static int tunnel_mode_is_ipv6(char *tunnel_mode) +{ + static const char * const ipv6_modes[] = { + "ipv6/ipv6", "ip6ip6", + "vti6", + "ip/ipv6", "ipv4/ipv6", "ipip6", "ip4ip6", + "ip6gre", "gre/ipv6", + "any/ipv6", "any" + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ipv6_modes); i++) { + if (strcmp(ipv6_modes[i], tunnel_mode) == 0) + return 1; + } + return 0; +} + +int do_iptunnel(int argc, char **argv) +{ + int i; + + for (i = 0; i < argc - 1; i++) { + if (strcmp(argv[i], "mode") == 0) { + if (tunnel_mode_is_ipv6(argv[i + 1])) + preferred_family = AF_INET6; + break; + } + } + switch (preferred_family) { + case AF_UNSPEC: + preferred_family = AF_INET; + break; + case AF_INET: + break; + /* + * This is silly enough but we have no easy way to make it + * protocol-independent because of unarranged structure between + * IPv4 and IPv6. + */ + case AF_INET6: + return do_ip6tunnel(argc, argv); + default: + fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family); + exit(-1); + } + + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); + if (matches(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); + if (matches(*argv, "delete") == 0) + return do_del(argc - 1, argv + 1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc - 1, argv + 1); + if (matches(*argv, "prl") == 0) + return do_prl(argc - 1, argv + 1); + if (matches(*argv, "6rd") == 0) + return do_6rd(argc - 1, argv + 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\"\n", *argv); + exit(-1); +} diff --git a/ip/iptuntap.c b/ip/iptuntap.c new file mode 100644 index 0000000..82e3849 --- /dev/null +++ b/ip/iptuntap.c @@ -0,0 +1,599 @@ +/* + * iptunnel.c "ip tuntap" + * + * 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. + * + * Authors: David Woodhouse + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +static const char drv_name[] = "tun"; + +#define TUNDEV "/dev/net/tun" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ]\n" + " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n" + " [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ] [ name NAME ]\n" + "\n" + "Where: USER := { STRING | NUMBER }\n" + " GROUP := { STRING | NUMBER }\n"); + exit(-1); +} + +static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid) +{ + int fd; + int ret = -1; + +#ifdef IFF_TUN_EXCL + ifr->ifr_flags |= IFF_TUN_EXCL; +#endif + + fd = open(TUNDEV, O_RDWR); + if (fd < 0) { + perror("open"); + return -1; + } + if (ioctl(fd, TUNSETIFF, ifr)) { + perror("ioctl(TUNSETIFF)"); + goto out; + } + if (uid != -1 && ioctl(fd, TUNSETOWNER, uid)) { + perror("ioctl(TUNSETOWNER)"); + goto out; + } + if (gid != -1 && ioctl(fd, TUNSETGROUP, gid)) { + perror("ioctl(TUNSETGROUP)"); + goto out; + } + if (ioctl(fd, TUNSETPERSIST, 1)) { + perror("ioctl(TUNSETPERSIST)"); + goto out; + } + ret = 0; + out: + close(fd); + return ret; +} + +static int tap_del_ioctl(struct ifreq *ifr) +{ + int fd = open(TUNDEV, O_RDWR); + int ret = -1; + + if (fd < 0) { + perror("open"); + return -1; + } + if (ioctl(fd, TUNSETIFF, ifr)) { + perror("ioctl(TUNSETIFF)"); + goto out; + } + if (ioctl(fd, TUNSETPERSIST, 0)) { + perror("ioctl(TUNSETPERSIST)"); + goto out; + } + ret = 0; + out: + close(fd); + return ret; + +} +static int parse_args(int argc, char **argv, + struct ifreq *ifr, uid_t *uid, gid_t *gid) +{ + int count = 0; + + memset(ifr, 0, sizeof(*ifr)); + + ifr->ifr_flags |= IFF_NO_PI; + + while (argc > 0) { + if (matches(*argv, "mode") == 0) { + NEXT_ARG(); + if (matches(*argv, "tun") == 0) { + if (ifr->ifr_flags & IFF_TAP) { + fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + ifr->ifr_flags |= IFF_TUN; + } else if (matches(*argv, "tap") == 0) { + if (ifr->ifr_flags & IFF_TUN) { + fprintf(stderr, "You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + ifr->ifr_flags |= IFF_TAP; + } else { + fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv); + exit(-1); + } + } else if (uid && matches(*argv, "user") == 0) { + char *end; + unsigned long user; + + NEXT_ARG(); + if (**argv && ((user = strtol(*argv, &end, 10)), !*end)) + *uid = user; + else { + struct passwd *pw = getpwnam(*argv); + + if (!pw) { + fprintf(stderr, "invalid user \"%s\"\n", *argv); + exit(-1); + } + *uid = pw->pw_uid; + } + } else if (gid && matches(*argv, "group") == 0) { + char *end; + unsigned long group; + + NEXT_ARG(); + + if (**argv && ((group = strtol(*argv, &end, 10)), !*end)) + *gid = group; + else { + struct group *gr = getgrnam(*argv); + + if (!gr) { + fprintf(stderr, "invalid group \"%s\"\n", *argv); + exit(-1); + } + *gid = gr->gr_gid; + } + } else if (matches(*argv, "pi") == 0) { + ifr->ifr_flags &= ~IFF_NO_PI; + } else if (matches(*argv, "one_queue") == 0) { + ifr->ifr_flags |= IFF_ONE_QUEUE; + } else if (matches(*argv, "vnet_hdr") == 0) { + ifr->ifr_flags |= IFF_VNET_HDR; + } else if (matches(*argv, "multi_queue") == 0) { + ifr->ifr_flags |= IFF_MULTI_QUEUE; + } else if (matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (get_ifname(ifr->ifr_name, *argv)) + invarg("\"dev\" not a valid ifname", *argv); + } else { + if (matches(*argv, "name") == 0) { + NEXT_ARG(); + } else if (matches(*argv, "help") == 0) + usage(); + if (ifr->ifr_name[0]) + duparg2("name", *argv); + if (get_ifname(ifr->ifr_name, *argv)) + invarg("\"name\" not a valid ifname", *argv); + } + count++; + argc--; argv++; + } + + if (!(ifr->ifr_flags & TUN_TYPE_MASK)) { + fprintf(stderr, "You failed to specify a tunnel mode\n"); + return -1; + } + + return 0; +} + + +static int do_add(int argc, char **argv) +{ + struct ifreq ifr; + uid_t uid = -1; + gid_t gid = -1; + + if (parse_args(argc, argv, &ifr, &uid, &gid) < 0) + return -1; + + return tap_add_ioctl(&ifr, uid, gid); +} + +static int do_del(int argc, char **argv) +{ + struct ifreq ifr; + + if (parse_args(argc, argv, &ifr, NULL, NULL) < 0) + return -1; + + return tap_del_ioctl(&ifr); +} + +static void print_flags(long flags) +{ + open_json_array(PRINT_JSON, "flags"); + + if (flags & IFF_TUN) + print_string(PRINT_ANY, NULL, " %s", "tun"); + + if (flags & IFF_TAP) + print_string(PRINT_ANY, NULL, " %s", "tap"); + + if (!(flags & IFF_NO_PI)) + print_string(PRINT_ANY, NULL, " %s", "pi"); + + if (flags & IFF_ONE_QUEUE) + print_string(PRINT_ANY, NULL, " %s", "one_queue"); + + if (flags & IFF_VNET_HDR) + print_string(PRINT_ANY, NULL, " %s", "vnet_hdr"); + + if (flags & IFF_PERSIST) + print_string(PRINT_ANY, NULL, " %s", "persist"); + + if (!(flags & IFF_NOFILTER)) + print_string(PRINT_ANY, NULL, " %s", "filter"); + + flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | + IFF_VNET_HDR | IFF_PERSIST | IFF_NOFILTER); + if (flags) + print_0xhex(PRINT_ANY, NULL, "%#llx", flags); + + close_json_array(PRINT_JSON, NULL); +} + +static char *pid_name(pid_t pid) +{ + char *comm; + FILE *f; + int err; + + err = asprintf(&comm, "/proc/%d/comm", pid); + if (err < 0) + return NULL; + + f = fopen(comm, "r"); + free(comm); + if (!f) { + perror("fopen"); + return NULL; + } + + if (fscanf(f, "%ms\n", &comm) != 1) { + perror("fscanf"); + comm = NULL; + } + + + if (fclose(f)) + perror("fclose"); + + return comm; +} + +static void show_processes(const char *name) +{ + glob_t globbuf = { }; + char **fd_path; + int err; + + err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT, + NULL, &globbuf); + if (err) + return; + + open_json_array(PRINT_JSON, "processes"); + + fd_path = globbuf.gl_pathv; + while (*fd_path) { + const char *dev_net_tun = "/dev/net/tun"; + const size_t linkbuf_len = strlen(dev_net_tun) + 2; + char linkbuf[linkbuf_len], *fdinfo; + int pid, fd; + FILE *f; + + if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2) + goto next; + + if (pid == getpid()) + goto next; + + err = readlink(*fd_path, linkbuf, linkbuf_len - 1); + if (err < 0) { + perror("readlink"); + goto next; + } + linkbuf[err] = '\0'; + if (strcmp(dev_net_tun, linkbuf)) + goto next; + + if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0) + goto next; + + f = fopen(fdinfo, "r"); + free(fdinfo); + if (!f) { + perror("fopen"); + goto next; + } + + while (!feof(f)) { + char *key = NULL, *value = NULL; + + err = fscanf(f, "%m[^:]: %ms\n", &key, &value); + if (err == EOF) { + if (ferror(f)) + perror("fscanf"); + break; + } else if (err == 2 && + !strcmp("iff", key) && + !strcmp(name, value)) { + char *pname = pid_name(pid); + + print_string(PRINT_ANY, "name", + "%s", pname ? : ""); + + print_uint(PRINT_ANY, "pid", + "(%d)", pid); + free(pname); + } + + free(key); + free(value); + } + if (fclose(f)) + perror("fclose"); + +next: + ++fd_path; + } + close_json_array(PRINT_JSON, NULL); + + globfree(&globbuf); +} + +static int tuntap_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + struct rtattr *linkinfo; + int err; + + linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO); + + err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, + drv_name, sizeof(drv_name) - 1); + if (err) + return err; + + addattr_nest_end(nlh, linkinfo); + + return 0; +} + +static int print_tuntap(struct nlmsghdr *n, void *arg) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + const char *name, *kind; + long flags, owner = -1, group = -1; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) + return -1; + + switch (ifi->ifi_type) { + case ARPHRD_NONE: + case ARPHRD_ETHER: + break; + default: + return 0; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + + if (!tb[IFLA_IFNAME]) + return 0; + + if (!tb[IFLA_LINKINFO]) + return 0; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_KIND]) + return 0; + + kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]); + if (strcmp(kind, drv_name)) + return 0; + + name = rta_getattr_str(tb[IFLA_IFNAME]); + + if (read_prop(name, "tun_flags", &flags)) + return 0; + if (read_prop(name, "owner", &owner)) + return 0; + if (read_prop(name, "group", &group)) + return 0; + + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, + "ifname", "%s:", name); + print_flags(flags); + if (owner != -1) + print_u64(PRINT_ANY, "user", + " user %ld", owner); + if (group != -1) + print_u64(PRINT_ANY, "group", + " group %ld", group); + + if (show_details) { + print_string(PRINT_FP, NULL, + "%s\tAttached to processes:", _SL_); + show_processes(name); + } + close_json_object(); + print_string(PRINT_FP, NULL, "%s", "\n"); + + return 0; +} + +static int do_show(int argc, char **argv) +{ + if (rtnl_linkdump_req_filter_fn(&rth, AF_UNSPEC, + tuntap_filter_req) < 0) { + perror("Cannot send dump request\n"); + return -1; + } + + new_json_obj(json); + + if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + + delete_json_obj(); + fflush(stdout); + + return 0; +} + +int do_iptuntap(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tuntap help\".\n", + *argv); + exit(-1); +} + +static void print_owner(FILE *f, uid_t uid) +{ + struct passwd *pw = getpwuid(uid); + + if (pw) + print_string(PRINT_ANY, "user", "user %s ", pw->pw_name); + else + print_uint(PRINT_ANY, "user", "user %u ", uid); +} + +static void print_group(FILE *f, gid_t gid) +{ + struct group *group = getgrgid(gid); + + if (group) + print_string(PRINT_ANY, "group", "group %s ", group->gr_name); + else + print_uint(PRINT_ANY, "group", "group %u ", gid); +} + +static void print_mq(FILE *f, struct rtattr *tb[]) +{ + if (!tb[IFLA_TUN_MULTI_QUEUE] || + !rta_getattr_u8(tb[IFLA_TUN_MULTI_QUEUE])) { + if (is_json_context()) + print_bool(PRINT_JSON, "multi_queue", NULL, false); + return; + } + + print_bool(PRINT_ANY, "multi_queue", "multi_queue ", true); + + if (tb[IFLA_TUN_NUM_QUEUES]) { + print_uint(PRINT_ANY, "numqueues", "numqueues %u ", + rta_getattr_u32(tb[IFLA_TUN_NUM_QUEUES])); + } + + if (tb[IFLA_TUN_NUM_DISABLED_QUEUES]) { + print_uint(PRINT_ANY, "numdisabled", "numdisabled %u ", + rta_getattr_u32(tb[IFLA_TUN_NUM_DISABLED_QUEUES])); + } +} + +static void print_onoff(FILE *f, const char *flag, __u8 val) +{ + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, !!val); + else + fprintf(f, "%s %s ", flag, val ? "on" : "off"); +} + +static void print_type(FILE *f, __u8 type) +{ + SPRINT_BUF(buf); + const char *str = buf; + + if (type == IFF_TUN) + str = "tun"; + else if (type == IFF_TAP) + str = "tap"; + else + snprintf(buf, sizeof(buf), "UNKNOWN:%hhu", type); + + print_string(PRINT_ANY, "type", "type %s ", str); +} + +static void tun_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + if (!tb) + return; + + if (tb[IFLA_TUN_TYPE]) + print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE])); + + if (tb[IFLA_TUN_PI]) + print_onoff(f, "pi", rta_getattr_u8(tb[IFLA_TUN_PI])); + + if (tb[IFLA_TUN_VNET_HDR]) { + print_onoff(f, "vnet_hdr", + rta_getattr_u8(tb[IFLA_TUN_VNET_HDR])); + } + + print_mq(f, tb); + + if (tb[IFLA_TUN_PERSIST]) + print_onoff(f, "persist", rta_getattr_u8(tb[IFLA_TUN_PERSIST])); + + if (tb[IFLA_TUN_OWNER]) + print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER])); + + if (tb[IFLA_TUN_GROUP]) + print_group(f, rta_getattr_u32(tb[IFLA_TUN_GROUP])); +} + +struct link_util tun_link_util = { + .id = "tun", + .maxattr = IFLA_TUN_MAX, + .print_opt = tun_print_opt, +}; diff --git a/ip/ipvrf.c b/ip/ipvrf.c new file mode 100644 index 0000000..28dd8e2 --- /dev/null +++ b/ip/ipvrf.c @@ -0,0 +1,652 @@ +/* + * ipvrf.c "ip vrf" + * + * 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. + * + * Authors: David Ahern + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "bpf_util.h" + +#define CGRP_PROC_FILE "/cgroup.procs" + +static struct link_filter vrf_filter; + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip vrf show [NAME] ...\n" + " ip vrf exec [NAME] cmd ...\n" + " ip vrf identify [PID]\n" + " ip vrf pids [NAME]\n"); + + exit(-1); +} + +/* + * parse process based cgroup file looking for PATH/vrf/NAME where + * NAME is the name of the vrf the process is associated with + */ +static int vrf_identify(pid_t pid, char *name, size_t len) +{ + char path[PATH_MAX]; + char buf[4096]; + char *vrf, *end; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/cgroup", pid); + fp = fopen(path, "r"); + if (!fp) + return -1; + + memset(name, 0, len); + + while (fgets(buf, sizeof(buf), fp)) { + /* want the controller-less cgroup */ + if (strstr(buf, "::/") == NULL) + continue; + + vrf = strstr(buf, "/vrf/"); + if (vrf) { + vrf += 5; /* skip past "/vrf/" */ + end = strchr(vrf, '\n'); + if (end) + *end = '\0'; + + strlcpy(name, vrf, len); + break; + } + } + + fclose(fp); + + return 0; +} + +static int ipvrf_identify(int argc, char **argv) +{ + char vrf[32]; + int rc; + unsigned int pid; + + if (argc < 1) + pid = getpid(); + else if (argc > 1) + invarg("Extra arguments specified\n", argv[1]); + else if (get_unsigned(&pid, argv[0], 10)) + invarg("Invalid pid\n", argv[0]); + + rc = vrf_identify(pid, vrf, sizeof(vrf)); + if (!rc) { + if (vrf[0] != '\0') + printf("%s\n", vrf); + } else { + fprintf(stderr, "Failed to lookup vrf association: %s\n", + strerror(errno)); + } + + return rc; +} + +/* read PATH/vrf/NAME/cgroup.procs file */ +static void read_cgroup_pids(const char *base_path, char *name) +{ + char path[PATH_MAX]; + char buf[4096]; + FILE *fp; + + if (snprintf(path, sizeof(path), "%s/vrf/%s%s", + base_path, name, CGRP_PROC_FILE) >= sizeof(path)) + return; + + fp = fopen(path, "r"); + if (!fp) + return; /* no cgroup file, nothing to show */ + + /* dump contents (pids) of cgroup.procs */ + while (fgets(buf, sizeof(buf), fp)) { + char *nl, comm[32]; + + nl = strchr(buf, '\n'); + if (nl) + *nl = '\0'; + + if (get_command_name(buf, comm, sizeof(comm))) + strcpy(comm, ""); + + printf("%5s %s\n", buf, comm); + } + + fclose(fp); +} + +/* recurse path looking for PATH[/NETNS]/vrf/NAME */ +static int recurse_dir(char *base_path, char *name, const char *netns) +{ + char path[PATH_MAX]; + struct dirent *de; + struct stat fstat; + int rc; + DIR *d; + + d = opendir(base_path); + if (!d) + return -1; + + while ((de = readdir(d)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (!strcmp(de->d_name, "vrf")) { + const char *pdir = strrchr(base_path, '/'); + + /* found a 'vrf' directory. if it is for the given + * namespace then dump the cgroup pids + */ + if (*netns == '\0' || + (pdir && !strcmp(pdir+1, netns))) + read_cgroup_pids(base_path, name); + + continue; + } + + /* is this a subdir that needs to be walked */ + if (snprintf(path, sizeof(path), "%s/%s", + base_path, de->d_name) >= sizeof(path)) + continue; + + if (lstat(path, &fstat) < 0) + continue; + + if (S_ISDIR(fstat.st_mode)) { + rc = recurse_dir(path, name, netns); + if (rc != 0) + goto out; + } + } + + rc = 0; +out: + closedir(d); + + return rc; +} + +static int ipvrf_get_netns(char *netns, int len) +{ + if (netns_identify_pid("self", netns, len-3)) { + fprintf(stderr, "Failed to get name of network namespace: %s\n", + strerror(errno)); + return -1; + } + + if (*netns != '\0') + strcat(netns, "-ns"); + + return 0; +} + +static int ipvrf_pids(int argc, char **argv) +{ + char *mnt, *vrf; + char netns[256]; + int ret = -1; + + if (argc != 1) { + fprintf(stderr, "Invalid arguments\n"); + return -1; + } + + vrf = argv[0]; + if (!name_is_vrf(vrf)) { + fprintf(stderr, "Invalid VRF name\n"); + return -1; + } + + mnt = find_cgroup2_mount(true); + if (!mnt) + return -1; + + if (ipvrf_get_netns(netns, sizeof(netns)) < 0) + goto out; + + ret = recurse_dir(mnt, vrf, netns); + +out: + free(mnt); + + return ret; +} + +/* load BPF program to set sk_bound_dev_if for sockets */ +static char bpf_log_buf[256*1024]; + +static int prog_load(int idx) +{ + struct bpf_insn prog[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_3, idx), + BPF_MOV64_IMM(BPF_REG_2, + offsetof(struct bpf_sock, bound_dev_if)), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, + offsetof(struct bpf_sock, bound_dev_if)), + BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ + BPF_EXIT_INSN(), + }; + + return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog), + "GPL", bpf_log_buf, sizeof(bpf_log_buf)); +} + +static int vrf_configure_cgroup(const char *path, int ifindex) +{ + int rc = -1, cg_fd, prog_fd = -1; + + cg_fd = open(path, O_DIRECTORY | O_RDONLY); + if (cg_fd < 0) { + fprintf(stderr, + "Failed to open cgroup path: '%s'\n", + strerror(errno)); + goto out; + } + + /* + * Load bpf program into kernel and attach to cgroup to affect + * socket creates + */ + prog_fd = prog_load(ifindex); + if (prog_fd < 0) { + fprintf(stderr, "Failed to load BPF prog: '%s'\n", + strerror(errno)); + + if (errno != EPERM) { + fprintf(stderr, + "Kernel compiled with CGROUP_BPF enabled?\n"); + } + goto out; + } + + if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) { + fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n", + strerror(errno)); + goto out; + } + + rc = 0; +out: + close(cg_fd); + close(prog_fd); + + return rc; +} + +/* get base path for controller-less cgroup for a process. + * path returned does not include /vrf/NAME if it exists + */ +static int vrf_path(char *vpath, size_t len) +{ + char path[PATH_MAX]; + char buf[4096]; + char *vrf; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/cgroup", getpid()); + fp = fopen(path, "r"); + if (!fp) + return -1; + + vpath[0] = '\0'; + + while (fgets(buf, sizeof(buf), fp)) { + char *start, *nl; + + start = strstr(buf, "::/"); + if (!start) + continue; + + /* advance past '::' */ + start += 2; + + nl = strchr(start, '\n'); + if (nl) + *nl = '\0'; + + vrf = strstr(start, "/vrf"); + if (vrf) + *vrf = '\0'; + + strlcpy(vpath, start, len); + + /* if vrf path is just / then return nothing */ + if (!strcmp(vpath, "/")) + vpath[0] = '\0'; + + break; + } + + fclose(fp); + + return 0; +} + +static int vrf_switch(const char *name) +{ + char path[PATH_MAX], *mnt, pid[16]; + char vpath[PATH_MAX], netns[256]; + int ifindex = 0; + int rc = -1, len, fd = -1; + + if (strcmp(name, "default")) { + ifindex = name_is_vrf(name); + if (!ifindex) { + fprintf(stderr, "Invalid VRF name\n"); + return -1; + } + } + + mnt = find_cgroup2_mount(true); + if (!mnt) + return -1; + + /* -1 on length to add '/' to the end */ + if (ipvrf_get_netns(netns, sizeof(netns) - 1) < 0) + goto out; + + if (vrf_path(vpath, sizeof(vpath)) < 0) { + fprintf(stderr, "Failed to get base cgroup path: %s\n", + strerror(errno)); + goto out; + } + + /* if path already ends in netns then don't add it again */ + if (*netns != '\0') { + char *pdir = strrchr(vpath, '/'); + + if (!pdir) + pdir = vpath; + else + pdir++; + + if (strcmp(pdir, netns) == 0) + *pdir = '\0'; + + strcat(netns, "/"); + } + + /* path to cgroup; make sure buffer has room to cat "/cgroup.procs" + * to the end of the path + */ + len = snprintf(path, sizeof(path) - sizeof(CGRP_PROC_FILE), + "%s%s/%svrf/%s", + mnt, vpath, netns, ifindex ? name : ""); + if (len > sizeof(path) - sizeof(CGRP_PROC_FILE)) { + fprintf(stderr, "Invalid path to cgroup2 mount\n"); + goto out; + } + + if (make_path(path, 0755)) { + fprintf(stderr, "Failed to setup vrf cgroup2 directory\n"); + goto out; + } + + if (ifindex && vrf_configure_cgroup(path, ifindex)) + goto out; + + /* + * write pid to cgroup.procs making process part of cgroup + */ + strcat(path, CGRP_PROC_FILE); + fd = open(path, O_RDWR | O_APPEND); + if (fd < 0) { + fprintf(stderr, "Failed to open cgroups.procs file: %s.\n", + strerror(errno)); + goto out; + } + + snprintf(pid, sizeof(pid), "%d", getpid()); + if (write(fd, pid, strlen(pid)) < 0) { + fprintf(stderr, "Failed to join cgroup\n"); + goto out2; + } + + rc = 0; +out2: + close(fd); +out: + free(mnt); + + drop_cap(); + + return rc; +} + +static int do_switch(void *arg) +{ + char *vrf = arg; + + return vrf_switch(vrf); +} + +static int ipvrf_exec(int argc, char **argv) +{ + if (argc < 1) { + fprintf(stderr, "No VRF name specified\n"); + return -1; + } + if (argc < 2) { + fprintf(stderr, "No command specified\n"); + return -1; + } + + return -cmd_exec(argv[1], argv + 1, !!batch_mode, do_switch, argv[0]); +} + +/* reset VRF association of current process to default VRF; + * used by netns_exec + */ +void vrf_reset(void) +{ + char vrf[32]; + + if (vrf_identify(getpid(), vrf, sizeof(vrf)) || + (vrf[0] == '\0')) + return; + + vrf_switch("default"); +} + +static int ipvrf_filter_req(struct nlmsghdr *nlh, int reqlen) +{ + struct rtattr *linkinfo; + int err; + + if (vrf_filter.kind) { + linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO); + + err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, vrf_filter.kind, + strlen(vrf_filter.kind)); + if (err) + return err; + + addattr_nest_end(nlh, linkinfo); + } + + return 0; +} + +/* input arg is linkinfo */ +static __u32 vrf_table_linkinfo(struct rtattr *li[]) +{ + struct rtattr *attr[IFLA_VRF_MAX + 1]; + + if (li[IFLA_INFO_DATA]) { + parse_rtattr_nested(attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]); + + if (attr[IFLA_VRF_TABLE]) + return rta_getattr_u32(attr[IFLA_VRF_TABLE]); + } + + return 0; +} + +static int ipvrf_print(struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + int len = n->nlmsg_len; + const char *name; + __u32 tb_id; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return 0; + + if (vrf_filter.ifindex && vrf_filter.ifindex != ifi->ifi_index) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + /* kernel does not support filter by master device */ + if (tb[IFLA_MASTER]) { + int master = *(int *)RTA_DATA(tb[IFLA_MASTER]); + + if (vrf_filter.master && master != vrf_filter.master) + return 0; + } + + if (!tb[IFLA_IFNAME]) { + fprintf(stderr, + "BUG: device with ifindex %d has nil ifname\n", + ifi->ifi_index); + return 0; + } + name = rta_getattr_str(tb[IFLA_IFNAME]); + + /* missing LINKINFO means not VRF. e.g., kernel does not + * support filtering on kind, so userspace needs to handle + */ + if (!tb[IFLA_LINKINFO]) + return 0; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND]) + return 0; + + if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) + return 0; + + tb_id = vrf_table_linkinfo(li); + if (!tb_id) { + fprintf(stderr, + "BUG: VRF %s is missing table id\n", name); + return 0; + } + + open_json_object(NULL); + print_string(PRINT_ANY, "name", "%-16s", name); + print_uint(PRINT_ANY, "table", " %5u", tb_id); + print_string(PRINT_FP, NULL, "%s", "\n"); + close_json_object(); + + return 1; +} + +static int ipvrf_show(int argc, char **argv) +{ + struct nlmsg_chain linfo = { NULL, NULL}; + int rc = 0; + + vrf_filter.kind = "vrf"; + + if (argc > 1) + usage(); + + if (argc == 1) { + __u32 tb_id; + + tb_id = ipvrf_get_table(argv[0]); + if (!tb_id) { + fprintf(stderr, "Invalid VRF\n"); + return 1; + } + printf("%s %u\n", argv[0], tb_id); + return 0; + } + + if (ip_link_list(ipvrf_filter_req, &linfo) == 0) { + struct nlmsg_list *l; + unsigned nvrf = 0; + + new_json_obj(json); + + print_string(PRINT_FP, NULL, "%-16s", "Name"); + print_string(PRINT_FP, NULL, " %5s\n", "Table"); + print_string(PRINT_FP, NULL, "%s\n", + "-----------------------"); + + for (l = linfo.head; l; l = l->next) + nvrf += ipvrf_print(&l->h); + + if (!nvrf) + print_string(PRINT_FP, NULL, "%s\n", + "No VRF has been configured"); + delete_json_obj(); + } else + rc = 1; + + free_nlmsg_chain(&linfo); + + return rc; +} + +int do_ipvrf(int argc, char **argv) +{ + if (argc == 0) + return ipvrf_show(0, NULL); + + if (matches(*argv, "identify") == 0) + return ipvrf_identify(argc-1, argv+1); + + if (matches(*argv, "pids") == 0) + return ipvrf_pids(argc-1, argv+1); + + if (matches(*argv, "exec") == 0) + return ipvrf_exec(argc-1, argv+1); + + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipvrf_show(argc-1, argv+1); + + if (matches(*argv, "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip vrf help\".\n", + *argv); + + exit(-1); +} diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c new file mode 100644 index 0000000..cac8ba2 --- /dev/null +++ b/ip/ipxfrm.c @@ -0,0 +1,1561 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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, see . + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +#define STRBUF_SIZE (128) + +struct xfrm_filter filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm XFRM-OBJECT { COMMAND | help }\n" + "where XFRM-OBJECT := state | policy | monitor\n"); + exit(-1); +} + +/* This is based on utils.c(inet_addr_match) */ +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits) +{ + __u32 *a1 = (__u32 *)x1; + __u32 *a2 = (__u32 *)x2; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +int xfrm_xfrmproto_is_ipsec(__u8 proto) +{ + return (proto == IPPROTO_ESP || + proto == IPPROTO_AH || + proto == IPPROTO_COMP); +} + +int xfrm_xfrmproto_is_ro(__u8 proto) +{ + return (proto == IPPROTO_ROUTING || + proto == IPPROTO_DSTOPTS); +} + +struct typeent { + const char *t_name; + int t_type; +}; + +static const struct typeent xfrmproto_types[] = { + { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP }, + { "route2", IPPROTO_ROUTING }, { "hao", IPPROTO_DSTOPTS }, + { "ipsec-any", IPSEC_PROTO_ANY }, + { NULL, -1 } +}; + +int xfrm_xfrmproto_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_xfrmproto(__u8 proto) +{ + static char str[16]; + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == proto) + return t->t_name; + } + + sprintf(str, "%u", proto); + return str; +} + +static const struct typeent algo_types[] = { + { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, + { "comp", XFRMA_ALG_COMP }, { "aead", XFRMA_ALG_AEAD }, + { "auth-trunc", XFRMA_ALG_AUTH_TRUNC }, + { NULL, -1 } +}; + +int xfrm_algotype_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_algotype(int type) +{ + static char str[32]; + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == type) + return t->t_name; + } + + sprintf(str, "%d", type); + return str; +} + +static const char *strxf_mask8(__u8 mask) +{ + static char str[16]; + const int sn = sizeof(mask) * 8 - 1; + __u8 b; + int i = 0; + + for (b = (1 << sn); b > 0; b >>= 1) + str[i++] = ((b & mask) ? '1' : '0'); + str[i] = '\0'; + + return str; +} + +const char *strxf_mask32(__u32 mask) +{ + static char str[16]; + + sprintf(str, "%.8x", mask); + + return str; +} + +static const char *strxf_share(__u8 share) +{ + static char str[32]; + + switch (share) { + case XFRM_SHARE_ANY: + strcpy(str, "any"); + break; + case XFRM_SHARE_SESSION: + strcpy(str, "session"); + break; + case XFRM_SHARE_USER: + strcpy(str, "user"); + break; + case XFRM_SHARE_UNIQUE: + strcpy(str, "unique"); + break; + default: + sprintf(str, "%u", share); + break; + } + + return str; +} + +const char *strxf_proto(__u8 proto) +{ + static char buf[32]; + struct protoent *pp; + const char *p; + + pp = getprotobynumber(proto); + if (pp) + p = pp->p_name; + else { + sprintf(buf, "%u", proto); + p = buf; + } + + return p; +} + +const char *strxf_ptype(__u8 ptype) +{ + static char str[16]; + + switch (ptype) { + case XFRM_POLICY_TYPE_MAIN: + strcpy(str, "main"); + break; + case XFRM_POLICY_TYPE_SUB: + strcpy(str, "sub"); + break; + default: + sprintf(str, "%u", ptype); + break; + } + + return str; +} + +static void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, int force_spi, + FILE *fp, const char *prefix, const char *title) +{ + if (title) + fputs(title, fp); + + fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), saddr)); + fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), &id->daddr)); + fprintf(fp, "%s", _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "\t"); + + fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto)); + + if (show_stats > 0 || force_spi || id->spi) { + __u32 spi = ntohl(id->spi); + + fprintf(fp, "spi 0x%08x", spi); + if (show_stats > 0) + fprintf(fp, "(%u)", spi); + fprintf(fp, " "); + } + + fprintf(fp, "reqid %u", reqid); + if (show_stats > 0) + fprintf(fp, "(0x%08x)", reqid); + fprintf(fp, " "); + + fprintf(fp, "mode "); + switch (mode) { + case XFRM_MODE_TRANSPORT: + fprintf(fp, "transport"); + break; + case XFRM_MODE_TUNNEL: + fprintf(fp, "tunnel"); + break; + case XFRM_MODE_ROUTEOPTIMIZATION: + fprintf(fp, "ro"); + break; + case XFRM_MODE_IN_TRIGGER: + fprintf(fp, "in_trigger"); + break; + case XFRM_MODE_BEET: + fprintf(fp, "beet"); + break; + default: + fprintf(fp, "%u", mode); + break; + } + fprintf(fp, "%s", _SL_); +} + +static const char *strxf_limit(__u64 limit) +{ + static char str[32]; + + if (limit == XFRM_INF) + strcpy(str, "(INF)"); + else + sprintf(str, "%llu", (unsigned long long) limit); + + return str; +} + +static void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, + const char *prefix) +{ + if (prefix) + fputs(prefix, fp); + fprintf(fp, "stats:%s", _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " replay-window %u replay %u failed %u%s", + s->replay_window, s->replay, s->integrity_failed, _SL_); +} + +static const char *strxf_time(__u64 time) +{ + static char str[32]; + + if (time == 0) + strcpy(str, "-"); + else { + time_t t; + struct tm *tp; + + /* XXX: treat time in the same manner of kernel's + * net/xfrm/xfrm_{user,state}.c + */ + t = (long)time; + tp = localtime(&t); + + strftime(str, sizeof(str), "%Y-%m-%d %T", tp); + } + + return str; +} + +static void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix) +{ + if (cfg) { + if (prefix) + fputs(prefix, fp); + fprintf(fp, "lifetime config:%s", _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " limit: soft %s(bytes),", + strxf_limit(cfg->soft_byte_limit)); + fprintf(fp, " hard %s(bytes)%s", + strxf_limit(cfg->hard_byte_limit), _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " limit: soft %s(packets),", + strxf_limit(cfg->soft_packet_limit)); + fprintf(fp, " hard %s(packets)%s", + strxf_limit(cfg->hard_packet_limit), _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " expire add: soft %llu(sec), hard %llu(sec)%s", + (unsigned long long) cfg->soft_add_expires_seconds, + (unsigned long long) cfg->hard_add_expires_seconds, + _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " expire use: soft %llu(sec), hard %llu(sec)%s", + (unsigned long long) cfg->soft_use_expires_seconds, + (unsigned long long) cfg->hard_use_expires_seconds, + _SL_); + } + if (cur) { + if (prefix) + fputs(prefix, fp); + fprintf(fp, "lifetime current:%s", _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " %llu(bytes), %llu(packets)%s", + (unsigned long long) cur->bytes, + (unsigned long long) cur->packets, + _SL_); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, " add %s ", strxf_time(cur->add_time)); + fprintf(fp, "use %s%s", strxf_time(cur->use_time), _SL_); + } +} + +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix) +{ + __u16 f; + + f = sel->family; + if (f == AF_UNSPEC) + f = family; + if (f == AF_UNSPEC) + f = preferred_family; + + if (prefix) + fputs(prefix, fp); + + fprintf(fp, "src %s/%u ", + rt_addr_n2a(f, sizeof(sel->saddr), &sel->saddr), + sel->prefixlen_s); + + fprintf(fp, "dst %s/%u ", + rt_addr_n2a(f, sizeof(sel->daddr), &sel->daddr), + sel->prefixlen_d); + + if (sel->proto) + fprintf(fp, "proto %s ", strxf_proto(sel->proto)); + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + default: /* XXX */ + if (sel->sport_mask) + fprintf(fp, "sport %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "dport %u ", ntohs(sel->dport)); + break; + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + /* type/code is stored at sport/dport in selector */ + if (sel->sport_mask) + fprintf(fp, "type %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "code %u ", ntohs(sel->dport)); + break; + case IPPROTO_GRE: + if (sel->sport_mask || sel->dport_mask) + fprintf(fp, "key %u ", + (((__u32)ntohs(sel->sport)) << 16) + + ntohs(sel->dport)); + break; + case IPPROTO_MH: + if (sel->sport_mask) + fprintf(fp, "type %u ", ntohs(sel->sport)); + if (sel->dport_mask) { + if (show_stats > 0) + fprintf(fp, "(dport) 0x%.4x ", sel->dport); + } + break; + } + + if (sel->ifindex > 0) + fprintf(fp, "dev %s ", ll_index_to_name(sel->ifindex)); + + if (show_stats > 0) + fprintf(fp, "uid %u", sel->user); + + fprintf(fp, "%s", _SL_); +} + +static void __xfrm_algo_print(struct xfrm_algo *algo, int type, int len, + FILE *fp, const char *prefix, int newline, + bool nokeys) +{ + int keylen; + int i; + + if (prefix) + fputs(prefix, fp); + + fprintf(fp, "%s ", strxf_algotype(type)); + + if (len < sizeof(*algo)) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + len -= sizeof(*algo); + + fprintf(fp, "%s ", algo->alg_name); + + keylen = algo->alg_key_len / 8; + if (len < keylen) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + + if (nokeys) + fprintf(fp, "</dev/null +.fi + +\&...will yield the following effects over a period of ~10 seconds (taken from +/proc/interrupts): + +.nf +319: 42124229 0 HPET_MSI\-edge hpet2 (before) +319: 42436214 0 HPET_MSI\-edge hpet2 (after 10s.) +.fi + +That's roughly 31000/s. Now compare it with HZ=1000 setting. The obvious +drawback of it is that cpu load can be rather high with servicing that +many timer interrupts. The example with 300Mbit RT service curve on 1Gbit link is +particularly ugly, as it requires a lot of throttling with minuscule delays. + +Also note that it's just an example showing the capabilities of current hardware. +The above example (essentially a 300Mbit TBF emulator) is pointless on an internal +interface to begin with: you will pretty much always want a regular LS service +curve there, and in such a scenario HFSC simply doesn't throttle at all. + +300Mbit RT service curve (selected columns from mpstat \-P ALL 1): + +.nf +10:56:43 PM CPU %sys %irq %soft %idle +10:56:44 PM all 20.10 6.53 34.67 37.19 +10:56:44 PM 0 35.00 0.00 63.00 0.00 +10:56:44 PM 1 4.95 12.87 6.93 73.27 +.fi + +So, in the rare case you need those speeds with only a RT service curve, or with a UL +service curve: remember the drawbacks. +. +.SH "CAVEAT: RANDOM ONLINE EXAMPLES" +. +For reasons unknown (though well guessed), many examples you can google love to +overuse UL criterion and stuff it in every node possible. This makes no sense +and works against what HFSC tries to do (and does pretty damn well). Use UL +where it makes sense: on the uppermost node to match upstream router's uplink +capacity. Or in special cases, such as testing (limit certain subtree to some +speed), or customers that must never get more than certain speed. In the last +case you can usually achieve the same by just using a RT criterion without LS+UL +on leaf nodes. + +As for the router case - remember it's good to differentiate between "traffic to +router" (remote console, web config, etc.) and "outgoing traffic", so for +example: + +.nf +tc qdisc add dev eth0 root handle 1:0 hfsc default 0x8002 +tc class add dev eth0 parent 1:0 classid 1:999 hfsc rt m2 50Mbit +tc class add dev eth0 parent 1:0 classid 1:1 hfsc ls m2 2Mbit ul m2 2Mbit +.fi + +\&... so "internet" tree under 1:1 and "router itself" as 1:999 +. +.SH "LAYER2 ADAPTATION" +. +Please refer to \fBtc\-stab\fR(8) +. +.SH "SEE ALSO" +. +\fBtc\fR(8), \fBtc\-hfsc\fR(8), \fBtc\-stab\fR(8) + +Please direct bugreports and patches to: +. +.SH "AUTHOR" +. +Manpage created by Michal Soltys (soltys@ziu.info) diff --git a/man/man8/.gitignore b/man/man8/.gitignore new file mode 100644 index 0000000..7b08e91 --- /dev/null +++ b/man/man8/.gitignore @@ -0,0 +1,5 @@ +# these pages are built +ip-address.8 +ip-link.8 +ip-netns.8 +ip-route.8 diff --git a/man/man8/Makefile b/man/man8/Makefile new file mode 100644 index 0000000..b1fd87b --- /dev/null +++ b/man/man8/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +TARGETS = ip-address.8 ip-link.8 ip-netns.8 ip-route.8 + +MAN8PAGES = $(TARGETS) $(filter-out $(TARGETS),$(wildcard *.8)) + +all: $(TARGETS) + +%: %.in + sed \ + -e "s|@NETNS_ETC_DIR@|$(NETNS_ETC_DIR)|g" \ + -e "s|@NETNS_RUN_DIR@|$(NETNS_RUN_DIR)|g" \ + -e "s|@SYSCONFDIR@|$(CONFDIR)|g" \ + $< > $@ + +distclean: clean + +clean: + @rm -f $(TARGETS) + +install: + $(INSTALLDIR) $(DESTDIR)$(MANDIR)/man8 + $(INSTALLMAN) $(MAN8PAGES) $(DESTDIR)$(MANDIR)/man8 + +check: all + @for page in $(MAN8PAGES); do test 0 -eq $$($(MAN_CHECK) $$page \ + $(MAN_REDIRECT)) || { echo "Error in $$page"; exit 1; }; done + +.PHONY: install clean distclean check diff --git a/man/man8/arpd.8 b/man/man8/arpd.8 new file mode 100644 index 0000000..5050a98 --- /dev/null +++ b/man/man8/arpd.8 @@ -0,0 +1,69 @@ +.TH ARPD 8 "28 June, 2007" + +.SH NAME +arpd \- userspace arp daemon. + +.SH SYNOPSIS +Usage: arpd [ -lkh? ] [ -a N ] [ -b dbase ] [ -B number ] [ -f file ] [-p interval ] [ -n time ] [ -R rate ] [ ] + +.SH DESCRIPTION +The +.B arpd +daemon collects gratuitous ARP information, saving it on local disk and feeding it to the kernel on demand to avoid redundant broadcasting due to limited size of the kernel ARP cache. + +.SH OPTIONS +.TP +-h -? +Print help +.TP +-l +Dump the arpd database to stdout and exit. The output consists of three columns: the interface index, the IP address of the interface, and the MAC address of the interface. Negative entries for dead hosts are also shown, in this case the MAC address is replaced by the word FAILED followed by a colon and the most recent time when the fact that the host is dead was proven. +.TP +-f +Read and load an arpd database from FILE in a text format similar to that dumped by option -l. Exit after load, possibly listing resulting database, if option -l is also given. If FILE is -, stdin is read to get the ARP table. +.TP +-b +the location of the database file. The default location is /var/lib/arpd/arpd.db +.TP +-a +With this option, arpd not only passively listens for ARP packets on the interface, but also sends broadcast queries itself. NUMBER is the number of such queries to make before a destination is considered dead. When arpd is started as kernel helper (i.e. with app_solicit enabled in sysctl or even with option -k) without this option and still did not learn enough information, you can observe 1 second gaps in service. Not fatal, but not good. +.TP +-k +Suppress sending broadcast queries by the kernel. This option only makes sense together with option -a. +.TP +-n