Project import generated by Copybara. GitOrigin-RevId: d28750d2694715fda73560ae6bda40d25f9c059b
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 <goodaddress> <badaddress> +Steve Wise <larrystevenwise@gmail.com> <swise@opengridcomputing.com> +Steve Wise <larrystevenwise@gmail.com> <swise@chelsio.com> + +Stephen Hemminger <stephen@networkplumber.org> <sthemmin@microsoft.com> +Stephen Hemminger <stephen@networkplumber.org> <shemming@brocade.com> +Stephen Hemminger <stephen@networkplumber.org> <stephen.hemminger@vyatta.com> +Stephen Hemminger <stephen@networkplumber.org> <shemminger@vyatta.com> +Stephen Hemminger <stephen@networkplumber.org> <shemminger> +Stephen Hemminger <stephen@networkplumber.org> <shemminger@linux-foundation.org> +Stephen Hemminger <stephen@networkplumber.org> <shemminger@osdl.org> +Stephen Hemminger <stephen@networkplumber.org> <osdl.org!shemminger> +Stephen Hemminger <stephen@networkplumber.org> <osdl.net!shemminger> + +David Ahern <dsahern@gmail.com> <dsa@cumulusnetworks.com>
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/Android.mk
@@ -0,0 +1 @@ +include $(call all-subdir-makefiles)
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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.
diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/CleanSpec.mk
@@ -0,0 +1,49 @@ +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b79b1f --- /dev/null +++ b/Makefile
@@ -0,0 +1,123 @@ +# 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) $(MFLAGS) -C $$i; done + +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 " snapshot - generate version number header" + @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 include/bpf_elf.h $(DESTDIR)$(HDRDIR) + +snapshot: + echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \ + > include/SNAPSHOT.h + +clean: + @for i in $(SUBDIRS) testsuite; \ + do $(MAKE) $(MFLAGS) -C $$i clean; done + +clobber: + touch config.mk + $(MAKE) $(MFLAGS) 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/NOTICE b/NOTICE new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/NOTICE
@@ -0,0 +1 @@ +COPYING \ No newline at end of file
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 + <netdev@vger.kernel.org> + +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/tc b/bash-completion/tc new file mode 100644 index 0000000..007e1c2 --- /dev/null +++ b/bash-completion/tc
@@ -0,0 +1,800 @@ +# tc(8) completion -*- shell-script -*- +# Copyright 2016 6WIND S.A. +# Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com> + +QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \ + mqprio multiq netem pfifo_fast 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' + 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/Android.mk b/bridge/Android.mk new file mode 100644 index 0000000..cf0487f --- /dev/null +++ b/bridge/Android.mk
@@ -0,0 +1,25 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bridge +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := bridge.c \ + fdb.c \ + monitor.c \ + link.c \ + mdb.c \ + vlan.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../include/uapi +LOCAL_STATIC_LIBRARIES := libnetlink \ + libiprouteutil +LOCAL_CFLAGS := \ + -O2 -W -Wall \ + -Wno-implicit-function-declaration \ + -Wno-missing-field-initializers \ + -Wno-pointer-arith \ + -Wno-sign-compare \ + -Wno-unused-parameter \ + -Werror \ + -D_GNU_SOURCE -DHAVE_SETNS +include $(BUILD_EXECUTABLE)
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..a50d9d5 --- /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 <shemminger@vyatta.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/socket.h> +#include <string.h> +#include <errno.h> + +#include "SNAPSHOT.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..941ce2d --- /dev/null +++ b/bridge/fdb.c
@@ -0,0 +1,545 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Get/set/delete fdb table with netlink + * + * TODO: merge/replace this with ip neighbour + * + * Authors: Stephen Hemminger <shemminger@vyatta.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <time.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/if_ether.h> +#include <linux/neighbour.h> +#include <string.h> +#include <limits.h> +#include <stdbool.h> + +#include "json_print.h" +#include "libnetlink.h" +#include "br_common.h" +#include "rt_names.h" +#include "utils.h" + +static unsigned int filter_index, filter_vlan, filter_state, filter_master; + +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 ] [ dst IPADDR ]\n" + " [ vlan VID ] [ port PORT] [ vni VNI ] [ via DEV ]\n" + " [ src_vni VNI ]\n" + " bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ] [ state STATE ] ]\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 ""; + + 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; + + 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_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 (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; + + 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, "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; + } + + /* 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 (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; +} + +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, "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..074edf0 --- /dev/null +++ b/bridge/link.c
@@ -0,0 +1,586 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/if_bridge.h> +#include <string.h> +#include <stdbool.h> + +#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 (strcmp(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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/if_ether.h> +#include <string.h> +#include <arpa/inet.h> + +#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 <shemminger@vyatta.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/neighbour.h> +#include <string.h> + +#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..6d33b0a --- /dev/null +++ b/bridge/vlan.c
@@ -0,0 +1,693 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <linux/if_ether.h> +#include <string.h> + +#include "json_print.h" +#include "libnetlink.h" +#include "br_common.h" +#include "utils.h" + +static unsigned int filter_index, filter_vlan; +static int show_vlan_tunnel_info = 0; + +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); + addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid); + addattr32(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, const char *fmt) +{ + open_json_object(NULL); + print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", fmt, + ll_index_to_name(ifi_index)); + open_json_array(PRINT_JSON, "vlans"); +} + +static void close_vlan_port(void) +{ + close_json_array(PRINT_JSON, NULL); + close_json_object(); +} + +static void print_range(const char *name, __u16 start, __u16 id) +{ + char end[64]; + + snprintf(end, sizeof(end), "%sEnd", name); + + print_hu(PRINT_ANY, name, "\t %hu", start); + if (start != id) + print_hu(PRINT_ANY, end, "-%hu", id); + +} + +static void print_vlan_tunnel_info(FILE *fp, 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; + + if (!filter_vlan) + open_vlan_port(ifindex, "%s"); + + open_json_array(PRINT_JSON, "tunnel"); + 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; + 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_u32(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_u32(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 (tunnel_flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) + continue; + + if (filter_vlan) + open_vlan_port(ifindex, "%s"); + + open_json_object(NULL); + print_range("vlan", last_vid_start, tunnel_vid); + print_range("tunid", last_tunid_start, tunnel_id); + close_json_object(); + + print_string(PRINT_FP, NULL, "%s", _SL_); + if (filter_vlan) + close_vlan_port(); + } + + if (!filter_vlan) + close_vlan_port(); +} + +static int print_vlan_tunnel(struct nlmsghdr *n, void *arg) +{ + struct ifinfomsg *ifm = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + FILE *fp = arg; + + 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 AF_SPEC isn't there, vlan table is not preset for this port */ + if (!tb[IFLA_AF_SPEC]) { + if (!filter_vlan && !is_json_context()) { + color_fprintf(fp, COLOR_IFNAME, "%s", + ll_index_to_name(ifm->ifi_index)); + fprintf(fp, "\tNone\n"); + } + return 0; + } + + print_vlan_tunnel_info(fp, tb[IFLA_AF_SPEC], ifm->ifi_index); + + fflush(fp); + return 0; +} + +static int print_vlan(struct nlmsghdr *n, void *arg) +{ + FILE *fp = 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 AF_SPEC isn't there, vlan table is not preset for this port */ + if (!tb[IFLA_AF_SPEC]) { + if (!filter_vlan && !is_json_context()) { + color_fprintf(fp, COLOR_IFNAME, "%s", + ll_index_to_name(ifm->ifi_index)); + fprintf(fp, "\tNone\n"); + } + return 0; + } + + print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index); + print_string(PRINT_FP, NULL, "%s", _SL_); + + fflush(fp); + 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_lluint(PRINT_ANY, "rx_bytes", + "\n RX: %llu bytes", + vstats->rx_bytes); + print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n", + vstats->rx_packets); + 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, "%-16s"); + found_vlan = true; + } else { + print_string(PRINT_FP, NULL, "%-16s", ""); + } + 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) +{ + 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("Cannont send dump request"); + exit(1); + } + + if (!is_json_context()) { + if (show_vlan_tunnel_info) + printf("port\tvlan ids\ttunnel id\n"); + else + printf("port\tvlan ids\n"); + } + + if (show_vlan_tunnel_info) + ret = rtnl_dump_filter(&rth, print_vlan_tunnel, + stdout); + else + ret = rtnl_dump_filter(&rth, print_vlan, stdout); + if (ret < 0) { + fprintf(stderr, "Dump ternminated\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("Cannont send dump request"); + exit(1); + } + + if (!is_json_context()) + printf("%-16s 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("Cannont 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; + + open_vlan_port(ifindex, "%s"); + + 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; + + open_json_object(NULL); + print_range("vlan", last_vid_start, vinfo->vid); + + print_vlan_flags(vinfo->flags); + close_json_object(); + print_string(PRINT_FP, NULL, "%s", _SL_); + } + 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); + if (matches(*argv, "tunnelshow") == 0) { + show_vlan_tunnel_info = 1; + return vlan_show(argc-1, argv+1); + } + if (matches(*argv, "help") == 0) + usage(); + } else { + return vlan_show(0, NULL); + } + + 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..45fcffb --- /dev/null +++ b/configure
@@ -0,0 +1,403 @@ +#!/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} + echo "PKG_CONFIG:=${PKG_CONFIG}" >>$CONFIG + echo "AR:=${AR}" >>$CONFIG + echo "CC:=${CC}" >>$CONFIG +} + +check_atm() +{ + cat >$TMPDIR/atmtest.c <<EOF +#include <atm.h> +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 <<EOF +#include <xtables.h> +#include <linux/netfilter.h> +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 <<EOF +#include <xtables.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 "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 <<EOF +#include <xtables.h> +#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 <<EOF +#include <sched.h> +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 <<EOF +#include <linux/netfilter/ipset/ip_set.h> +#ifndef IP_SET_INVALID +#define IPSET_DIM_MAX 3 +typedef unsigned short ip_set_id_t; +#endif +#include <linux/netfilter/xt_set.h> + +struct xt_set_info info; +#if IPSET_PROTOCOL == 6 +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 <<EOF +#include <fcntl.h> +#include <stdlib.h> +#include <db_185.h> +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 <<EOF +#include <string.h> +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 <<EOF +# user can control verbosity similar to kernel builds (e.g., V=1) +ifeq ("\$(origin V)", "command line") + VERBOSE = \$(V) +endif +ifndef VERBOSE + VERBOSE = 0 +endif +ifeq (\$(VERBOSE),1) + Q = +else + Q = @ +endif + +ifeq (\$(VERBOSE), 0) + QUIET_CC = @echo ' CC '\$@; + QUIET_AR = @echo ' AR '\$@; + QUIET_LINK = @echo ' LINK '\$@; + QUIET_YACC = @echo ' YACC '\$@; + QUIET_LEX = @echo ' LEX '\$@; +endif +EOF +} + +echo "# Generated config based on" $INCLUDE >$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..056ac95 --- /dev/null +++ b/devlink/devlink.c
@@ -0,0 +1,7257 @@ +/* + * 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 <jiri@mellanox.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdbool.h> +#include <unistd.h> +#include <getopt.h> +#include <limits.h> +#include <errno.h> +#include <inttypes.h> +#include <sys/sysinfo.h> +#define _LINUX_SYSINFO_H /* avoid collision with musl header */ +#include <linux/genetlink.h> +#include <linux/devlink.h> +#include <libmnl/libmnl.h> +#include <netinet/ether.h> +#include <sys/types.h> + +#include "SNAPSHOT.h" +#include "list.h" +#include "mnlg.h" +#include "json_writer.h" +#include "utils.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 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 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) + +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_enable; + bool eswitch_encap_mode; + const char *resource_path; + uint32_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; + const char *trap_name; + const char *trap_group_name; + enum devlink_trap_action trap_action; +}; + +struct dl { + struct mnlg_socket *nlg; + struct list_head ifname_map_list; + int argc; + char **argv; + bool no_nice_names; + struct dl_opts opts; + json_writer_t *jw; + 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 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 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_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_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, +}; + +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, +}; + +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 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")) + val = true; + else if (!strcmp(str, "false") || !strcmp(str, "0")) + 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 dpipe_counters_enable_get(const char *typestr, + bool *counters_enable) +{ + if (strcmp(typestr, "enable") == 0) { + *counters_enable = 1; + } else if (strcmp(typestr, "disable") == 0) { + *counters_enable = 0; + } else { + pr_err("Unknown counter_state \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + +static int eswitch_encap_mode_get(const char *typestr, bool *p_mode) +{ + if (strcmp(typestr, "enable") == 0) { + *p_mode = true; + } else if (strcmp(typestr, "disable") == 0) { + *p_mode = false; + } 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 { + pr_err("Unknown trap action \"%s\"\n", actionstr); + return -EINVAL; + } + 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."}, +}; + +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; + } + } + 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_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") && + (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = dpipe_counters_enable_get(typestr, + &opts->dpipe_counters_enable); + if (err) + return err; + o_found |= DL_OPT_DPIPE_TABLE_COUNTERS; + } else if (dl_argv_match(dl, "encap") && + (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_uint32_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, "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 { + 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_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_enable); + 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_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); + +} + +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 { disable | enable } ]\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\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)) + jsonw_end_array(dl->jw); + if (should_arr_last_handle_start(dl, bus_name, + dev_name)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_handle_set(dl, bus_name, dev_name); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } 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) + jsonw_end_object(dl->jw); + 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)) + jsonw_end_array(dl->jw); + if (should_arr_last_port_handle_start(dl, bus_name, + dev_name, + port_index)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_port_handle_set(dl, bus_name, dev_name, + port_index); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } 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) + jsonw_end_object(dl->jw); + else + pr_out("\n"); +} + + +static void pr_out_str(struct dl *dl, const char *name, const char *val) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_string_field(dl->jw, name, val); + else + pr_out("%s %s", name, val); +} + +static void pr_out_bool(struct dl *dl, const char *name, bool val) +{ + if (dl->json_output) + jsonw_bool_field(dl->jw, name, val); + else + pr_out_str(dl, name, val ? "true" : "false"); +} + +static void pr_out_uint(struct dl *dl, const char *name, unsigned int val) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_uint_field(dl->jw, name, val); + else + pr_out("%s %u", name, val); +} + +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 pr_out_str(dl, name, "unlimited"); + + if (dl->json_output) + jsonw_u64_field(dl->jw, name, val); + else + pr_out("%s %"PRIu64, name, val); +} + +static void pr_out_bool_value(struct dl *dl, bool value) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_bool(dl->jw, value); + else + pr_out("%s", value ? "true" : "false"); +} + +static void pr_out_uint_value(struct dl *dl, unsigned int value) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_uint(dl->jw, value); + else + pr_out("%u", value); +} + +static void pr_out_uint64_value(struct dl *dl, uint64_t value) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_u64(dl->jw, value); + else + pr_out("%"PRIu64, value); +} + +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) + jsonw_printf(dl->jw, "%d", 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_str_value(struct dl *dl, const char *value) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_string(dl->jw, value); + else + pr_out("%s", value); +} + +static void pr_out_name(struct dl *dl, const char *name) +{ + __pr_out_indent_newline(dl); + if (dl->json_output) + jsonw_name(dl->jw, name); + else + pr_out("%s:", name); +} + +static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr) +{ + if (dl->json_output) { + jsonw_name(dl->jw, "address"); + jsonw_uint(dl->jw, addr); + jsonw_name(dl->jw, "data"); + jsonw_start_array(dl->jw); + } +} + +static void pr_out_region_chunk_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_array(dl->jw); +} + +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) + jsonw_printf(dl->jw, "%d", data[i]); + else + pr_out("%02x ", data[i]); + + addr++; + i++; + } + pr_out_region_chunk_end(dl); +} + +static void pr_out_section_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + jsonw_start_object(dl->jw); + jsonw_name(dl->jw, name); + jsonw_start_object(dl->jw); + } +} + +static void pr_out_section_end(struct dl *dl) +{ + if (dl->json_output) { + if (dl->arr_last.present) + jsonw_end_array(dl->jw); + jsonw_end_object(dl->jw); + jsonw_end_object(dl->jw); + } +} + +static void pr_out_array_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + jsonw_name(dl->jw, name); + jsonw_start_array(dl->jw); + } 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) { + jsonw_end_array(dl->jw); + } 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) { + jsonw_name(dl->jw, name); + jsonw_start_object(dl->jw); + } 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) { + jsonw_end_object(dl->jw); + } else { + __pr_out_indent_dec(); + __pr_out_indent_dec(); + } +} + +static void pr_out_entry_start(struct dl *dl) +{ + if (dl->json_output) + jsonw_start_object(dl->jw); +} + +static void pr_out_entry_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + else + __pr_out_newline(); +} + +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])); + 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 "<unknown type>"; + } +} + +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 "<unknown mode>"; + } +} + +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 "<unknown mode>"; + } +} + +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]) + pr_out_str(dl, "mode", + eswitch_mode_name(mnl_attr_get_u16(tb[DEVLINK_ATTR_ESWITCH_MODE]))); + + if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) + pr_out_str(dl, "inline-mode", + eswitch_inline_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]))); + + if (tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) { + bool encap_mode = !!mnl_attr_get_u8(tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]); + + pr_out_str(dl, "encap", encap_mode ? "enable" : "disable"); + } + + 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 = "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; + + pr_out_str(dl, "cmode", + 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; + pr_out_str(dl, "value", vstr); + } else { + pr_out_uint(dl, "value", 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; + pr_out_str(dl, "value", vstr); + } else { + pr_out_uint(dl, "value", 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; + pr_out_str(dl, "value", vstr); + } else { + pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr)); + } + break; + case MNL_TYPE_STRING: + pr_out_str(dl, "value", mnl_attr_get_str(val_attr)); + break; + case MNL_TYPE_FLAG: + pr_out_bool(dl, "value", val_attr ? true : false); + 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]); + pr_out_str(dl, "name", nla_name); + + if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC]) + pr_out_str(dl, "type", "driver-specific"); + else + pr_out_str(dl, "type", "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); + pr_out_bool(dl, "reload_failed", 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 void cmd_dev_reload_help(void) +{ + pr_err("Usage: devlink dev reload [ DEV ]\n"); +} + +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_reload_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, 0); + 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]); + + pr_out_str(dl, 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(); + pr_out_str(dl, "driver", 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(); + pr_out_str(dl, "serial_number", mnl_attr_get_str(nla_sn)); + } + __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] || + has_versions; + + if (has_info) + pr_out_info(dl, nlh, tb, has_versions); + + return MNL_CB_OK; +} + +static void cmd_dev_info_help(void) +{ + pr_err("Usage: devlink dev info [ DEV ]\n"); +} + +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_info_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; +} + +static void cmd_dev_flash_help(void) +{ + pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n"); +} + + +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_flash_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. + */ + close(pipe_r); + err = _mnlg_socket_send(dl->nlg, nlh); + write(pipe_w, &err, sizeof(err)); + close(pipe_w); + exit(0); + } + 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"); +} + +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 "<unknown type>"; + } +} + +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"; + default: + return "<unknown flavour>"; + } +} + +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]); + pr_out_uint(dl, "pfnum", fn_num); + } + if (tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]) { + fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]); + pr_out_uint(dl, "vfnum", fn_num); + } +} + +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); + if (pt_attr) { + uint16_t port_type = mnl_attr_get_u16(pt_attr); + + pr_out_str(dl, "type", 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) + pr_out_str(dl, "des_type", + port_type_name(des_port_type)); + } + } + if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) + pr_out_str(dl, "netdev", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); + if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) + pr_out_str(dl, "ibdev", + 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]); + + pr_out_str(dl, "flavour", 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]); + pr_out_uint(dl, "port", port_number); + } + if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]) + pr_out_uint(dl, "split_group", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); + 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 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); + } + 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); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); + pr_out_uint(dl, "ing_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); + pr_out_uint(dl, "eg_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); + pr_out_uint(dl, "ing_tcs", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); + pr_out_uint(dl, "eg_tcs", + 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 "<unknown type>"; + } +} + +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 "<unknown type>"; + } +} + +static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle_start_arr(dl, tb); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); + pr_out_str(dl, "thtype", + threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + if (tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE]) + pr_out_uint(dl, "cell_size", + 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); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", + 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); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "tc", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", + 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]; + + jsonw_name(dl->jw, label); + jsonw_start_object(dl->jw); + list_for_each_entry(occ_item, list, list) { + sprintf(buf, "%u", occ_item->index); + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + if (bound_pool) + jsonw_uint_field(dl->jw, "bound_pool", + occ_item->bound_pool_index); + jsonw_uint_field(dl->jw, "current", occ_item->cur); + jsonw_uint_field(dl->jw, "max", occ_item->max); + jsonw_end_object(dl->jw); + } + jsonw_end_object(dl->jw); +} + +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_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"; + default: return "<unknown cmd>"; + } +} + +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_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"; + default: return "<unknown obj>"; + } +} + +static void pr_out_mon_header(uint8_t cmd) +{ + pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd)); +} + +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]) + pr_out_str(dl, "msg", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])); + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) + pr_out_str(dl, "component", + 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_trap(struct dl *dl, struct nlattr **tb, bool array); +static void pr_out_trap_group(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); + 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); + 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); + 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); + 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); + 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); + 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); + break; + } + 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, "trap") != 0 && + strcmp(cur_obj, "trap-group") != 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; + err = _mnlg_socket_recv_run(dl->nlg, cmd_mon_show_cb, dl); + 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 | trap | trap-group }\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 "<unknown>"; + } +} + +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); + pr_out_str(ctx->dl, "name", field->name); + if (ctx->dl->verbose) + pr_out_uint(ctx->dl, "id", field->id); + pr_out_uint(ctx->dl, "bitwidth", field->bitwidth); + if (field->mapping_type) + pr_out_str(ctx->dl, "mapping_type", + 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); + pr_out_str(ctx->dl, "name", header->name); + if (ctx->dl->verbose) { + pr_out_uint(ctx->dl, "id", header->id); + pr_out_str(ctx->dl, "global", + global ? "true" : "false"); + } + 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_header_help(void) +{ + pr_err("Usage: devlink dpipe headers show DEV\n"); +} + +static int cmd_dpipe_header(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dpipe_header_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 "<unknown>"; + } +} + +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; + + pr_out_str(ctx->dl, "type", + dpipe_action_type_e2s(action->type)); + pr_out_str(ctx->dl, "header", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + pr_out_str(ctx->dl, "field", + 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) + pr_out_str(ctx->dl, "mapping", 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 "<unknown>"; + } +} + +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; + + pr_out_str(ctx->dl, "type", + dpipe_match_type_e2s(match->type)); + pr_out_str(ctx->dl, "header", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + pr_out_str(ctx->dl, "field", + 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) + pr_out_str(ctx->dl, "mapping", 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)); + } + pr_out_str(dl, "resource_path", 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; + + pr_out_str(ctx->dl, "name", table->name); + pr_out_uint(ctx->dl, "size", size); + pr_out_str(ctx->dl, "counters_enabled", + counters_enabled ? "true" : "false"); + + 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); + pr_out_uint(ctx->dl, "resource_units", 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 "<unknown>"; + } +} + +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); + pr_out_str(ctx->dl, 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) +{ + pr_out_str(ctx->dl, 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); + pr_out_str(ctx->dl, 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; + + pr_out_uint(ctx->dl, 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]); + pr_out_uint(ctx->dl, "mapping_value", 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; + } + + entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]); + pr_out_uint(ctx->dl, "index", entry_index); + + if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) { + counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]); + pr_out_uint(ctx->dl, "counter", 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 void cmd_dpipe_table_help(void) +{ + pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n" + "where OBJECT-LIST := { show | set | dump }\n"); +} + +static int cmd_dpipe_table(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dpipe_table_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 void cmd_dpipe_help(void) +{ + pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n" + "where OBJECT-LIST := { header | table }\n"); +} + +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 "<unknown unit>"; + } +} + +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; + + pr_out_str(dl, "name", 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) + pr_out_uint(dl, "occ", resource->size_occ); + pr_out_str(dl, "unit", resource_unit_str_get(resource->unit)); + + if (resource->size_min != resource->size_max) { + pr_out_uint(dl, "size_min", resource->size_min); + pr_out_u64(dl, "size_max", resource->size_max); + pr_out_uint(dl, "size_gran", 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 + pr_out_str(dl, "dpipe_tables", "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); + pr_out_str(dl, "table_name", 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) + pr_out_str(dl, "size_valid", 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) { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } else { + pr_out("%s:", buf); + } +} + +static void pr_out_region_handle_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + 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) { + jsonw_name(dl->jw, "snapshot"); + jsonw_start_array(dl->jw); + } else { + pr_out("snapshot %s", array ? "[" : ""); + } +} + +static void pr_out_region_snapshots_end(struct dl *dl, bool array) +{ + if (dl->json_output) + jsonw_end_array(dl->jw); + 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) + jsonw_uint(dl->jw, 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 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 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); + } + 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_HEALTH_REPORTER_NAME, + DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD | + DL_OPT_HEALTH_REPORTER_AUTO_RECOVER); + 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_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; + + switch (type) { + case MNL_TYPE_FLAG: + pr_out_bool_value(dl, mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U8: + pr_out_uint_value(dl, mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U16: + pr_out_uint_value(dl, mnl_attr_get_u16(nl_data)); + break; + case MNL_TYPE_U32: + pr_out_uint_value(dl, mnl_attr_get_u32(nl_data)); + break; + case MNL_TYPE_U64: + pr_out_uint64_value(dl, mnl_attr_get_u64(nl_data)); + break; + case MNL_TYPE_NUL_STRING: + pr_out_str_value(dl, 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; +} + +struct nest_entry { + int attr_type; + struct list_head list; +}; + +struct fmsg_cb_data { + 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 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_entry_start(dl); + else + pr_out_entry_end(dl); + break; + case DEVLINK_ATTR_FMSG_PAIR_NEST_START: + break; + case DEVLINK_ATTR_FMSG_ARR_NEST_START: + if (dl->json_output) { + if (start) + jsonw_start_array(dl->jw); + else + jsonw_end_array(dl->jw); + } else { + if (start) { + __pr_out_newline(); + __pr_out_indent_inc(); + } else { + __pr_out_indent_dec(); + } + } + 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: + pr_out_name(dl, mnl_attr_get_str(nla_object)); + 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: + 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 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_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + data.dl = dl; + INIT_LIST_HEAD(&data.entry_list); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data); + 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_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 "<unknown state>"; + } +} + +static void format_logtime(uint64_t time_ms, char *ts_date, char *ts_time) +{ + 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(ts_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info); + strftime(ts_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info); +} + +static void pr_out_health(struct dl *dl, struct nlattr **tb_health) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + enum devlink_health_reporter_state state; + const struct nlattr *attr; + uint64_t time_ms; + 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; + + pr_out_handle_start_arr(dl, tb_health); + + pr_out_str(dl, "reporter", + 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]); + pr_out_str(dl, "state", 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]) { + char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + + attr = tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]; + time_ms = mnl_attr_get_u64(attr); + format_logtime(time_ms, dump_date, dump_time); + + pr_out_str(dl, "last_dump_date", dump_date); + pr_out_str(dl, "last_dump_time", dump_time); + } + 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]) + pr_out_bool(dl, "auto_recover", + mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])); + + __pr_out_indent_dec(); + pr_out_handle_end(dl); +} + +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 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_HEALTH_REPORTER]) + return MNL_CB_ERROR; + + pr_out_health(dl, tb); + + return MNL_CB_OK; +} + +static int cmd_health_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_HEALTH_REPORTER_GET, + flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | + 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, dl); + pr_out_section_end(dl); + return err; +} + +static void cmd_health_help(void) +{ + pr_err("Usage: devlink health show [ dev DEV reporter REPORTER_NAME ]\n"); + pr_err(" devlink health recover DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health diagnose DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health dump show DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health dump clear DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health set DEV reporter REPORTER_NAME { grace_period | auto_recover } { msec | boolean }\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); + } 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"; + default: + return "<unknown type>"; + } +} + +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"; + default: + return "<unknown action>"; + } +} + +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"; + default: + return "<unknown metadata type>"; + } +} +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) + pr_out_str_value(dl, 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); + + pr_out_str(dl, "name", mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME])); + pr_out_str(dl, "type", trap_type_name(type)); + pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + pr_out_str(dl, "action", trap_action_name(action)); + pr_out_str(dl, "group", + 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 } ]\n"); + pr_err(" devlink trap show [ DEV trap TRAP ]\n"); + pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop } ]\n"); + pr_err(" devlink trap group show [ DEV group GROUP ]\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); + + pr_out_str(dl, "name", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME])); + pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + 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); + 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 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); + } + 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" + "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; + } + if (dl->json_output) { + dl->jw = jsonw_new(stdout); + if (!dl->jw) { + pr_err("Failed to create JSON writer\n"); + goto err_json_new; + } + jsonw_pretty(dl->jw, dl->pretty_output); + } + return 0; + +err_json_new: + ifname_map_fini(dl); +err_ifname_map_create: + mnlg_socket_close(dl->nlg); + return err; +} + +static void dl_fini(struct dl *dl) +{ + if (dl->json_output) + jsonw_destroy(&dl->jw); + 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' }, + { 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:njpvs", + long_options, NULL)) >= 0) { + + switch (opt) { + case 'V': + printf("devlink utility, iproute2-ss%s\n", SNAPSHOT); + 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': + dl->pretty_output = true; + break; + case 'v': + dl->verbose = true; + break; + case 's': + dl->stats = true; + 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 <jiri@mellanox.com> + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <libmnl/libmnl.h> +#include <linux/genetlink.h> + +#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 <jiri@mellanox.com> + */ + +#ifndef _MNLG_H_ +#define _MNLG_H_ + +#include <libmnl/libmnl.h> + +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 <DEVICE> parent 1:0 protocol ip prio 10 <Filter description> +flowid 1:1 action <ACTION description>* + +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 <u32 syntax> action ipt -j <iptables target syntax> + +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 <ACTION> [RAND] [INDEX] + +Where: + ACTION := reclassify | drop | continue | pass | ok + RAND := random <RANDTYPE> <ACTION> <VAL> + 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 <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> +where: +DIRECTION := <ingress | egress> +ACTION := <mirror | redirect> +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..b3a0ec8 --- /dev/null +++ b/etc/iproute2/rt_protos
@@ -0,0 +1,23 @@ +# +# 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 +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 + * <idle>-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 + * <idle>-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 + * <idle>-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 '\<dlsym' $$files | sed -n '/snprintf/{s:.*"\([^"]*\)".*:\1:;s:%s::;p}'` ; do \ + sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \ + done > $@ + +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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#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 <CMD>\n" \ + "CMD := get <PARMS> | list | monitor\n" \ + "PARMS := name <name> | id <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..aba3c13 --- /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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> /* until we put our own header */ +#include "SNAPSHOT.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-ss%s\n", SNAPSHOT); + 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..a8d433a --- /dev/null +++ b/genl/genl_utils.h
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TC_UTIL_H_ +#define _TC_UTIL_H_ 1 + +#include "utils.h" +#include "linux/genetlink.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 <string.h> +#include "dlfcn.h" + +void *_dlsym(const char *sym) +{ +#include "static-syms.h" + return NULL; +}
diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h new file mode 100644 index 0000000..b98ad50 --- /dev/null +++ b/include/SNAPSHOT.h
@@ -0,0 +1 @@ +static const char SNAPSHOT[] = "191125";
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 <stdint.h> + +#include <linux/pkt_cls.h> +#include <linux/bpf.h> +#include <linux/filter.h> + +#include <asm/byteorder.h> + +#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 <asm/types.h> + +/* 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +#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 <daniel@iogearbox.net> + * Jiri Pirko <jiri@resnulli.us> + */ + +#ifndef __BPF_UTIL__ +#define __BPF_UTIL__ + +#include <linux/bpf.h> +#include <linux/btf.h> +#include <linux/filter.h> +#include <linux/magic.h> +#include <linux/elf-em.h> +#include <linux/if_alg.h> + +#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/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 <stdbool.h> + +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 <dlfcn.h> +#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 <netinet/ip.h> +#include <xtables.h> +#include <libiptc/libip6tc.h> +#include <iptables/internal.h> + +/* 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 <netinet/ip.h> +#include <xtables.h> +#include <libiptc/libiptc.h> +#include <iptables/internal.h> + +/* 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..fe92d14 --- /dev/null +++ b/include/json_print.h
@@ -0,0 +1,77 @@ +/* + * 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, <julien@cumulusnetworks.com> + */ + +#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); + +bool is_json_context(void); + +void fflush_fp(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) \ + void print_color_##type_name(enum output_type t, \ + enum color_attr color, \ + const char *key, \ + const char *fmt, \ + type value); \ + \ + static inline void print_##type_name(enum output_type t, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + print_color_##type_name(t, COLOR_NONE, key, fmt, value); \ + } + +_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 + +#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 <stephen@networkplumber.org> + */ + +#ifndef _JSON_WRITER_H_ +#define _JSON_WRITER_H_ + +#include <stdbool.h> +#include <stdint.h> + +/* 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 <limits.h> + +#include <netinet/ip.h> +#include <netinet/in.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <net/if.h> +#include <sys/types.h> +#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 <linux/types.h> +#include <libiptc/ipt_kernel_headers.h> +#ifdef __cplusplus +# include <climits> +#else +# include <limits.h> /* INT_MAX in ip6_tables.h */ +#endif +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <libiptc/xtcshared.h> + +#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 <linux/types.h> +#include <libiptc/ipt_kernel_headers.h> +#ifdef __cplusplus +# include <climits> +#else +# include <limits.h> /* INT_MAX in ip_tables.h */ +#endif +#include <linux/netfilter_ipv4/ip_tables.h> +#include <libiptc/xtcshared.h> + +#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 <libiptc/ipt_kernel_headers.h> +#include <linux/netfilter/x_tables.h> + +#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..8ebdc6d --- /dev/null +++ b/include/libnetlink.h
@@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include <stdio.h> +#include <string.h> +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/if_link.h> +#include <linux/if_addr.h> +#include <linux/neighbour.h> +#include <linux/netconf.h> +#include <arpa/inet.h> + +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((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) + +#define parse_rtattr_one_nested(type, rta) \ + (parse_rtattr_one(type, RTA_DATA(rta), RTA_PAYLOAD(rta))) + +#define parse_rtattr_nested_compat(tb, max, rta, data, len) \ + ({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \ + __parse_rtattr_nested_compat(tb, max, rta, len); }) + +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 <stddef.h> + +#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 <sched.h> +#include <sys/mount.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <errno.h> + +#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 <features.h> + +/* + * 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 <sys/types.h> + +# 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 <bits/endian.h> 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..62ebbd6 --- /dev/null +++ b/include/rt_names.h
@@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include <asm/types.h> + +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); + +#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 <linux/atm.h> directly. + * Instead, #include <atm.h> + */ + +#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 <linux/atmapi.h> +#include <linux/atmsap.h> +#include <linux/atmioc.h> +#include <linux/types.h> + + +/* 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 <linux/types.h> +#include <linux/atmapi.h> +#include <linux/atmioc.h> + + +#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 <linux/atmapi.h> +#include <linux/atm.h> +#include <linux/atmioc.h> + + +#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..cd7655e --- /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 http://icawww1.epfl.ch/linux-atm/magic.html for the complete list of + * "magic" ioctl numbers. + */ + + +#ifndef _LINUX_ATMIOC_H +#define _LINUX_ATMIOC_H + +#include <asm/ioctl.h> + /* 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 <linux/atmapi.h> + +/* + * 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..f7a7553 --- /dev/null +++ b/include/uapi/linux/bpf.h
@@ -0,0 +1,3616 @@ +/* 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 <linux/types.h> +#include <linux/bpf_common.h> + +/* 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 */ +}; + +/* 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, +}; + +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, +}; + +/* 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, +}; + +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, + __MAX_BPF_ATTACH_TYPE +}; + +#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_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. + * + * 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) + +/* 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 */ +#define BPF_ANY 0 /* create new element or update existing */ +#define BPF_NOEXIST 1 /* create new element if it didn't exist */ +#define BPF_EXIST 2 /* update existing element */ +#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */ + +/* flags for BPF_MAP_CREATE command */ +#define 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. + */ +#define BPF_F_NO_COMMON_LRU (1U << 1) +/* Specify numa node during map creation */ +#define BPF_F_NUMA_NODE (1U << 2) + +#define BPF_OBJ_NAME_LEN 16U + +/* Flags for accessing BPF object from syscall side. */ +#define BPF_F_RDONLY (1U << 3) +#define BPF_F_WRONLY (1U << 4) + +/* Flag for stack_map, store build_id+offset instead of pointer */ +#define BPF_F_STACK_BUILD_ID (1U << 5) + +/* Zero-initialize hash function seed. This should only be used for testing. */ +#define BPF_F_ZERO_SEED (1U << 6) + +/* Flags for accessing BPF object from program side. */ +#define BPF_F_RDONLY_PROG (1U << 7) +#define BPF_F_WRONLY_PROG (1U << 8) + +/* Clone map from listener for newly accepted socket */ +#define BPF_F_CLONE (1U << 9) + +/* flags for BPF_PROG_QUERY */ +#define BPF_F_QUERY_EFFECTIVE (1U << 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; + }; +}; + +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 */ + }; + + 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 { /* 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 */ + }; + + 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; + }; + + 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 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 { + __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; +} __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. + * + * int 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. + * + * int 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. + * + * int bpf_probe_read(void *dst, u32 size, const void *src) + * Description + * For tracing programs, safely attempt to read *size* bytes from + * address *src* and store the data in *dst*. + * 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. + * Return + * Current *ktime*. + * + * int 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: <formatted msg> + * + * 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. + * * ``<formatted msg>`` 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 + * bloc (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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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*. + * + * int bpf_get_current_comm(char *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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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 be attained with the more generic + * **bpf_redirect_map**\ (), which requires specific maps to be + * used but offers better performance. + * 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 + * indentifier 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. + * + * int bpf_perf_event_output(struct pt_regs *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. + * + * int bpf_skb_load_bytes(const struct sk_buff *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. + * + * int bpf_get_stackid(struct pt_regs *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=<new value> + * 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. + * + * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, u8 *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. + * + * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, u8 *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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) + * Description + * Copy a NUL terminated string from an unsafe 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_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()** 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. + * + * 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). + * + * u32 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 + * + * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *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*. + * + * 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**. + * * **IPPROTO_TCP**, which supports the following *optname*\ s: + * **TCP_CONGESTION**, **TCP_BPF_IW**, + * **TCP_BPF_SNDCWND_CLAMP**. + * * **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. + * + * int 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*. + * + * 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. + * + * int 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. + * + * When used to redirect packets to net devices, this helper + * provides a high performance increase over **bpf_redirect**\ (). + * This is due to various implementation details of the underlying + * mechanisms, one of which is the fact that **bpf_redirect_map**\ + * () tries to send packet as a "bulk" to the device. + * Return + * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error. + * + * int bpf_sk_redirect_map(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. + * + * int 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. + * + * int 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. + * + * int 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 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. + * + * int 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. + * + * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, char *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*. + * + * 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. + * + * int 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 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 + * + * int 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). + * + * int 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. + * + * int 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 + * + * int 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 + * + * int 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. + * + * int 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**). Looking for a free port to bind to can be + * expensive, therefore binding to port is not permitted by the + * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively) + * must be set to zero. + * Return + * 0 on success, or a negative error in case of failure. + * + * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta) + * Description + * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is + * only possible to shrink the packet as of this writing, + * therefore *delta* must be 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. + * + * int 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. + * + * int bpf_get_stack(struct pt_regs *regs, 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=<new value> + * Return + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + * + * int bpf_skb_load_bytes_relative(const struct sk_buff *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. + * + * int 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 + * + * int bpf_sock_hash_update(struct bpf_sock_ops_kern *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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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 + * + * int 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. + * + * int 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 + * 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 + * 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int bpf_msg_push_data(struct sk_buff *skb, 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. + * + * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 pop, u64 flags) + * Description + * Will remove *pop* 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. + * + * int 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 + * + * int 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 + * + * int 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. + * + * int bpf_skb_ecn_set_ce(struct sk_buf *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. + * + * int 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. + * + * int 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). + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int 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. + * + * int bpf_send_signal(u32 sig) + * Description + * Send signal *sig* 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. + * + * 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 + */ +#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), + +/* 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. */ +#define BPF_F_RECOMPUTE_CSUM (1ULL << 0) +#define 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. + */ +#define BPF_F_HDR_FIELD_MASK 0xfULL + +/* BPF_FUNC_l4_csum_replace flags. */ +#define BPF_F_PSEUDO_HDR (1ULL << 4) +#define BPF_F_MARK_MANGLED_0 (1ULL << 5) +#define BPF_F_MARK_ENFORCE (1ULL << 6) + +/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ +#define BPF_F_INGRESS (1ULL << 0) + +/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ +#define BPF_F_TUNINFO_IPV6 (1ULL << 0) + +/* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ +#define BPF_F_SKIP_FIELD_MASK 0xffULL +#define BPF_F_USER_STACK (1ULL << 8) +/* flags used by BPF_FUNC_get_stackid only. */ +#define BPF_F_FAST_STACK_CMP (1ULL << 9) +#define BPF_F_REUSE_STACKID (1ULL << 10) +/* flags used by BPF_FUNC_get_stack only. */ +#define BPF_F_USER_BUILD_ID (1ULL << 11) + +/* BPF_FUNC_skb_set_tunnel_key flags. */ +#define BPF_F_ZERO_CSUM_TX (1ULL << 1) +#define BPF_F_DONT_FRAGMENT (1ULL << 2) +#define BPF_F_SEQ_NUMBER (1ULL << 3) + +/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and + * BPF_FUNC_perf_event_read_value flags. + */ +#define BPF_F_INDEX_MASK 0xffffffffULL +#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK +/* BPF_FUNC_perf_event_output for sk_buff input context. */ +#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) + +/* Current network namespace */ +#define BPF_F_CURRENT_NETNS (-1L) + +/* BPF_FUNC_skb_adjust_room flags. */ +#define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0) + +#define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff +#define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56 + +#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1) +#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2) +#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3) +#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4) +#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. */ +#define BPF_F_SYSCTL_BASE_NAME (1ULL << 0) + +/* BPF_FUNC_sk_storage_get flags */ +#define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0) + +/* 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); +}; + +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; +}; + +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 */ +}; + +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 */ +}; + +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 :32; + __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))); + +/* 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 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 4-byte read and 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 */ +#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0) +#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1) +#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2) +#define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3) +#define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently + * supported cb flags + */ + +/* 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! */ +}; + +#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ +#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ + +struct bpf_perf_event_value { + __u64 counter; + __u64 enabled; + __u64 running; +}; + +#define BPF_DEVCG_ACC_MKNOD (1ULL << 0) +#define BPF_DEVCG_ACC_READ (1ULL << 1) +#define BPF_DEVCG_ACC_WRITE (1ULL << 2) + +#define BPF_DEVCG_DEV_BLOCK (1ULL << 0) +#define 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 + */ +#define BPF_FIB_LOOKUP_DIRECT (1U << 0) +#define 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 */ +}; + +#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0) +#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1) +#define 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; +}; + +#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..d28dd89 --- /dev/null +++ b/include/uapi/linux/btf.h
@@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2018 Facebook */ +#ifndef __LINUX_BTF_H__ +#define __LINUX_BTF_H__ + +#include <linux/types.h> + +#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, +}; + +/* 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 <oliver.hartkopp@volkswagen.de> + * Urs Thuermann <urs.thuermann@volkswagen.de> + * 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 <linux/types.h> +#include <linux/socket.h> + +/* 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 + * + * <received_can_id> & 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 <wg@grandegger.com> + * + * 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 <linux/types.h> + +/* + * 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..84c0caf --- /dev/null +++ b/include/uapi/linux/devlink.h
@@ -0,0 +1,471 @@ +/* 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 <jiri@mellanox.com> + * + * 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, + + /* 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. + */ +}; + +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_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. + */ +enum devlink_trap_action { + DEVLINK_TRAP_ACTION_DROP, + DEVLINK_TRAP_ACTION_TRAP, +}; + +/** + * 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. + */ +enum devlink_trap_type { + DEVLINK_TRAP_TYPE_DROP, + DEVLINK_TRAP_TYPE_EXCEPTION, +}; + +enum { + /* Trap can report input port as metadata */ + DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT, +}; + +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 */ + /* 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, +}; + +#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 <linux/types.h> +#include <linux/rtnetlink.h> + +/* 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 <linux/types.h> +#include <linux/bpf_common.h> + +/* + * 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..065408e --- /dev/null +++ b/include/uapi/linux/gen_stats.h
@@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include <linux/types.h> + +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_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_basic_packed { + __u64 bytes; + __u32 packets; +} __attribute__ ((packed)); + +/** + * 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..1317119 --- /dev/null +++ b/include/uapi/linux/genetlink.h
@@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include <linux/types.h> +#include <linux/netlink.h> + +#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_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_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..0fe4238 --- /dev/null +++ b/include/uapi/linux/hdlc/ioctl.h
@@ -0,0 +1,85 @@ +/* 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; + +/* 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..1dc7cc6 --- /dev/null +++ b/include/uapi/linux/icmpv6.h
@@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_ICMPV6_H +#define _LINUX_ICMPV6_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +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 <asm/byteorder.h>" +#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 <asm/byteorder.h>" +#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_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..495cdd2 --- /dev/null +++ b/include/uapi/linux/if.h
@@ -0,0 +1,293 @@ +/* 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, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <linux/libc-compat.h> /* for compatibility with glibc */ +#include <linux/types.h> /* for "__kernel_caddr_t" et al */ +#include <linux/socket.h> /* for "struct sockaddr" et al */ + /* for "__user" et al */ + +#include <sys/socket.h> /* for struct sockaddr. */ + +#if __UAPI_DEF_IF_IFNAMSIZ +#define IFNAMSIZ 16 +#endif /* __UAPI_DEF_IF_IFNAMSIZ */ +#define IFALIASZ 256 +#include <linux/hdlc/ioctl.h> + +/* 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/<dev>/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 */ +}; + +/* + * 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; + + /* 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 <linux/types.h> +#include <linux/netlink.h> + +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 <yoshfuji@linux-ipv6.org> + */ + +#ifndef __LINUX_IF_ADDRLABEL_H +#define __LINUX_IF_ADDRLABEL_H + +#include <linux/types.h> + +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 <herbert@gondor.apana.org.au> + * + * 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 <linux/types.h> + +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, <waltje@uWalt.NL.Mugnet.ORG> + * Florian La Roche, + * Jonathan Layes <layes@loran.com> + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 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 <linux/netdevice.h> + +/* 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..790585f --- /dev/null +++ b/include/uapi/linux/if_bonding.h
@@ -0,0 +1,155 @@ +/* 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 <amir.noam at intel dot com> + * - Added support for getting slave's speed and duplex via ethtool. + * Needed for 802.3ad and other future modes. + * + * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Shmulik Hen <shmulik.hen at intel dot com> + * - Enable support of modes that need to use the unique mac address of + * each slave. + * + * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Amir Noam <amir.noam at intel dot com> + * - Moved driver's private data types to bonding.h + * + * 2003/03/18 - Amir Noam <amir.noam at intel dot com>, + * Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Shmulik Hen <shmulik.hen at intel dot com> + * - Added support for IEEE 802.3ad Dynamic link aggregation mode. + * + * 2003/05/01 - Amir Noam <amir.noam at intel dot com> + * - Added ABI version control to restore compatibility between + * new/old ifenslave and new/old bonding. + * + * 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com> + * - Code cleanup and style changes + * + * 2005/05/05 - Jason Gabler <jygabler at lbl dot gov> + * - added definitions for various XOR hashing policies + */ + +#ifndef _LINUX_IF_BONDING_H +#define _LINUX_IF_BONDING_H + +#include <linux/if.h> +#include <linux/types.h> +#include <linux/if_ether.h> + +/* 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 */ + +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..31fc51b --- /dev/null +++ b/include/uapi/linux/if_bridge.h
@@ -0,0 +1,317 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * 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 <linux/types.h> +#include <linux/if_ether.h> +#include <linux/in6.h> + +#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_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 */ + +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; +}; + +/* 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_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..728c42d --- /dev/null +++ b/include/uapi/linux/if_ether.h
@@ -0,0 +1,171 @@ +/* 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, <waltje@uWalt.NL.Mugnet.ORG> + * Donald Becker, <becker@super.org> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> + * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk> + * + * 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 <linux/types.h> + +/* + * 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_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 + * <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD + * license, available in the LICENSE.TXT file accompanying this + * software. These details are also available at + * <http://www.openfabrics.org/software_license.htm>. + * + * 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..d36919f --- /dev/null +++ b/include/uapi/linux/if_link.h
@@ -0,0 +1,1029 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include <linux/types.h> +#include <linux/netlink.h> + +/* 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_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 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_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_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, +}; + +/* 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, +}; + +/* 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 section */ + +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_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_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) + +/* 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_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..7743993 --- /dev/null +++ b/include/uapi/linux/if_macsec.h
@@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * include/uapi/linux/if_macsec.h - MACsec device + * + * Copyright (c) 2015 Sabrina Dubroca <sd@queasysnail.net> + * + * 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 <linux/types.h> + +#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.1AEbn-2011 */ +#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL +#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL + +/* 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_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 */ + 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_END, + NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, + MACSEC_SA_ATTR_MAX = __MACSEC_SA_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, +}; + +/* 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 <linux/types.h> + +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 <max_mk@yahoo.com> + * + * 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 <linux/types.h> +#include <linux/if_ether.h> +#include <linux/filter.h> + +/* 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 <linux/types.h> +#include <linux/if.h> +#include <linux/ip.h> +#include <linux/in6.h> +#include <asm/byteorder.h> + + +#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 <greearb@candelatech.com> + * + * 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..ac079ab --- /dev/null +++ b/include/uapi/linux/in.h
@@ -0,0 +1,306 @@ +/* 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 <netinet/in.h> file. + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <linux/types.h> +#include <linux/libc-compat.h> +#include <linux/socket.h> + +#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_RAW = 255, /* Raw IP packets */ +#define IPPROTO_RAW IPPROTO_RAW + 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 + +/* 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 allos 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 + +/* <asm/byteorder.h> contains the htonl type stuff.. */ +#include <asm/byteorder.h> + + +#endif /* _LINUX_IN_H */
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h new file mode 100644 index 0000000..d016ac9 --- /dev/null +++ b/include/uapi/linux/in6.h
@@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Types and definitions for AF_INET6 + * Linux INET6 implementation + * + * Authors: + * Pedro Roque <roque@di.fc.ul.pt> + * + * Sources: + * IPv6 Program Interfaces for BSD Systems + * <draft-ietf-ipngwg-bsd-api-05.txt> + * + * Advanced Sockets API for IPv6 + * <draft-stevens-advanced-api-00.txt> + * + * 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 <linux/types.h> +#include <linux/libc-compat.h> + +/* + * 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 + +/* 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..3dff684 --- /dev/null +++ b/include/uapi/linux/inet_diag.h
@@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _INET_DIAG_H_ +#define _INET_DIAG_H_ + +#include <linux/types.h> + +/* 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, +}; + +#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE + +/* 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, +}; + +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_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_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, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <linux/types.h> +#include <asm/byteorder.h> + +#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 <asm/byteorder.h>" +#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 <linux/types.h> +#include <linux/if.h> /* For IFNAMSIZ. */ +#include <linux/in6.h> /* 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 <linux/pfkeyv2.h> + +#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 <linux/sysinfo.h> + +/* + * '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 <jchapman@katalix.com> + */ + +#ifndef _LINUX_L2TP_H_ +#define _LINUX_L2TP_H_ + +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/in6.h> + +#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 <linux/libc-compat.h> + * 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..3f3fe6f --- /dev/null +++ b/include/uapi/linux/lwtunnel.h
@@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LWTUNNEL_H_ +#define _LWTUNNEL_H_ + +#include <linux/types.h> + +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_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_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_MAX, +}; + +#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_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..903cc2d --- /dev/null +++ b/include/uapi/linux/magic.h
@@ -0,0 +1,98 @@ +/* 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 + +/* 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 Z3FOLD_MAGIC 0x33 + +#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 <linux/types.h> +#include <asm/byteorder.h> + +/* 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 <roopa@cumulusnetworks.com> + * + * 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/neighbour.h b/include/uapi/linux/neighbour.h new file mode 100644 index 0000000..cd144e3 --- /dev/null +++ b/include/uapi/linux/neighbour.h
@@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include <linux/types.h> +#include <linux/netlink.h> + +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_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) + +#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, <obz@Kodak.COM> + * Ross Biro + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <linux/socket.h> +#include <asm/socket.h> + +#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 <nicolas.dichtel@6wind.com> + * + * 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 <linux/types.h> +#include <linux/netlink.h> + +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, <waltje@uWalt.NL.Mugnet.ORG> + * Corey Minyard <wf-rch!minyard@relay.EU.net> + * Donald J. Becker, <becker@cesdis.gsfc.nasa.gov> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> + * Bjorn Ekwall. <bj0rn@blox.se> + * Pekka Riikonen <priikone@poseidon.pspt.fi> + * + * 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 <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/if_link.h> + + +#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 <linux/types.h> + +#include <linux/in.h> +#include <linux/in6.h> + +/* 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..c512003 --- /dev/null +++ b/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> + * Patrick Schaaf <bof@bof.de> + * Martin Josefsson <gandalf@wlug.westbo.se> + * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@netfilter.org> + * + * 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 <linux/types.h> + +/* 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_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 <linux/kernel.h> +#include <linux/types.h> + +#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 <linux/types.h> +#include <linux/netfilter/ipset/ip_set.h> + +/* 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 <linux/types.h> + +/* 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 <linux/netfilter.h> + +/* only for userspace compatibility */ + +#include <limits.h> /* 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..409cff7 --- /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 <linux/types.h> + +#include <linux/if.h> +#include <linux/netfilter_ipv4.h> + +#include <linux/netfilter/x_tables.h> + +#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 <linux/netfilter/xt_tcpudp.h> +#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 (void *)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 <linux/netfilter.h> + +/* only for userspace compatibility */ + +#include <limits.h> /* 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..7ae314b --- /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 <linux/types.h> + +#include <linux/if.h> +#include <linux/netfilter_ipv6.h> + +#include <linux/netfilter/x_tables.h> + +#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 <linux/netfilter/xt_tcpudp.h> +#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 (void *)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..2c28d32 --- /dev/null +++ b/include/uapi/linux/netlink.h
@@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/kernel.h> +#include <linux/socket.h> /* for __kernel_sa_family_t */ +#include <linux/types.h> + +#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; +}; + +#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 <linux/types.h> + +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..b56c5b8 --- /dev/null +++ b/include/uapi/linux/nexthop.h
@@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NEXTHOP_H +#define _LINUX_NEXTHOP_H + +#include <linux/types.h> + +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_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 <linux/types.h> + +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 <asm/param.h> + +#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 <linux/types.h> + +#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..a6aa466 --- /dev/null +++ b/include/uapi/linux/pkt_cls.h
@@ -0,0 +1,690 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +#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_MAX +}; + +#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, + /* 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_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_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_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..12b5189 --- /dev/null +++ b/include/uapi/linux/pkt_sched.h
@@ -0,0 +1,1416 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +#include <linux/const.h> +#include <linux/types.h> + +/* 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) + +/* NSSFIFO section */ + +enum { + TCA_NSSFIFO_UNSPEC, + TCA_NSSFIFO_PARMS, + __TCA_NSSFIFO_MAX +}; + +#define TCA_NSSFIFO_MAX (__TCA_NSSFIFO_MAX - 1) + +struct tc_nssfifo_qopt { + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ + __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ +}; + +/* NSSWRED section */ + +enum { + TCA_NSSWRED_UNSPEC, + TCA_NSSWRED_PARMS, + __TCA_NSSWRED_MAX +}; + +#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1) +#define NSSWRED_CLASS_MAX 6 +struct tc_red_alg_parameter { + __u32 min; /* qlen_avg < min: all pkts are enqueued */ + __u32 max; /* qlen_avg > max: all pkts are dropped */ + __u32 probability; /* Drop probability at qlen_avg = max */ + __u32 exp_weight_factor; /* exp_weight_factor for calculate qlen_avg */ +}; + +struct tc_nsswred_traffic_class { + __u32 limit; /* Queue length */ + __u32 weight_mode_value; /* Weight mode value */ + struct tc_red_alg_parameter rap;/* Parameters for RED alg */ +}; + +/* + * Weight modes for WRED + */ +enum tc_nsswred_weight_modes { + TC_NSSWRED_WEIGHT_MODE_DSCP = 0, /* Weight mode is DSCP */ + TC_NSSWRED_WEIGHT_MODES, /* Must be last */ +}; +typedef enum tc_nsswred_weight_modes tc_nsswred_weight_mode_t; + +struct tc_nsswred_qopt { + __u32 limit; /* Queue length */ + tc_nsswred_weight_mode_t weight_mode; /* Weight mode */ + __u32 traffic_classes; /* How many traffic classes: DPs */ + __u32 def_traffic_class; /* Default traffic if no match: def_DP */ + __u32 traffic_id; /* The traffic id to be configured */ + __u32 weight_mode_value; /* Weight mode value */ + struct tc_red_alg_parameter rap; /* RED algorithm parameters */ + struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX]; + /* Traffic settings for dumpping */ + __u8 ecn; /* Setting ECN bit or dropping */ + __u8 set_default; /* Sets qdisc to be the default for enqueue */ +}; + +/* NSSCODEL section */ + +enum { + TCA_NSSCODEL_UNSPEC, + TCA_NSSCODEL_PARMS, + __TCA_NSSCODEL_MAX +}; + +#define TCA_NSSCODEL_MAX (__TCA_NSSCODEL_MAX - 1) + +struct tc_nsscodel_qopt { + __u32 target; /* Acceptable queueing delay */ + __u32 limit; /* Maximum number of packets that can be held in the queue */ + __u32 interval; /* Monitoring interval */ + __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ +}; + +struct tc_nsscodel_xstats { + __u32 peak_queue_delay; /* Peak delay experienced by a dequeued packet */ + __u32 peak_drop_delay; /* Peak delay experienced by a dropped packet */ +}; + +/* NSSTBL section */ + +enum { + TCA_NSSTBL_UNSPEC, + TCA_NSSTBL_PARMS, + __TCA_NSSTBL_MAX +}; + +#define TCA_NSSTBL_MAX (__TCA_NSSTBL_MAX - 1) + +struct tc_nsstbl_qopt { + __u32 burst; /* Maximum burst size */ + __u32 rate; /* Limiting rate of TBF */ + __u32 peakrate; /* Maximum rate at which TBF is allowed to send */ + __u32 mtu; /* Max size of packet, or minumim burst size */ +}; + +/* NSSPRIO section */ + +#define TCA_NSSPRIO_MAX_BANDS 256 + +enum { + TCA_NSSPRIO_UNSPEC, + TCA_NSSPRIO_PARMS, + __TCA_NSSPRIO_MAX +}; + +#define TCA_NSSPRIO_MAX (__TCA_NSSPRIO_MAX - 1) + +struct tc_nssprio_qopt { + int bands; /* Number of bands */ +}; + +/* NSSBF section */ + +enum { + TCA_NSSBF_UNSPEC, + TCA_NSSBF_CLASS_PARMS, + TCA_NSSBF_QDISC_PARMS, + __TCA_NSSBF_MAX +}; + +#define TCA_NSSBF_MAX (__TCA_NSSBF_MAX - 1) + +struct tc_nssbf_class_qopt { + __u32 burst; /* Maximum burst size */ + __u32 rate; /* Allowed bandwidth for this class */ + __u32 mtu; /* MTU of the associated interface */ + __u32 quantum; /* Quamtum allocation for DRR */ +}; + +struct tc_nssbf_qopt { + __u16 defcls; /* Default class value */ +}; + +/* NSSWRR section */ + +enum { + TCA_NSSWRR_UNSPEC, + TCA_NSSWRR_CLASS_PARMS, + __TCA_NSSWRR_MAX +}; + +#define TCA_NSSWRR_MAX (__TCA_NSSWRR_MAX - 1) + +struct tc_nsswrr_class_qopt { + __u32 quantum; /* Weight associated to this class */ +}; + +/* NSSWFQ section */ + +enum { + TCA_NSSWFQ_UNSPEC, + TCA_NSSWFQ_CLASS_PARMS, + __TCA_NSSWFQ_MAX +}; + +#define TCA_NSSWFQ_MAX (__TCA_NSSWFQ_MAX - 1) + +struct tc_nsswfq_class_qopt { + __u32 quantum; /* Weight associated to this class */ +}; + +/* NSSHTB section */ + +enum { + TCA_NSSHTB_UNSPEC, + TCA_NSSHTB_CLASS_PARMS, + TCA_NSSHTB_QDISC_PARMS, + __TCA_NSSHTB_MAX +}; + +#define TCA_NSSHTB_MAX (__TCA_NSSHTB_MAX - 1) + +struct tc_nsshtb_class_qopt { + __u32 burst; /* Allowed burst size */ + __u32 rate; /* Allowed bandwidth for this class */ + __u32 cburst; /* Maximum burst size */ + __u32 crate; /* Maximum bandwidth for this class */ + __u32 quantum; /* Quantum allocation for DRR */ + __u32 priority; /* Priority value associated with this class */ + __u32 overhead; /* Overhead in bytes per packet */ +}; + +struct tc_nsshtb_qopt { + __u32 r2q; /* Rate to quantum ratio */ +}; + +/* NSSBLACKHOLE section */ + +enum { + TCA_NSSBLACKHOLE_UNSPEC, + TCA_NSSBLACKHOLE_PARMS, + __TCA_NSSBLACKHOLE_MAX +}; + +#define TCA_NSSBLACKHOLE_MAX (__TCA_NSSBLACKHOLE_MAX - 1) + +struct tc_nssblackhole_qopt { + __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ +}; + +/* 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_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 */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +#define TC_RED_ADAPTATIVE 4 +}; + +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) + +/* ARL section */ + +struct tc_arl_xstats { + __u32 max_bw; /* The maximum bw measured */ + __u32 min_rate; /* The lowest base rate */ + __u32 current_rate; /* The current rate */ + __u32 latency; /* The current latency */ + __u32 base_rate; /* The base rate */ + __u32 current_bw; /* The current bw measured */ + __u32 state; /* The current state */ +}; + +enum { + TCA_ARL_UNSPEC, + TCA_ARL_BUFFER, + TCA_ARL_MIN_RATE, + TCA_ARL_MAX_BW, + TCA_ARL_LIMIT, + TCA_ARL_MAX_LATENCY, + TCA_ARL_LATENCY_HYSTERESIS, + TCA_ARL_PAD, + TCA_ARL_MODE, + TCA_ARL_CODEL_TARGET, + __TCA_ARL_MAX, +}; +#define TCA_ARL_MAX (__TCA_ARL_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_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 */ +}; + +/* 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_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 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*/ +}; + +/* 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 BIT(0) +#define TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD BIT(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) + +#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 <linux/stddef.h> + +/* + * 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 + * <sys/time.h> (and thus <linux/time.h>) - but this is a more logical + * place for them. Solved by having dummy defines in <sys/time.h>. + */ + +/* + * This macro may have been defined in <gnu/types.h>. 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 <asm/posix_types.h> + +#endif /* _LINUX_POSIX_TYPES_H */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h new file mode 100644 index 0000000..358e83e --- /dev/null +++ b/include/uapi/linux/rtnetlink.h
@@ -0,0 +1,759 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include <linux/types.h> +#include <linux/netlink.h> +#include <linux/if_link.h> +#include <linux/if_addr.h> +#include <linux/neighbour.h> + +/* 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_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_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 */ + +/* 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_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +/* 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_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) + +/* 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..0d4c150 --- /dev/null +++ b/include/uapi/linux/sctp.h
@@ -0,0 +1,1174 @@ +/* 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 + * <http://www.gnu.org/licenses/>. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers <linux-sctp@vger.kernel.org> + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll <piggy@acm.org> + * R. Stewart <randall@sctp.chicago.il.us> + * K. Morneau <kmorneau@cisco.com> + * Q. Xie <qxie1@email.mot.com> + * Karl Knutson <karl@athena.chicago.il.us> + * Jon Grimm <jgrimm@us.ibm.com> + * Daisy Chang <daisyc@us.ibm.com> + * Ryan Layer <rmlayer@us.ibm.com> + * Ardelle Fan <ardelle.fan@intel.com> + * Sridhar Samudrala <sri@us.ibm.com> + * Inaky Perez-Gonzalez <inaky.gonzalez@intel.com> + * Vlad Yasevich <vladislav.yasevich@hp.com> + * + * 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 <linux/types.h> +#include <linux/socket.h> + +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 <draft-ietf-tsvwg-sctpsocket-07.txt>. + */ +#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 + +/* 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 + +/* 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 <bits/socket.h> 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, +}; + + +/* + * 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]; +}; + +/* + * 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; +}; + +/* + * 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; +}; + +/* 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_SN_TYPE_MAX = SCTP_STREAM_CHANGE_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, + 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; +}; + +/* + * 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 <david.lebrun@uclouvain.be> + * + * + * 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 <linux/types.h> +#include <linux/in6.h> /* 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 <linux/types.h> +#include <linux/seg6.h> + +#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..3004e98 --- /dev/null +++ b/include/uapi/linux/seg6_iptunnel.h
@@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun <david.lebrun@uclouvain.be> + * + * + * 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 <linux/seg6.h> /* 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 <david.lebrun@uclouvain.be> + * + * + * 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 <linux/seg6.h> + +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..549a31c --- /dev/null +++ b/include/uapi/linux/snmp.h
@@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Definitions for MIBs + * + * Author: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org> + */ + +#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_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 +}; + +#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..a69cf20 --- /dev/null +++ b/include/uapi/linux/sock_diag.h
@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __SOCK_DIAG_H__ +#define __SOCK_DIAG_H__ + +#include <linux/types.h> + +#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) + +#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, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <asm/bitsperlong.h> +#include <asm/sockios.h> + +/* 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 <linux/types.h> + +#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 <jiri@resnulli.us> + * + * 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 <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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_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 <linux/types.h> +#include <linux/pkt_cls.h> +#include <linux/ife.h> + +/* 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 <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +#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 <linux/pkt_cls.h> + +#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 <linux/pkt_cls.h> +#include <linux/types.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> +#include <linux/if_ether.h> + +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 <alexander.h.duyck@intel.com> + */ + +#ifndef __LINUX_TC_SKBEDIT_H +#define __LINUX_TC_SKBEDIT_H + +#include <linux/pkt_cls.h> + +#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 <linux/pkt_cls.h> + +#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..41c8b46 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_tunnel_key.h
@@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) 2016, Amir Vadai <amir@vadai.me> + * 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 <linux/pkt_cls.h> + +#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_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) + +#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 <jiri@resnulli.us> + * + * 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 <linux/pkt_cls.h> + +#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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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 <linux/types.h> +#include <linux/pkt_cls.h> + +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..d08acf4 --- /dev/null +++ b/include/uapi/linux/tcp.h
@@ -0,0 +1,339 @@ +/* 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, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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 <linux/types.h> +#include <asm/byteorder.h> +#include <linux/socket.h> + +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 <asm/byteorder.h> 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, +}; + +/* 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<<TCP_CA_Open) + /* + * The sender enters disordered state when it has received DUPACKs or + * SACKs in the last round of packets sent. This could be due to packet + * loss or reordering but needs further information to confirm packets + * have been lost. + */ + TCP_CA_Disorder = 1, +#define TCPF_CA_Disorder (1<<TCP_CA_Disorder) + /* + * The sender enters Congestion Window Reduction (CWR) state when it + * has received ACKs with ECN-ECE marks, or has experienced congestion + * or packet discard on the sender host (e.g. qdisc). + */ + TCP_CA_CWR = 2, +#define TCPF_CA_CWR (1<<TCP_CA_CWR) + /* + * The sender is in fast recovery and retransmitting lost packets, + * typically triggered by ACK events. + */ + TCP_CA_Recovery = 3, +#define TCPF_CA_Recovery (1<<TCP_CA_Recovery) + /* + * The sender is in loss recovery triggered by retransmission timeout. + */ + TCP_CA_Loss = 4 +#define TCPF_CA_Loss (1<<TCP_CA_Loss) +}; + +struct tcp_info { + __u8 tcpi_state; + __u8 tcpi_ca_state; + __u8 tcpi_retransmits; + __u8 tcpi_probes; + __u8 tcpi_backoff; + __u8 tcpi_options; + __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + __u8 tcpi_delivery_rate_app_limited:1; + + __u32 tcpi_rto; + __u32 tcpi_ato; + __u32 tcpi_snd_mss; + __u32 tcpi_rcv_mss; + + __u32 tcpi_unacked; + __u32 tcpi_sacked; + __u32 tcpi_lost; + __u32 tcpi_retrans; + __u32 tcpi_fackets; + + /* Times. */ + __u32 tcpi_last_data_sent; + __u32 tcpi_last_ack_sent; /* Not remembered, sorry. */ + __u32 tcpi_last_data_recv; + __u32 tcpi_last_ack_recv; + + /* Metrics. */ + __u32 tcpi_pmtu; + __u32 tcpi_rcv_ssthresh; + __u32 tcpi_rtt; + __u32 tcpi_rttvar; + __u32 tcpi_snd_ssthresh; + __u32 tcpi_snd_cwnd; + __u32 tcpi_advmss; + __u32 tcpi_reordering; + + __u32 tcpi_rcv_rtt; + __u32 tcpi_rcv_space; + + __u32 tcpi_total_retrans; + + __u64 tcpi_pacing_rate; + __u64 tcpi_max_pacing_rate; + __u64 tcpi_bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked */ + __u64 tcpi_bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived */ + __u32 tcpi_segs_out; /* RFC4898 tcpEStatsPerfSegsOut */ + __u32 tcpi_segs_in; /* RFC4898 tcpEStatsPerfSegsIn */ + + __u32 tcpi_notsent_bytes; + __u32 tcpi_min_rtt; + __u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */ + __u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */ + + __u64 tcpi_delivery_rate; + + __u64 tcpi_busy_time; /* Time (usec) busy sending data */ + __u64 tcpi_rwnd_limited; /* Time (usec) limited by receive window */ + __u64 tcpi_sndbuf_limited; /* Time (usec) limited by send buffer */ + + __u32 tcpi_delivered; + __u32 tcpi_delivered_ce; + + __u64 tcpi_bytes_sent; /* RFC4898 tcpEStatsPerfHCDataOctetsOut */ + __u64 tcpi_bytes_retrans; /* RFC4898 tcpEStatsPerfOctetsRetrans */ + __u32 tcpi_dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups */ + __u32 tcpi_reord_seen; /* reordering events seen */ + + __u32 tcpi_rcv_ooopack; /* Out-of-order packets received */ + + __u32 tcpi_snd_wnd; /* peer's advertised receive window after + * scaling (bytes) + */ +}; + +/* netlink attributes types for SCM_TIMESTAMPING_OPT_STATS */ +enum { + TCP_NLA_PAD, + TCP_NLA_BUSY, /* Time (usec) busy sending data */ + TCP_NLA_RWND_LIMITED, /* Time (usec) limited by receive window */ + TCP_NLA_SNDBUF_LIMITED, /* Time (usec) limited by send buffer */ + TCP_NLA_DATA_SEGS_OUT, /* Data pkts sent including retransmission */ + TCP_NLA_TOTAL_RETRANS, /* Data pkts retransmitted */ + TCP_NLA_PACING_RATE, /* Pacing rate in bytes per second */ + TCP_NLA_DELIVERY_RATE, /* Delivery rate in bytes per second */ + TCP_NLA_SND_CWND, /* Sending congestion window */ + TCP_NLA_REORDERING, /* Reordering metric */ + TCP_NLA_MIN_RTT, /* minimum RTT */ + TCP_NLA_RECUR_RETRANS, /* Recurring retransmits for the current pkt */ + TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ + TCP_NLA_SNDQ_SIZE, /* Data (bytes) pending in send queue */ + TCP_NLA_CA_STATE, /* ca_state of socket */ + TCP_NLA_SND_SSTHRESH, /* Slow start size threshold */ + TCP_NLA_DELIVERED, /* Data pkts delivered incl. out-of-order */ + TCP_NLA_DELIVERED_CE, /* Like above but only ones w/ CE marks */ + TCP_NLA_BYTES_SENT, /* Data bytes sent including retransmission */ + TCP_NLA_BYTES_RETRANS, /* Data bytes retransmitted */ + TCP_NLA_DSACK_DUPS, /* DSACK blocks received */ + TCP_NLA_REORD_SEEN, /* reordering events seen */ + TCP_NLA_SRTT, /* smoothed RTT in usecs */ +}; + +/* for TCP_MD5SIG socket option */ +#define TCP_MD5SIG_MAXKEYLEN 80 + +/* tcp_md5sig extension flags for TCP_MD5SIG_EXT */ +#define TCP_MD5SIG_FLAG_PREFIX 1 /* address prefix length */ + +struct tcp_md5sig { + struct __kernel_sockaddr_storage tcpm_addr; /* address associated */ + __u8 tcpm_flags; /* extension flags */ + __u8 tcpm_prefixlen; /* address prefix */ + __u16 tcpm_keylen; /* key length */ + __u32 __tcpm_pad; /* zero */ + __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ +}; + +/* INET_DIAG_MD5SIG */ +struct tcp_diag_md5sig { + __u8 tcpm_family; + __u8 tcpm_prefixlen; + __u16 tcpm_keylen; + __be32 tcpm_addr[4]; + __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; +}; + +/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ + +struct tcp_zerocopy_receive { + __u64 address; /* in: address of mapping */ + __u32 length; /* in/out: number of bytes to map/mapped */ + __u32 recv_skip_hint; /* out: amount of bytes to skip */ +}; +#endif /* _LINUX_TCP_H */
diff --git a/include/uapi/linux/tcp_metrics.h b/include/uapi/linux/tcp_metrics.h new file mode 100644 index 0000000..7cb4a17 --- /dev/null +++ b/include/uapi/linux/tcp_metrics.h
@@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* tcp_metrics.h - TCP Metrics Interface */ + +#ifndef _LINUX_TCP_METRICS_H +#define _LINUX_TCP_METRICS_H + +#include <linux/types.h> + +/* 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..e16cb4e --- /dev/null +++ b/include/uapi/linux/tipc.h
@@ -0,0 +1,291 @@ +/* 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 <linux/types.h> +#include <linux/sockios.h> + +/* + * 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) */ + +/* + * 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]; +}; + +/* 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..efb958f --- /dev/null +++ b/include/uapi/linux/tipc_netlink.h
@@ -0,0 +1,333 @@ +/* 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_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_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_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 <linux/types.h> +#include <linux/sock_diag.h> + +/* 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/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 <asm/types.h> + +#ifndef __ASSEMBLY__ + +#include <linux/posix_types.h> + + +/* + * 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/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 <linux/types.h> + +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 <linux/types.h> + +/* 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..78b2591 --- /dev/null +++ b/include/uapi/linux/xdp_diag.h
@@ -0,0 +1,72 @@ +/* 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 <linux/types.h> + +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) + +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_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; +}; + +#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..5cdda9d --- /dev/null +++ b/include/uapi/linux/xfrm.h
@@ -0,0 +1,540 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include <linux/in6.h> +#include <linux/types.h> + +/* 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_state_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 + +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..794d360 --- /dev/null +++ b/include/utils.h
@@ -0,0 +1,322 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include <sys/types.h> +#include <asm/types.h> +#include <resolv.h> +#include <stdlib.h> +#include <stdbool.h> +#include <time.h> + +#ifdef HAVE_LIBBSD +#include <bsd/string.h> +#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 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(void); +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/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 <sys/socket.h> /* PF_* */ +#include <sys/types.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <netinet/in.h> +#include <net/if.h> +#include <linux/types.h> +#include <linux/netfilter.h> +#include <linux/netfilter/x_tables.h> + +#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 <xtables-version.h> + +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/Android.mk b/ip/Android.mk new file mode 100644 index 0000000..8e6724b --- /dev/null +++ b/ip/Android.mk
@@ -0,0 +1,130 @@ +LOCAL_PATH := $(call my-dir) + +IPROUTE2_USE_CONFIG ?= no + +etc_iproute2_dir := etc/iproute2 + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := ip.c ipaddress.c ipaddrlabel.c iproute.c iprule.c ipnetns.c \ + rtm_map.c iptunnel.c ip6tunnel.c tunnel.c ipneigh.c ipntable.c iplink.c \ + ipmaddr.c ipmonitor.c ipmroute.c ipprefix.c iptuntap.c iptoken.c \ + ipxfrm.c xfrm_state.c xfrm_policy.c xfrm_monitor.c iplink_dummy.c \ + iplink_vlan.c link_veth.c link_gre.c iplink_can.c iplink_xdp.c \ + iplink_macvlan.c ipl2tp.c link_vti.c link_vti6.c link_xfrm.c \ + iplink_vxlan.c tcp_metrics.c iplink_ipoib.c ipnetconf.c link_ip6tnl.c \ + link_iptnl.c link_gre6.c iplink_bond.c iplink_bond_slave.c iplink_hsr.c \ + iplink_bridge.c iplink_bridge_slave.c ipfou.c iplink_ipvlan.c \ + iplink_geneve.c iplink_vrf.c iproute_lwtunnel.c ipmacsec.c ipila.c \ + ipvrf.c iplink_xstats.c ipseg6.c iplink_netdevsim.c iplink_rmnet.c \ + ipnexthop.c + +LOCAL_MODULE := ip + +ifeq ($(USE_IP_USER_BUILD),1) +LOCAL_MODULE_TAGS := user eng +else +LOCAL_MODULE_TAGS := eng +endif + +LOCAL_STATIC_LIBRARIES += libiprouteutil libnetlink + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi + +LOCAL_CFLAGS := \ + -O2 -g \ + -W -Wall \ + -Wno-implicit-function-declaration \ + -Wno-missing-field-initializers \ + -Wno-pointer-arith \ + -Wno-sign-compare \ + -Wno-unused-parameter \ + -Werror \ + -D_GNU_SOURCE + +ifeq ($(BUILD_EUREKA),true) +LOCAL_CFLAGS += -DHAVE_SETNS +endif + +LOCAL_LDFLAGS := -Wl,-export-dynamic -Wl,--no-gc-sections + +ifeq ($(IPROUTE2_USE_CONFIG), yes) +LOCAL_REQUIRED_MODULES += ematch_map group rt_dsfield rt_protos rt_realms rt_scopes rt_tables +endif + +LOCAL_REQUIRED_MODULES += rtmon + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := rtmon.c +LOCAL_MODULE := rtmon +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi +LOCAL_STATIC_LIBRARIES += libiprouteutil libnetlink +LOCAL_CFLAGS := \ + -O2 -g \ + -W -Wall \ + -Wno-implicit-function-declaration \ + -Wno-missing-field-initializers \ + -Wno-pointer-arith \ + -Wno-sign-compare \ + -Wno-unused-parameter \ + -Werror \ + -D_GNU_SOURCE +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := ematch_map +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := group +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt_dsfield +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt_protos +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt_realms +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt_scopes +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt_tables +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/$(etc_iproute2_dir) +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := ../$(etc_iproute2_dir)/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) +
diff --git a/ip/MODULE_LICENSE_GPL b/ip/MODULE_LICENSE_GPL new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ip/MODULE_LICENSE_GPL
diff --git a/ip/Makefile b/ip/Makefile new file mode 100644 index 0000000..5ab78d7 --- /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 + +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 '\<dlsym' $$files | sed -n '/snprintf/{s:.*"\([^"]*\)".*:\1:;s:%s::;p}'` ; do \ + sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \ + done > $@ + +endif
diff --git a/ip/NOTICE b/ip/NOTICE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/ip/NOTICE
@@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.
diff --git a/ip/ifcfg b/ip/ifcfg new file mode 100644 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 & + +ip route add unreachable 224.0.0.0/24 >/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 <linux/ila.h> +#include <string.h> + +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..fed26f8 --- /dev/null +++ b/ip/ip.c
@@ -0,0 +1,326 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "ip_common.h" +#include "namespace.h" +#include "color.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; +int numeric; +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 }\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 }, + { "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-ss%s\n", SNAPSHOT); + 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 <http://www.gnu.org/licenses>. + */ +/* + * Author: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/ip.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/if_tunnel.h> +#include <linux/ip6_tunnel.h> + +#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..879287e --- /dev/null +++ b/ip/ip_common.h
@@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _IP_COMMON_H_ +#define _IP_COMMON_H_ + +#include <stdbool.h> + +#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 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..0c65f34 --- /dev/null +++ b/ip/ipaddress.c
@@ -0,0 +1,2435 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <fnmatch.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/if_infiniband.h> +#include <linux/sockios.h> +#include <linux/net_namespace.h> + +#include "utils.h" +#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 | optimistic | 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_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(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + 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_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); + } + + 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 */ +static const struct { + const char *name; + unsigned long value; +} ifa_flag_names[] = { + { "secondary", IFA_F_SECONDARY }, + { "temporary", IFA_F_SECONDARY }, + { "nodad", IFA_F_NODAD }, + { "optimistic", IFA_F_OPTIMISTIC }, + { "dadfailed", IFA_F_DADFAILED }, + { "home", IFA_F_HOMEADDRESS }, + { "deprecated", IFA_F_DEPRECATED }, + { "tentative", IFA_F_TENTATIVE }, + { "permanent", IFA_F_PERMANENT }, + { "mngtmpaddr", IFA_F_MANAGETEMPADDR }, + { "noprefixroute", IFA_F_NOPREFIXROUTE }, + { "autojoin", IFA_F_MCAUTOJOIN }, + { "stable-privacy", IFA_F_STABLE_PRIVACY }, +}; + +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_names); i++) { + unsigned long mask = ifa_flag_names[i].value; + + if (mask == IFA_F_PERMANENT) { + if (!(flags & mask)) + print_bool(PRINT_ANY, + "dynamic", "dynamic ", true); + } else if (flags & mask) { + if (mask == IFA_F_SECONDARY && + ifa->ifa_family == AF_INET6) { + print_bool(PRINT_ANY, + "temporary", "temporary ", true); + } else { + print_string(PRINT_FP, NULL, + "%s ", ifa_flag_names[i].name); + print_bool(PRINT_JSON, + ifa_flag_names[i].name, NULL, true); + } + } + + flags &= ~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; + unsigned int i; + + 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"; + } + + for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) { + if (strcmp(arg, ifa_flag_names[i].name)) + continue; + + if (inv) + filter.flags &= ~ifa_flag_names[i].value; + else + filter.flags |= ifa_flag_names[i].value; + filter.flagmask |= ifa_flag_names[i].value; + return 0; + } + return -1; +} + +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:\n", ifa->ifa_index); + 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 (strcmp(*argv, "home") == 0) { + if (req.ifa.ifa_family == AF_INET6) + ifa_flags |= IFA_F_HOMEADDRESS; + else + fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n"); + } else if (strcmp(*argv, "optimistic") == 0) { + if (req.ifa.ifa_family == AF_INET6) + ifa_flags |= IFA_F_OPTIMISTIC; + else + fprintf(stderr, "Warning: optimistic option can be set only for IPv6 addresses\n"); + } else if (strcmp(*argv, "nodad") == 0) { + if (req.ifa.ifa_family == AF_INET6) + ifa_flags |= IFA_F_NODAD; + else + fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n"); + } else if (strcmp(*argv, "mngtmpaddr") == 0) { + if (req.ifa.ifa_family == AF_INET6) + ifa_flags |= IFA_F_MANAGETEMPADDR; + else + fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n"); + } else if (strcmp(*argv, "noprefixroute") == 0) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + } else if (strcmp(*argv, "autojoin") == 0) { + ifa_flags |= IFA_F_MCAUTOJOIN; + } 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 <http://www.gnu.org/licenses>. + * + * + * Based on iprule.c. + * + * Authors: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/types.h> +#include <linux/if_addrlabel.h> + +#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..ea126b0 --- /dev/null +++ b/ip/ipfou.c
@@ -0,0 +1,350 @@ +/* + * 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 <therbert@google.com> + */ + +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <net/if.h> +#include <linux/fou.h> +#include <linux/genetlink.h> +#include <linux/ip.h> +#include <arpa/inet.h> + +#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 } [ -6 ]\n" + " [ local IFADDR ] [ peer IFADDR ]\n" + " [ peer_port PORT ] [ dev IFNAME ]\n" + " ip fou del port PORT [ -6 ] [ 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 = AF_INET; + bool gue_set = false; + int ipproto_set = 0; + __u8 ipproto, type; + int port_set = 0; + int index = 0; + + 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 <tom@herbertland.com> + */ + +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <net/if.h> +#include <linux/ila.h> +#include <linux/genetlink.h> +#include <linux/ip.h> +#include <arpa/inet.h> + +#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 <jchapman@katalix.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/ip.h> + +#include <linux/genetlink.h> +#include <linux/l2tp.h> +#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..212a088 --- /dev/null +++ b/ip/iplink.c
@@ -0,0 +1,1685 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <errno.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <linux/sockios.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <sys/ioctl.h> +#include <stdbool.h> +#include <linux/mpls.h> + +#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"); + + 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 | 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_ifname(*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), + IFLA_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 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, "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_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 <jiri@resnulli.us> + * Scott Feldman <sfeldma@cumulusnetworks.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/if_bonding.h> + +#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..4eaf72b --- /dev/null +++ b/ip/iplink_bond_slave.c
@@ -0,0 +1,161 @@ +/* + * 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 <jiri@resnulli.us> + */ + +#include <stdio.h> +#include <sys/socket.h> +#include <linux/if_bonding.h> + +#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 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]) + print_int(PRINT_ANY, + "ad_actor_oper_port_state", + "ad_actor_oper_port_state %d ", + rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])); + + if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]) + print_int(PRINT_ANY, + "ad_partner_oper_port_state", + "ad_partner_oper_port_state %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_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..06f736d --- /dev/null +++ b/ip/iplink_bridge.c
@@ -0,0 +1,871 @@ +/* + * 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 <jiri@resnulli.us> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <netinet/ether.h> +#include <linux/if_link.h> +#include <linux/if_bridge.h> +#include <net/if.h> + +#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 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; + } + } + 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, "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 <jiri@resnulli.us> + */ + +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if_link.h> +#include <linux/if_bridge.h> + +#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 <wg@grandegger.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/can/netlink.h> + +#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 <stdio.h> +#include <stdlib.h> + +#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 <linville@tuxdriver.com> + */ + +#include <stdio.h> + +#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..7d9167d --- /dev/null +++ b/ip/iplink_hsr.c
@@ -0,0 +1,157 @@ +/* + * 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 <arvid.brodin@alten.se> + * + * Based on iplink_vlan.c by Patrick McHardy <kaber@trash.net> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> /* Needed by linux/if.h for some reason */ +#include <linux/if.h> +#include <linux/if_arp.h> +#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]\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"); +} + +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; + + 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, "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 ", "<none>"); + + 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 ", "<none>"); + + 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))); +} + +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 <stdio.h> +#include <stdlib.h> + +#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..05dba35 --- /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 <ogerlitz@mellanox.com> + * copied iflink_vlan.c authored by Patrick McHardy <kaber@trash.net> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/if_link.h> + +#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 <maheshb@google.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <linux/if_link.h> + +#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 <kaber@trash.net> + * Arnd Bergmann <arnd@arndb.de> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <linux/if_link.h> +#include <linux/if_ether.h> + +#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 } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | 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 <stdio.h> +#include <stdlib.h> + +#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 <stdio.h> +#include <stdlib.h> + +#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 <dnlplm@gmail.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 <stdio.h> +#include <stdlib.h> + +#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 <stdio.h> +#include <stdlib.h> + +#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..0dfb4a8 --- /dev/null +++ b/ip/iplink_vlan.c
@@ -0,0 +1,285 @@ +/* + * 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 <kaber@trash.net> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/if_vlan.h> + +#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_string(PRINT_FP, NULL, "\n %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 <shm@cumulusnetworks.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <linux/if_link.h> +#include <errno.h> + +#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 <socketcan@hartkopp.net> + * Based on: link_veth.c from Pavel Emelianov <xemul@openvz.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <net/if.h> +#include <linux/can/vxcan.h> + +#include "utils.h" +#include "ip_common.h" + +static void print_usage(FILE *f) +{ + printf("Usage: ip link <options> type vxcan [peer <options>]\n" + "To get <options> 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 <shemminger@vyatta.com + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <net/if.h> +#include <linux/ip.h> +#include <linux/if_link.h> +#include <arpa/inet.h> + +#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 <daniel@iogearbox.net> + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <linux/bpf.h> + +#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 <nikolay@cumulusnetworks.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/if_link.h> +#include <netinet/ether.h> + +#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..ad6ad7d --- /dev/null +++ b/ip/ipmacsec.c
@@ -0,0 +1,1381 @@ +/* + * 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 <sd@queasysnail.net> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <linux/genetlink.h> +#include <linux/if_ether.h> +#include <linux/if_macsec.h> + +#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", +}; + +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" + "where OPTS := [ pn <u32> ] [ on | off ]\n" + " ID := 128-bit hex string\n" + " KEY := 128-bit or 256-bit hex string\n" + " SCI := { sci <u64> | port { 1..2^16-1 } address <lladdr> }\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_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}, + }, +}; + +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; +} + +/* 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 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_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]); + + 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); + + 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_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 <lladdr> ] port { 1..2^16-1 } | sci <u64> ]\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" + ); +} + +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; + 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 { + 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..3400e05 --- /dev/null +++ b/ip/ipmaddr.c
@@ -0,0 +1,369 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#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; + + 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); + if (ll_addr_a2n(ifr.ifr_hwaddr.sa_data, + 14, *argv) < 0) { + fprintf(stderr, "Error: \"%s\" is not a legal ll address.\n", *argv); + 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <time.h> + +#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/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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include <rt_names.h> +#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..a3869c8 --- /dev/null +++ b/ip/ipneigh.c
@@ -0,0 +1,631 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#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" + "\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; +} + +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) { + fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n"); + return -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, <nicolas.dichtel@6wind.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> + +#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..fc58a04 --- /dev/null +++ b/ip/ipnetns.c
@@ -0,0 +1,1042 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define _ATFILE_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/inotify.h> +#include <sys/mount.h> +#include <sys/syscall.h> +#include <stdio.h> +#include <string.h> +#include <sched.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <linux/limits.h> + +#include <linux/net_namespace.h> + +#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) { + have_rtnl_getnsid = 0; + return 0; + } + + addattr32(&req.n, 1024, NETNSA_FD, fd); + + if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { + perror("request send failed"); + exit(1); + } + 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_u32(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) +{ + uint32_t h = NSID_HASH_NSID(nsid); + struct hlist_node *n; + + 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; + + 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; + + 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 (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_u32(tb[NETNSA_NSID]); + if (nsid < 0) + print_string(PRINT_ANY, "nsid", "nsid %s ", "not-assigned"); + else + print_uint(PRINT_ANY, "nsid", "nsid %u ", nsid); + + if (tb[NETNSA_CURRENT_NSID]) { + current = rta_getattr_u32(tb[NETNSA_CURRENT_NSID]); + if (current < 0) + print_string(PRINT_ANY, "current-nsid", + "current-nsid %s ", "not-assigned"); + else + print_uint(PRINT_ANY, "current-nsid", + "current-nsid %u ", 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); + } + + /* During 'ip monitor nsid', no chance to have new nsid 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\n", + *argv); + else if (filter.target_nsid < 0) + invarg("\"target-nsid\" value should be >= 0\n", + 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\n", *argv); + else if (nsid < 0) + invarg("\"nsid\" value should be >= 0\n", + 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_uint(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\n", argv[1]); + else if (nsid < 0) + invarg("\"netnsid\" value should be >= 0\n", 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..9f860c8 --- /dev/null +++ b/ip/ipnexthop.c
@@ -0,0 +1,573 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ip nexthop + * + * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com> + */ + +#include <linux/nexthop.h> +#include <stdio.h> +#include <string.h> +#include <rt_names.h> +#include <errno.h> + +#include "utils.h" +#include "ip_common.h" + +static struct { + unsigned int flushed; + unsigned int groups; + unsigned int ifindex; + unsigned int master; + unsigned int proto; +} 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 ]\n" + "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n" + " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n" + "GROUP := [ id[,weight]>/<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) { + addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0); + if (err) + return err; + } + + if (filter.master) { + addattr32(nlh, reqlen, NHA_MASTER, filter.master); + 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, "<invalid nexthop group>"); + 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_DELROUTE) + 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); + + 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, "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, "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..ddee490 --- /dev/null +++ b/ip/ipntable.c
@@ -0,0 +1,696 @@ +/* + * 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 <http://www.gnu.org/licenses>. + */ +/* + * based on ipneigh.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <time.h> + +#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_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 <http://www.gnu.org/licenses>. + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/icmp6.h> + +#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..32bb52d --- /dev/null +++ b/ip/iproute.c
@@ -0,0 +1,2333 @@ +/* + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/in_route.h> +#include <linux/icmpv6.h> +#include <errno.h> + +#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 ]\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"); + + 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<<i)) + fprintf(fp, "lock "); + } + + switch (i) { + case RTAX_FEATURES: + print_rtax_features(fp, val); + break; + default: + print_uint(PRINT_ANY, mx_names[i], "%u ", val); + break; + + case RTAX_RTT: + case RTAX_RTTVAR: + case RTAX_RTO_MIN: + if (i == RTAX_RTT) + val /= 8; + else if (i == RTAX_RTTVAR) + val /= 4; + + if (is_json_context()) + print_uint(PRINT_JSON, mx_names[i], + NULL, val); + else { + if (val >= 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_MULTIPATH]) + print_rta_multipath(fp, r, tb[RTA_MULTIPATH]); + + 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"); + } + + 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<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) + invarg("\"mtu\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); + } else if (strcmp(*argv, "hoplimit") == 0) { + unsigned int hoplimit; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_HOPLIMIT); + NEXT_ARG(); + } + if (get_unsigned(&hoplimit, *argv, 0) || hoplimit > 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<<RTAX_ADVMSS); + NEXT_ARG(); + } + if (get_unsigned(&mss, *argv, 0)) + invarg("\"mss\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss); + } else if (matches(*argv, "reordering") == 0) { + unsigned int reord; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_REORDERING); + NEXT_ARG(); + } + if (get_unsigned(&reord, *argv, 0)) + invarg("\"reordering\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord); + } else if (strcmp(*argv, "rtt") == 0) { + unsigned int rtt; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTT); + NEXT_ARG(); + } + if (get_time_rtt(&rtt, *argv, &raw)) + invarg("\"rtt\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, + (raw) ? rtt : rtt * 8); + } else if (strcmp(*argv, "rto_min") == 0) { + unsigned int rto_min; + + NEXT_ARG(); + mxlock |= (1<<RTAX_RTO_MIN); + if (get_time_rtt(&rto_min, *argv, &raw)) + invarg("\"rto_min\" value is invalid\n", + *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN, + rto_min); + } else if (matches(*argv, "window") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_WINDOW); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"window\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); + } else if (matches(*argv, "cwnd") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_CWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"cwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); + } else if (matches(*argv, "initcwnd") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_INITCWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"initcwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), + RTAX_INITCWND, win); + } else if (matches(*argv, "initrwnd") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_INITRWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"initrwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), + RTAX_INITRWND, win); + } else if (matches(*argv, "features") == 0) { + unsigned int features = 0; + + while (argc > 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<<RTAX_RTTVAR); + NEXT_ARG(); + } + if (get_time_rtt(&win, *argv, &raw)) + invarg("\"rttvar\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, + (raw) ? win : win * 4); + } else if (matches(*argv, "ssthresh") == 0) { + unsigned int win; + + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_SSTHRESH); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"ssthresh\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); + } 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); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (strcmp(*argv, "onlink") == 0) { + req.r.rtm_flags |= RTNH_F_ONLINK; + } else if (strcmp(*argv, "nexthop") == 0) { + nhs_ok = 1; + break; + } else if (!strcmp(*argv, "nhid")) { + NEXT_ARG(); + if (get_u32(&nhid, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_NH_ID, nhid); + } else if (matches(*argv, "protocol") == 0) { + __u32 prot; + + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + } else if (matches(*argv, "table") == 0) { + __u32 tid; + + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("\"table\" value is invalid\n", *argv); + if (tid < 256) + req.r.rtm_table = tid; + else { + req.r.rtm_table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), RTA_TABLE, tid); + } + table_ok = 1; + } else if (matches(*argv, "vrf") == 0) { + __u32 tid; + + NEXT_ARG(); + tid = ipvrf_get_table(*argv); + if (tid == 0) + invarg("Invalid VRF\n", *argv); + if (tid < 256) + req.r.rtm_table = tid; + else { + req.r.rtm_table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), RTA_TABLE, tid); + } + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else if (matches(*argv, "pref") == 0) { + __u8 pref; + + NEXT_ARG(); + if (strcmp(*argv, "low") == 0) + pref = ICMPV6_ROUTER_PREF_LOW; + else if (strcmp(*argv, "medium") == 0) + pref = ICMPV6_ROUTER_PREF_MEDIUM; + else if (strcmp(*argv, "high") == 0) + pref = ICMPV6_ROUTER_PREF_HIGH; + else if (get_u8(&pref, *argv, 0)) + invarg("\"pref\" value is invalid\n", *argv); + addattr8(&req.n, sizeof(req), RTA_PREF, pref); + } else if (strcmp(*argv, "encap") == 0) { + char buf[1024]; + struct rtattr *rta = (void *)buf; + + rta->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<<RTN_BROADCAST); + } else if (matches(*argv, "cached") == 0 || + matches(*argv, "cloned") == 0) { + filter.cloned = 1; + } 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); + filter.tos = tos; + filter.tosmask = -1; + } else if (matches(*argv, "protocol") == 0) { + __u32 prot = 0; + + 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 (matches(*argv, "scope") == 0) { + __u32 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 (matches(*argv, "type") == 0) { + int type; + + NEXT_ARG(); + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("node type value is invalid\n", *argv); + filter.typemask = (1<<type); + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (strcmp(*argv, "mark") == 0) { + NEXT_ARG(); + if (get_unsigned(&mark, *argv, 0)) + invarg("invalid mark value", *argv); + filter.markmask = -1; + } 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); + filter.metric = metric; + filter.metricmask = -1; + } else if (strcmp(*argv, "via") == 0) { + int family; + + NEXT_ARG(); + family = read_family(*argv); + if (family == AF_UNSPEC) + family = dump_family; + else + NEXT_ARG(); + get_prefix(&filter.rvia, *argv, family); + } else if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.rprefsrc, *argv, dump_family); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + + NEXT_ARG(); + if (get_rt_realms_or_raw(&realm, *argv)) + invarg("invalid realms\n", *argv); + filter.realm = realm; + filter.realmmask = ~0U; + if ((filter.realm&0xFFFF) == 0 && + (*argv)[strlen(*argv) - 1] == '/') + filter.realmmask &= ~0xFFFF; + if ((filter.realm&0xFFFF0000U) == 0 && + (strchr(*argv, '/') == NULL || + (*argv)[0] == '/')) + filter.realmmask &= ~0xFFFF0000U; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, dump_family); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, dump_family); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, dump_family); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, dump_family); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, dump_family); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, dump_family); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (dump_family == AF_UNSPEC && filter.tb) + dump_family = AF_INET; + + if (id || od) { + int idx; + + if (id) { + idx = ll_name_to_index(id); + if (!idx) + return nodev(id); + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + idx = ll_name_to_index(od); + if (!idx) + return nodev(od); + filter.oif = idx; + filter.oifmask = -1; + } + } + filter.mark = mark; + + if (action == IPROUTE_FLUSH) + return iproute_flush(dump_family, filter_fn); + + if (rtnl_routedump_req(&rth, dump_family, iproute_dump_filter) < 0) { + perror("Cannot send dump request"); + return -2; + } + + new_json_obj(json); + + if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -2; + } + + delete_json_obj(); + fflush(stdout); + return 0; +} + + +static int iproute_get(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = preferred_family, + }; + char *idev = NULL; + char *odev = NULL; + struct nlmsghdr *answer; + int connected = 0; + int fib_match = 0; + int from_ok = 0; + unsigned int mark = 0; + bool address_found = false; + + iproute_reset_filter(0); + filter.cloned = 2; + + while (argc > 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..60f34a3 --- /dev/null +++ b/ip/iproute_lwtunnel.c
@@ -0,0 +1,1174 @@ +/* + * 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, <roopa@cumulusnetworks.com> + * Thomas Graf <tgraf@suug.ch> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <linux/ila.h> +#include <linux/lwtunnel.h> +#include <linux/mpls_iptunnel.h> +#include <errno.h> + +#include "rt_names.h" +#include "bpf_util.h" +#include "utils.h" +#include "ip_common.h" +#include "ila_common.h" + +#include <linux/seg6.h> +#include <linux/seg6_iptunnel.h> +#include <linux/seg6_hmac.h> +#include <linux/seg6_local.h> +#include <linux/if_tunnel.h> + +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"; + 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, "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 "<unknown>"; + + 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 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 "<invalid>"; + + return seg6_action_names[action] ?: "<unknown>"; +} + +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 ? : "<unknown>"); + 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; + + 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_uint(PRINT_ANY, "table", + "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE])); + + 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 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); + } +} + +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); + } +} + +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; + } +} + +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; +} + +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); + get_u32(&table, *argv, 0); + 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 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; + 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, "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; + 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, "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; + 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/if.h> +#include <linux/fib_rules.h> +#include <errno.h> + +#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 <david.lebrun@uclouvain.be> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> + +#include <linux/genetlink.h> +#include <linux/seg6_genl.h> +#include <linux/seg6_hmac.h> + +#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 = "<unknown>"; + } + + 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, <borkmann@redhat.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/if.h> + +#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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <linux/ip.h> +#include <linux/if_tunnel.h> + +#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 <David.Woodhouse@intel.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/if_tun.h> +#include <linux/if_arp.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <glob.h> + +#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 ? : "<NULL>"); + + 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..43366f6 --- /dev/null +++ b/ip/ipvrf.c
@@ -0,0 +1,643 @@ +/* + * 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 <dsa@cumulusnetworks.com> + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <linux/bpf.h> +#include <linux/if.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> + +#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, "<terminated?>"); + + 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(); + 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(); + 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; + } + + printf("%-16s %5u", name, tb_id); + + printf("\n"); + 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; + int n; + + n = printf("%-16s %5s\n", "Name", "Table"); + printf("%.*s\n", n-1, "-----------------------"); + for (l = linfo.head; l; l = l->next) + nvrf += ipvrf_print(&l->h); + + if (!nvrf) + printf("No VRF has been configured\n"); + } 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..32f5609 --- /dev/null +++ b/ip/ipxfrm.c
@@ -0,0 +1,1552 @@ +/* $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 <http://www.gnu.org/licenses>. + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <alloca.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <time.h> +#include <netdb.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#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, "<<Keys hidden>>"); + else if (keylen > 0) { + fprintf(fp, "0x"); + for (i = 0; i < keylen; i++) + fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]); + + if (show_stats > 0) + fprintf(fp, " (%d bits)", algo->alg_key_len); + } + + fin: + if (newline) + fprintf(fp, "%s", _SL_); +} + +static inline void xfrm_algo_print(struct xfrm_algo *algo, int type, int len, + FILE *fp, const char *prefix, bool nokeys) +{ + return __xfrm_algo_print(algo, type, len, fp, prefix, 1, nokeys); +} + +static void xfrm_aead_print(struct xfrm_algo_aead *algo, int len, + FILE *fp, const char *prefix, bool nokeys) +{ + struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8); + + memcpy(base_algo->alg_name, algo->alg_name, sizeof(base_algo->alg_name)); + base_algo->alg_key_len = algo->alg_key_len; + memcpy(base_algo->alg_key, algo->alg_key, algo->alg_key_len / 8); + + __xfrm_algo_print(base_algo, XFRMA_ALG_AEAD, len, fp, prefix, 0, + nokeys); + + fprintf(fp, " %d", algo->alg_icv_len); + + fprintf(fp, "%s", _SL_); +} + +static void xfrm_auth_trunc_print(struct xfrm_algo_auth *algo, int len, + FILE *fp, const char *prefix, bool nokeys) +{ + struct xfrm_algo *base_algo = alloca(sizeof(*base_algo) + algo->alg_key_len / 8); + + memcpy(base_algo->alg_name, algo->alg_name, sizeof(base_algo->alg_name)); + base_algo->alg_key_len = algo->alg_key_len; + memcpy(base_algo->alg_key, algo->alg_key, algo->alg_key_len / 8); + + __xfrm_algo_print(base_algo, XFRMA_ALG_AUTH_TRUNC, len, fp, prefix, 0, + nokeys); + + fprintf(fp, " %d", algo->alg_trunc_len); + + fprintf(fp, "%s", _SL_); +} + +static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len, + FILE *fp, const char *prefix) +{ + int ntmpls = len / sizeof(struct xfrm_user_tmpl); + int i; + + if (ntmpls <= 0) { + if (prefix) + fputs(prefix, fp); + fprintf(fp, "(ERROR \"tmpl\" truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + for (i = 0; i < ntmpls; i++) { + struct xfrm_user_tmpl *tmpl = &tmpls[i]; + + if (prefix) + fputs(prefix, fp); + + xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode, + tmpl->reqid, tmpl->family, 0, fp, prefix, "tmpl "); + + if (show_stats > 0 || tmpl->optional) { + if (prefix) + fputs(prefix, fp); + fprintf(fp, "\t"); + switch (tmpl->optional) { + case 0: + if (show_stats > 0) + fprintf(fp, "level required "); + break; + case 1: + fprintf(fp, "level use "); + break; + default: + fprintf(fp, "level %u ", tmpl->optional); + break; + } + + if (show_stats > 0) + fprintf(fp, "share %s ", strxf_share(tmpl->share)); + + fprintf(fp, "%s", _SL_); + } + + if (show_stats > 0) { + if (prefix) + fputs(prefix, fp); + fprintf(fp, "\t"); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_mask32(tmpl->ealgos)); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_AUTH), + strxf_mask32(tmpl->aalgos)); + fprintf(fp, "%s-mask %s", + strxf_algotype(XFRMA_ALG_COMP), + strxf_mask32(tmpl->calgos)); + + fprintf(fp, "%s", _SL_); + } + } +} + +static void xfrm_output_mark_print(struct rtattr *tb[], FILE *fp) +{ + __u32 output_mark = rta_getattr_u32(tb[XFRMA_OUTPUT_MARK]); + + fprintf(fp, "output-mark 0x%x", output_mark); +} + +int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + NEXT_ARG(); + if (get_u32(&mark->v, *argv, 0)) { + invarg("MARK value is invalid\n", *argv); + } + if (argc > 1) + NEXT_ARG(); + else { /* last entry on parse line */ + mark->m = 0xffffffff; + goto done; + } + + if (strcmp(*argv, "mask") == 0) { + NEXT_ARG(); + if (get_u32(&mark->m, *argv, 0)) { + invarg("MASK value is invalid\n", *argv); + } + } else { + mark->m = 0xffffffff; + PREV_ARG(); + } + +done: + *argcp = argc; + *argvp = argv; + + return 0; +} + +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix, bool nokeys) +{ + if (tb[XFRMA_MARK]) { + struct rtattr *rta = tb[XFRMA_MARK]; + struct xfrm_mark *m = RTA_DATA(rta); + + fprintf(fp, "\tmark %#x/%#x ", m->v, m->m); + + if (tb[XFRMA_OUTPUT_MARK]) + xfrm_output_mark_print(tb, fp); + fprintf(fp, "%s", _SL_); + } else if (tb[XFRMA_OUTPUT_MARK]) { + fprintf(fp, "\t"); + + xfrm_output_mark_print(tb, fp); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_ALG_AUTH] && !tb[XFRMA_ALG_AUTH_TRUNC]) { + struct rtattr *rta = tb[XFRMA_ALG_AUTH]; + + xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_AUTH, RTA_PAYLOAD(rta), + fp, prefix, nokeys); + } + + if (tb[XFRMA_ALG_AUTH_TRUNC]) { + struct rtattr *rta = tb[XFRMA_ALG_AUTH_TRUNC]; + + xfrm_auth_trunc_print(RTA_DATA(rta), RTA_PAYLOAD(rta), fp, + prefix, nokeys); + } + + if (tb[XFRMA_ALG_AEAD]) { + struct rtattr *rta = tb[XFRMA_ALG_AEAD]; + + xfrm_aead_print(RTA_DATA(rta), RTA_PAYLOAD(rta), fp, prefix, + nokeys); + } + + if (tb[XFRMA_ALG_CRYPT]) { + struct rtattr *rta = tb[XFRMA_ALG_CRYPT]; + + xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_CRYPT, + RTA_PAYLOAD(rta), fp, prefix, nokeys); + } + + if (tb[XFRMA_ALG_COMP]) { + struct rtattr *rta = tb[XFRMA_ALG_COMP]; + + xfrm_algo_print(RTA_DATA(rta), XFRMA_ALG_COMP, RTA_PAYLOAD(rta), + fp, prefix, nokeys); + } + + if (tb[XFRMA_ENCAP]) { + struct xfrm_encap_tmpl *e; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "encap "); + + if (RTA_PAYLOAD(tb[XFRMA_ENCAP]) < sizeof(*e)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + e = RTA_DATA(tb[XFRMA_ENCAP]); + + fprintf(fp, "type "); + switch (e->encap_type) { + case 1: + fprintf(fp, "espinudp-nonike "); + break; + case 2: + fprintf(fp, "espinudp "); + break; + default: + fprintf(fp, "%u ", e->encap_type); + break; + } + fprintf(fp, "sport %u ", ntohs(e->encap_sport)); + fprintf(fp, "dport %u ", ntohs(e->encap_dport)); + + fprintf(fp, "addr %s", + rt_addr_n2a(family, sizeof(e->encap_oa), &e->encap_oa)); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_TMPL]) { + struct rtattr *rta = tb[XFRMA_TMPL]; + + xfrm_tmpl_print(RTA_DATA(rta), + RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_COADDR]) { + const xfrm_address_t *coa; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "coa "); + + coa = RTA_DATA(tb[XFRMA_COADDR]); + if (RTA_PAYLOAD(tb[XFRMA_COADDR]) < sizeof(*coa)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + fprintf(fp, "%s", + rt_addr_n2a(family, sizeof(*coa), coa)); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_LASTUSED]) { + __u64 lastused; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "lastused "); + + if (RTA_PAYLOAD(tb[XFRMA_LASTUSED]) < sizeof(lastused)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + lastused = rta_getattr_u64(tb[XFRMA_LASTUSED]); + + fprintf(fp, "%s", strxf_time(lastused)); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_REPLAY_VAL]) { + struct xfrm_replay_state *replay; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "anti-replay context: "); + + if (RTA_PAYLOAD(tb[XFRMA_REPLAY_VAL]) < sizeof(*replay)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + replay = RTA_DATA(tb[XFRMA_REPLAY_VAL]); + fprintf(fp, "seq 0x%x, oseq 0x%x, bitmap 0x%08x", + replay->seq, replay->oseq, replay->bitmap); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_REPLAY_ESN_VAL]) { + struct xfrm_replay_state_esn *replay; + unsigned int i, j; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "anti-replay esn context:"); + + if (RTA_PAYLOAD(tb[XFRMA_REPLAY_ESN_VAL]) < sizeof(*replay)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + fprintf(fp, "%s", _SL_); + + replay = RTA_DATA(tb[XFRMA_REPLAY_ESN_VAL]); + if (prefix) + fputs(prefix, fp); + fprintf(fp, " seq-hi 0x%x, seq 0x%x, oseq-hi 0x%0x, oseq 0x%0x", + replay->seq_hi, replay->seq, replay->oseq_hi, + replay->oseq); + fprintf(fp, "%s", _SL_); + if (prefix) + fputs(prefix, fp); + fprintf(fp, " replay_window %u, bitmap-length %u", + replay->replay_window, replay->bmp_len); + for (i = replay->bmp_len, j = 0; i; i--) { + if (j++ % 8 == 0) { + fprintf(fp, "%s", _SL_); + if (prefix) + fputs(prefix, fp); + fprintf(fp, " "); + } + fprintf(fp, "%08x ", replay->bmp[i - 1]); + } + fprintf(fp, "%s", _SL_); + } + if (tb[XFRMA_OFFLOAD_DEV]) { + struct xfrm_user_offload *xuo; + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "crypto offload parameters: "); + + if (RTA_PAYLOAD(tb[XFRMA_OFFLOAD_DEV]) < sizeof(*xuo)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + xuo = (struct xfrm_user_offload *) + RTA_DATA(tb[XFRMA_OFFLOAD_DEV]); + fprintf(fp, "dev %s dir %s", ll_index_to_name(xuo->ifindex), + (xuo->flags & XFRM_OFFLOAD_INBOUND) ? "in" : "out"); + fprintf(fp, "%s", _SL_); + } + if (tb[XFRMA_IF_ID]) { + __u32 if_id = rta_getattr_u32(tb[XFRMA_IF_ID]); + + if (prefix) + fputs(prefix, fp); + fprintf(fp, "if_id %#x", if_id); + fprintf(fp, "%s", _SL_); + } +} + +static int xfrm_selector_iszero(struct xfrm_selector *s) +{ + struct xfrm_selector s0 = {}; + + return (memcmp(&s0, s, sizeof(s0)) == 0); +} + +void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo, + struct rtattr *tb[], FILE *fp, const char *prefix, + const char *title, bool nokeys) +{ + char buf[STRBUF_SIZE] = {}; + int force_spi = xfrm_xfrmproto_is_ipsec(xsinfo->id.proto); + + xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode, + xsinfo->reqid, xsinfo->family, force_spi, fp, + prefix, title); + + if (prefix) + strlcat(buf, prefix, sizeof(buf)); + strlcat(buf, "\t", sizeof(buf)); + + fputs(buf, fp); + fprintf(fp, "replay-window %u ", xsinfo->replay_window); + if (show_stats > 0) + fprintf(fp, "seq 0x%08u ", xsinfo->seq); + if (show_stats > 0 || xsinfo->flags) { + __u8 flags = xsinfo->flags; + + fprintf(fp, "flag "); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOECN, "noecn"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_DECAP_DSCP, "decap-dscp"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOPMTUDISC, "nopmtudisc"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_WILDRECV, "wildrecv"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ICMP, "icmp"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_AF_UNSPEC, "af-unspec"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ALIGN4, "align4"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ESN, "esn"); + if (flags) + fprintf(fp, "%x", flags); + } + if (show_stats > 0 && tb[XFRMA_SA_EXTRA_FLAGS]) { + __u32 extra_flags = rta_getattr_u32(tb[XFRMA_SA_EXTRA_FLAGS]); + + fprintf(fp, "extra_flag "); + XFRM_FLAG_PRINT(fp, extra_flags, + XFRM_SA_XFLAG_DONT_ENCAP_DSCP, + "dont-encap-dscp"); + if (extra_flags) + fprintf(fp, "%x", extra_flags); + } + if (show_stats > 0) + fprintf(fp, " (0x%s)", strxf_mask8(xsinfo->flags)); + fprintf(fp, "%s", _SL_); + + xfrm_xfrma_print(tb, xsinfo->family, fp, buf, nokeys); + + if (!xfrm_selector_iszero(&xsinfo->sel)) { + char sbuf[STRBUF_SIZE]; + + memcpy(sbuf, buf, sizeof(sbuf)); + strlcat(sbuf, "sel ", sizeof(sbuf)); + + xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, sbuf); + } + + if (show_stats > 0) { + xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, buf); + xfrm_stats_print(&xsinfo->stats, fp, buf); + } + + if (tb[XFRMA_SEC_CTX]) { + struct xfrm_user_sec_ctx *sctx; + + fprintf(fp, "\tsecurity context "); + + if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx)) + fprintf(fp, "(ERROR truncated)"); + + sctx = RTA_DATA(tb[XFRMA_SEC_CTX]); + + fprintf(fp, "%s %s", (char *)(sctx + 1), _SL_); + } + +} + +void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo, + struct rtattr *tb[], FILE *fp, const char *prefix, + const char *title) +{ + char buf[STRBUF_SIZE] = {}; + + xfrm_selector_print(&xpinfo->sel, preferred_family, fp, title); + + if (tb[XFRMA_SEC_CTX]) { + struct xfrm_user_sec_ctx *sctx; + + fprintf(fp, "\tsecurity context "); + + if (RTA_PAYLOAD(tb[XFRMA_SEC_CTX]) < sizeof(*sctx)) + fprintf(fp, "(ERROR truncated)"); + + sctx = RTA_DATA(tb[XFRMA_SEC_CTX]); + + fprintf(fp, "%s ", (char *)(sctx + 1)); + fprintf(fp, "%s", _SL_); + } + + if (prefix) + strlcat(buf, prefix, sizeof(buf)); + strlcat(buf, "\t", sizeof(buf)); + + fputs(buf, fp); + if (xpinfo->dir >= XFRM_POLICY_MAX) { + xpinfo->dir -= XFRM_POLICY_MAX; + fprintf(fp, "socket "); + } else + fprintf(fp, "dir "); + + switch (xpinfo->dir) { + case XFRM_POLICY_IN: + fprintf(fp, "in"); + break; + case XFRM_POLICY_OUT: + fprintf(fp, "out"); + break; + case XFRM_POLICY_FWD: + fprintf(fp, "fwd"); + break; + default: + fprintf(fp, "%u", xpinfo->dir); + break; + } + fprintf(fp, " "); + + switch (xpinfo->action) { + case XFRM_POLICY_ALLOW: + if (show_stats > 0) + fprintf(fp, "action allow "); + break; + case XFRM_POLICY_BLOCK: + fprintf(fp, "action block "); + break; + default: + fprintf(fp, "action %u ", xpinfo->action); + break; + } + + if (show_stats) + fprintf(fp, "index %u ", xpinfo->index); + fprintf(fp, "priority %u ", xpinfo->priority); + + if (tb[XFRMA_POLICY_TYPE]) { + struct xfrm_userpolicy_type *upt; + + fprintf(fp, "ptype "); + + if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) + fprintf(fp, "(ERROR truncated)"); + + upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]); + fprintf(fp, "%s ", strxf_ptype(upt->type)); + } + + if (show_stats > 0) + fprintf(fp, "share %s ", strxf_share(xpinfo->share)); + + if (show_stats > 0 || xpinfo->flags) { + __u8 flags = xpinfo->flags; + + fprintf(fp, "flag "); + XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_LOCALOK, "localok"); + XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_ICMP, "icmp"); + if (flags) + fprintf(fp, "%x", flags); + } + if (show_stats > 0) + fprintf(fp, " (0x%s)", strxf_mask8(xpinfo->flags)); + fprintf(fp, "%s", _SL_); + + if (show_stats > 0) + xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, buf); + + xfrm_xfrma_print(tb, xpinfo->sel.family, fp, buf, false); +} + +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst = {}; + inet_prefix src = {}; + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("value after \"src\" has an unrecognized address family", *argv); + if (family) + *family = src.family; + + memcpy(saddr, &src.data, sizeof(*saddr)); + + filter.id_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("value after \"dst\" has an unrecognized address family", *argv); + if (family) + *family = dst.family; + + memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); + + filter.id_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "proto") == 0) { + int ret; + + NEXT_ARG(); + + ret = xfrm_xfrmproto_getbyname(*argv); + if (ret < 0) + invarg("XFRM-PROTO value is invalid", *argv); + + id->proto = (__u8)ret; + + filter.id_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "spi") == 0) { + NEXT_ARG(); + if (get_be32(&id->spi, *argv, 0)) + invarg("SPI value is invalid", *argv); + + filter.id_spi_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between values after \"src\" and \"dst\"", *argv); + + if (id->spi && id->proto) { + if (xfrm_xfrmproto_is_ro(id->proto)) { + fprintf(stderr, "\"spi\" is invalid with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(id->proto)); + exit(1); + } else if (id->proto == IPPROTO_COMP && ntohl(id->spi) >= 0x10000) { + fprintf(stderr, "SPI value is too large with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(id->proto)); + exit(1); + } + } + + if (loose == 0 && id->proto == 0) + missarg("XFRM-PROTO"); + if (argc == *argcp) + missarg("ID"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (matches(*argv, "transport") == 0) + *mode = XFRM_MODE_TRANSPORT; + else if (matches(*argv, "tunnel") == 0) + *mode = XFRM_MODE_TUNNEL; + else if (matches(*argv, "ro") == 0) + *mode = XFRM_MODE_ROUTEOPTIMIZATION; + else if (matches(*argv, "in_trigger") == 0) + *mode = XFRM_MODE_IN_TRIGGER; + else if (matches(*argv, "beet") == 0) + *mode = XFRM_MODE_BEET; + else + invarg("MODE value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "espinudp-nonike") == 0) + *type = 1; + else if (strcmp(*argv, "espinudp") == 0) + *type = 2; + else + invarg("ENCAP-TYPE value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +/* NOTE: reqid is used by host-byte order */ +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (get_u32(reqid, *argv, 0)) + invarg("REQID value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *sportp = NULL; + char *dportp = NULL; + char *typep = NULL; + char *codep = NULL; + char *grekey = NULL; + + while (1) { + if (strcmp(*argv, "proto") == 0) { + __u8 upspec; + + NEXT_ARG(); + + if (strcmp(*argv, "any") == 0) + upspec = 0; + else { + struct protoent *pp; + + pp = getprotobyname(*argv); + if (pp) + upspec = pp->p_proto; + else { + if (get_u8(&upspec, *argv, 0)) + invarg("PROTO value is invalid", *argv); + } + } + sel->proto = upspec; + + filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sport") == 0) { + sportp = *argv; + + NEXT_ARG(); + + if (get_be16(&sel->sport, *argv, 0)) + invarg("value after \"sport\" is invalid", *argv); + if (sel->sport) + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "dport") == 0) { + dportp = *argv; + + NEXT_ARG(); + + if (get_be16(&sel->dport, *argv, 0)) + invarg("value after \"dport\" is invalid", *argv); + if (sel->dport) + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "type") == 0) { + typep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0) || + (sel->sport & ~((__u16)0xff))) + invarg("value after \"type\" is invalid", *argv); + sel->sport = htons(sel->sport); + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + + } else if (strcmp(*argv, "code") == 0) { + codep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0) || + (sel->dport & ~((__u16)0xff))) + invarg("value after \"code\" is invalid", *argv); + sel->dport = htons(sel->dport); + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "key") == 0) { + unsigned int uval; + + grekey = *argv; + + NEXT_ARG(); + + if (strchr(*argv, '.')) + uval = htonl(get_addr32(*argv)); + else { + if (get_unsigned(&uval, *argv, 0) < 0) { + fprintf(stderr, "value after \"key\" is invalid\n"); + exit(-1); + } + } + + sel->sport = htons(uval >> 16); + sel->dport = htons(uval & 0xffff); + sel->sport_mask = ~((__u16)0); + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + if (argc == *argcp) + missarg("UPSPEC"); + if (sportp || dportp) { + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + case IPPROTO_IP: /* to allow shared SA for different protocols */ + break; + default: + fprintf(stderr, "\"sport\" and \"dport\" are invalid with PROTO value \"%s\"\n", strxf_proto(sel->proto)); + exit(1); + } + } + if (typep || codep) { + switch (sel->proto) { + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + case IPPROTO_MH: + break; + default: + fprintf(stderr, "\"type\" and \"code\" are invalid with PROTO value \"%s\"\n", strxf_proto(sel->proto)); + exit(1); + } + } + if (grekey) { + switch (sel->proto) { + case IPPROTO_GRE: + break; + default: + fprintf(stderr, "\"key\" is invalid with PROTO value \"%s\"\n", strxf_proto(sel->proto)); + exit(1); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst = {}; + inet_prefix src = {}; + char *upspecp = NULL; + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("value after \"src\" has an unrecognized address family", *argv); + sel->family = src.family; + + memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); + sel->prefixlen_s = src.bitlen; + + filter.sel_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("value after \"dst\" has an unrecognized address family", *argv); + sel->family = dst.family; + + memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); + sel->prefixlen_d = dst.bitlen; + + filter.sel_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "dev") == 0) { + int ifindex; + + NEXT_ARG(); + + if (strcmp(*argv, "none") == 0) + ifindex = 0; + else { + ifindex = ll_name_to_index(*argv); + if (ifindex <= 0) + invarg("DEV value is invalid", *argv); + } + sel->ifindex = ifindex; + + filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (upspecp) { + PREV_ARG(); /* back track */ + break; + } else { + upspecp = *argv; + xfrm_selector_upspec_parse(sel, &argc, &argv); + } + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between values after \"src\" and \"dst\"", *argv); + + if (argc == *argcp) + missarg("SELECTOR"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (strcmp(*argv, "time-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0); + if (ret) + invarg("value after \"time-soft\" is invalid", *argv); + } else if (strcmp(*argv, "time-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0); + if (ret) + invarg("value after \"time-hard\" is invalid", *argv); + } else if (strcmp(*argv, "time-use-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0); + if (ret) + invarg("value after \"time-use-soft\" is invalid", *argv); + } else if (strcmp(*argv, "time-use-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0); + if (ret) + invarg("value after \"time-use-hard\" is invalid", *argv); + } else if (strcmp(*argv, "byte-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_byte_limit, *argv, 0); + if (ret) + invarg("value after \"byte-soft\" is invalid", *argv); + } else if (strcmp(*argv, "byte-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_byte_limit, *argv, 0); + if (ret) + invarg("value after \"byte-hard\" is invalid", *argv); + } else if (strcmp(*argv, "packet-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_packet_limit, *argv, 0); + if (ret) + invarg("value after \"packet-soft\" is invalid", *argv); + } else if (strcmp(*argv, "packet-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_packet_limit, *argv, 0); + if (ret) + invarg("value after \"packet-hard\" is invalid", *argv); + } else + invarg("LIMIT value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int do_xfrm(int argc, char **argv) +{ + memset(&filter, 0, sizeof(filter)); + + if (argc < 1) + usage(); + + if (matches(*argv, "state") == 0 || + matches(*argv, "sa") == 0) + return do_xfrm_state(argc-1, argv+1); + else if (matches(*argv, "policy") == 0) + return do_xfrm_policy(argc-1, argv+1); + else if (matches(*argv, "monitor") == 0) + return do_xfrm_monitor(argc-1, argv+1); + else if (matches(*argv, "help") == 0) { + usage(); + fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv); + exit(-1); + } + usage(); +}
diff --git a/ip/link_gre.c b/ip/link_gre.c new file mode 100644 index 0000000..15beb73 --- /dev/null +++ b/ip/link_gre.c
@@ -0,0 +1,571 @@ +/* + * link_gre.c gre driver module + * + * 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: Herbert Xu <herbert@gondor.apana.org.au> + * + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f) +{ + fprintf(f, + "Usage: ... %-9s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ [no][i|o]seq ]\n" + " [ [i|o]key KEY | no[i|o]key ]\n" + " [ [no][i|o]csum ]\n" + " [ ttl TTL ]\n" + " [ tos TOS ]\n" + " [ [no]pmtudisc ]\n" + " [ [no]ignore-df ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + " [ external ]\n" + " [ noencap ]\n" + " [ encap { fou | gue | none } ]\n" + " [ encap-sport PORT ]\n" + " [ encap-dport PORT ]\n" + " [ [no]encap-csum ]\n" + " [ [no]encap-csum6 ]\n" + " [ [no]encap-remcsum ]\n" + " [ erspan_ver version ]\n" + " [ erspan IDX ]\n" + " [ erspan_dir { ingress | egress } ]\n" + " [ erspan_hwid hwid ]\n" + "\n" + "Where: ADDR := { IP_ADDRESS | any }\n" + " TOS := { NUMBER | inherit }\n" + " TTL := { 1..255 | inherit }\n" + " KEY := { DOTTED_QUAD | NUMBER }\n" + " MARK := { 0x0..0xffffffff }\n", + lu->id); +} + +static int gre_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *greinfo[IFLA_GRE_MAX + 1]; + int len; + __u16 iflags = 0; + __u16 oflags = 0; + __be32 ikey = 0; + __be32 okey = 0; + inet_prefix saddr, daddr; + __u8 pmtudisc = 1; + __u8 ignore_df = 0; + __u8 tos = 0; + __u8 ttl = 0; + __u32 link = 0; + __u16 encaptype = 0; + __u16 encapflags = 0; + __u16 encapsport = 0; + __u16 encapdport = 0; + __u8 metadata = 0; + __u32 fwmark = 0; + __u32 erspan_idx = 0; + __u8 erspan_ver = 0; + __u8 erspan_dir = 0; + __u16 erspan_hwid = 0; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(greinfo, IFLA_GRE_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = greinfo[IFLA_GRE_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET)) + goto get_failed; + + rta = greinfo[IFLA_GRE_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET)) + goto get_failed; + + if (greinfo[IFLA_GRE_IKEY]) + ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]); + + if (greinfo[IFLA_GRE_OKEY]) + okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]); + + if (greinfo[IFLA_GRE_IFLAGS]) + iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]); + + if (greinfo[IFLA_GRE_OFLAGS]) + oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]); + + if (greinfo[IFLA_GRE_PMTUDISC]) + pmtudisc = rta_getattr_u8( + greinfo[IFLA_GRE_PMTUDISC]); + + if (greinfo[IFLA_GRE_IGNORE_DF]) + ignore_df = + !!rta_getattr_u8(greinfo[IFLA_GRE_IGNORE_DF]); + + if (greinfo[IFLA_GRE_TOS]) + tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]); + + if (greinfo[IFLA_GRE_TTL]) + ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]); + + if (greinfo[IFLA_GRE_LINK]) + link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]); + + if (greinfo[IFLA_GRE_ENCAP_TYPE]) + encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]); + + if (greinfo[IFLA_GRE_ENCAP_FLAGS]) + encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]); + + if (greinfo[IFLA_GRE_ENCAP_SPORT]) + encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]); + + if (greinfo[IFLA_GRE_ENCAP_DPORT]) + encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]); + + if (greinfo[IFLA_GRE_COLLECT_METADATA]) + metadata = 1; + + if (greinfo[IFLA_GRE_FWMARK]) + fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]); + + if (greinfo[IFLA_GRE_ERSPAN_INDEX]) + erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]); + + if (greinfo[IFLA_GRE_ERSPAN_VER]) + erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]); + + if (greinfo[IFLA_GRE_ERSPAN_DIR]) + erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]); + + if (greinfo[IFLA_GRE_ERSPAN_HWID]) + erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]); + + free(answer); + } + + while (argc > 0) { + if (!matches(*argv, "key")) { + NEXT_ARG(); + iflags |= GRE_KEY; + oflags |= GRE_KEY; + ikey = okey = tnl_parse_key("key", *argv); + } else if (!matches(*argv, "nokey")) { + iflags &= ~GRE_KEY; + oflags &= ~GRE_KEY; + ikey = okey = 0; + } else if (!matches(*argv, "ikey")) { + NEXT_ARG(); + iflags |= GRE_KEY; + ikey = tnl_parse_key("ikey", *argv); + } else if (!matches(*argv, "noikey")) { + iflags &= ~GRE_KEY; + ikey = 0; + } else if (!matches(*argv, "okey")) { + NEXT_ARG(); + oflags |= GRE_KEY; + okey = tnl_parse_key("okey", *argv); + } else if (!matches(*argv, "nookey")) { + oflags &= ~GRE_KEY; + okey = 0; + } else if (!matches(*argv, "seq")) { + iflags |= GRE_SEQ; + oflags |= GRE_SEQ; + } else if (!matches(*argv, "noseq")) { + iflags &= ~GRE_SEQ; + oflags &= ~GRE_SEQ; + } else if (!matches(*argv, "iseq")) { + iflags |= GRE_SEQ; + } else if (!matches(*argv, "noiseq")) { + iflags &= ~GRE_SEQ; + } else if (!matches(*argv, "oseq")) { + oflags |= GRE_SEQ; + } else if (!matches(*argv, "nooseq")) { + oflags &= ~GRE_SEQ; + } else if (!matches(*argv, "csum")) { + iflags |= GRE_CSUM; + oflags |= GRE_CSUM; + } else if (!matches(*argv, "nocsum")) { + iflags &= ~GRE_CSUM; + oflags &= ~GRE_CSUM; + } else if (!matches(*argv, "icsum")) { + iflags |= GRE_CSUM; + } else if (!matches(*argv, "noicsum")) { + iflags &= ~GRE_CSUM; + } else if (!matches(*argv, "ocsum")) { + oflags |= GRE_CSUM; + } else if (!matches(*argv, "noocsum")) { + oflags &= ~GRE_CSUM; + } else if (!matches(*argv, "nopmtudisc")) { + pmtudisc = 0; + } else if (!matches(*argv, "pmtudisc")) { + pmtudisc = 1; + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET); + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET); + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (!matches(*argv, "ttl") || + !matches(*argv, "hoplimit") || + !matches(*argv, "hlim")) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_u8(&ttl, *argv, 0)) + invarg("invalid TTL\n", *argv); + } else + ttl = 0; + } else if (!matches(*argv, "tos") || + !matches(*argv, "tclass") || + !matches(*argv, "dsfield")) { + __u32 uval; + + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + tos = uval; + } else + tos = 1; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; + } else if (strcmp(*argv, "ignore-df") == 0) { + ignore_df = 1; + } else if (strcmp(*argv, "noignore-df") == 0) { + /* + *only the lsb is significant, use 2 for presence + */ + ignore_df = 2; + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + } else if (strcmp(*argv, "erspan") == 0) { + NEXT_ARG(); + if (get_u32(&erspan_idx, *argv, 0)) + invarg("invalid erspan index\n", *argv); + if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0) + invarg("erspan index must be > 0 and <= 20-bit\n", *argv); + } else if (strcmp(*argv, "erspan_ver") == 0) { + NEXT_ARG(); + if (get_u8(&erspan_ver, *argv, 0)) + invarg("invalid erspan version\n", *argv); + if (erspan_ver != 1 && erspan_ver != 2) + invarg("erspan version must be 1 or 2\n", *argv); + } else if (strcmp(*argv, "erspan_dir") == 0) { + NEXT_ARG(); + if (matches(*argv, "ingress") == 0) + erspan_dir = 0; + else if (matches(*argv, "egress") == 0) + erspan_dir = 1; + else + invarg("Invalid erspan direction.", *argv); + } else if (strcmp(*argv, "erspan_hwid") == 0) { + NEXT_ARG(); + if (get_u16(&erspan_hwid, *argv, 0)) + invarg("invalid erspan hwid\n", *argv); + } else { + gre_print_help(lu, argc, argv, stderr); + return -1; + } + argc--; argv++; + } + + if (is_addrtype_inet_multi(&daddr)) { + if (!ikey) { + ikey = daddr.data[0]; + iflags |= GRE_KEY; + } + if (!okey) { + okey = daddr.data[0]; + oflags |= GRE_KEY; + } + if (!is_addrtype_inet_not_unspec(&saddr)) { + fprintf(stderr, + "A broadcast tunnel requires a source address.\n"); + return -1; + } + } + + if (metadata) { + addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0); + return 0; + } + + addattr32(n, 1024, IFLA_GRE_IKEY, ikey); + addattr32(n, 1024, IFLA_GRE_OKEY, okey); + addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2); + addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2); + if (is_addrtype_inet_not_unspec(&saddr)) + addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen); + if (is_addrtype_inet_not_unspec(&daddr)) + addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen); + addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1); + if (ignore_df) + addattr8(n, 1024, IFLA_GRE_IGNORE_DF, ignore_df & 1); + addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); + if (link) + addattr32(n, 1024, IFLA_GRE_LINK, link); + addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); + addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark); + if (erspan_ver) { + addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver); + if (erspan_ver == 1 && erspan_idx != 0) { + addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx); + } else if (erspan_ver == 2) { + addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir); + addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid); + } + } + addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport)); + + return 0; +} + +static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + __u16 iflags = 0; + __u16 oflags = 0; + __u8 ttl = 0; + __u8 tos = 0; + + if (!tb) + return; + + if (tb[IFLA_GRE_COLLECT_METADATA]) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET); + tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET); + + if (tb[IFLA_GRE_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_GRE_TTL]) + ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); + else + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + + if (tb[IFLA_GRE_TOS]) + tos = rta_getattr_u8(tb[IFLA_GRE_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_GRE_PMTUDISC]) { + if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC])) + print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false); + else + print_bool(PRINT_JSON, "pmtudisc", NULL, true); + } + + if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF])) + print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true); + + if (tb[IFLA_GRE_IFLAGS]) + iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]); + + if (tb[IFLA_GRE_OFLAGS]) + oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]); + + if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) { + inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2)); + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); + } + + if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) { + inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2)); + print_string(PRINT_ANY, "okey", "okey %s ", s2); + } + + if (iflags & GRE_SEQ) + print_bool(PRINT_ANY, "iseq", "iseq ", true); + if (oflags & GRE_SEQ) + print_bool(PRINT_ANY, "oseq", "oseq ", true); + if (iflags & GRE_CSUM) + print_bool(PRINT_ANY, "icsum", "icsum ", true); + if (oflags & GRE_CSUM) + print_bool(PRINT_ANY, "ocsum", "ocsum ", true); + + if (tb[IFLA_GRE_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } + + if (tb[IFLA_GRE_ERSPAN_INDEX]) { + __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]); + + print_uint(PRINT_ANY, + "erspan_index", "erspan_index %u ", erspan_idx); + } + + if (tb[IFLA_GRE_ERSPAN_VER]) { + __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]); + + print_uint(PRINT_ANY, + "erspan_ver", "erspan_ver %u ", erspan_ver); + } + + if (tb[IFLA_GRE_ERSPAN_DIR]) { + __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]); + + if (erspan_dir == 0) + print_string(PRINT_ANY, "erspan_dir", + "erspan_dir ingress ", NULL); + else + print_string(PRINT_ANY, "erspan_dir", + "erspan_dir egress ", NULL); + } + + if (tb[IFLA_GRE_ERSPAN_HWID]) { + __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]); + + print_0xhex(PRINT_ANY, + "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid); + } + + tnl_print_encap(tb, + IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, + IFLA_GRE_ENCAP_DPORT); +} + +struct link_util gre_link_util = { + .id = "gre", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +}; + +struct link_util gretap_link_util = { + .id = "gretap", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +}; + +struct link_util erspan_link_util = { + .id = "erspan", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +};
diff --git a/ip/link_gre6.c b/ip/link_gre6.c new file mode 100644 index 0000000..9d1741b --- /dev/null +++ b/ip/link_gre6.c
@@ -0,0 +1,639 @@ +/* + * link_gre6.c gre driver module + * + * 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: Dmitry Kozlov <xeb@mail.ru> + * + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include <linux/ip6_tunnel.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000) +#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF) + +#define DEFAULT_TNL_HOP_LIMIT (64) + +static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f) +{ + fprintf(f, + "Usage: ... %-9s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ [no][i|o]seq ]\n" + " [ [i|o]key KEY | no[i|o]key ]\n" + " [ [no][i|o]csum ]\n" + " [ hoplimit TTL ]\n" + " [ encaplimit ELIM ]\n" + " [ tclass TCLASS ]\n" + " [ flowlabel FLOWLABEL ]\n" + " [ dscp inherit ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + " [ [no]allow-localremote ]\n" + " [ external ]\n" + " [ noencap ]\n" + " [ encap { fou | gue | none } ]\n" + " [ encap-sport PORT ]\n" + " [ encap-dport PORT ]\n" + " [ [no]encap-csum ]\n" + " [ [no]encap-csum6 ]\n" + " [ [no]encap-remcsum ]\n" + " [ erspan_ver version ]\n" + " [ erspan IDX ]\n" + " [ erspan_dir { ingress | egress } ]\n" + " [ erspan_hwid hwid ]\n" + "\n" + "Where: ADDR := IPV6_ADDRESS\n" + " TTL := { 0..255 } (default=%d)\n" + " KEY := { DOTTED_QUAD | NUMBER }\n" + " ELIM := { none | 0..255 }(default=%d)\n" + " TCLASS := { 0x0..0xff | inherit }\n" + " FLOWLABEL := { 0x0..0xfffff | inherit }\n" + " MARK := { 0x0..0xffffffff | inherit }\n", + lu->id, + DEFAULT_TNL_HOP_LIMIT, IPV6_DEFAULT_TNL_ENCAP_LIMIT); +} + +static int gre_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *greinfo[IFLA_GRE_MAX + 1]; + int len; + __u16 iflags = 0; + __u16 oflags = 0; + __be32 ikey = 0; + __be32 okey = 0; + inet_prefix saddr, daddr; + __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT; + __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + __u32 flowinfo = 0; + __u32 flags = 0; + __u32 link = 0; + __u16 encaptype = 0; + __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6; + __u16 encapsport = 0; + __u16 encapdport = 0; + __u8 metadata = 0; + __u32 fwmark = 0; + __u32 erspan_idx = 0; + __u8 erspan_ver = 0; + __u8 erspan_dir = 0; + __u16 erspan_hwid = 0; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(greinfo, IFLA_GRE_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = greinfo[IFLA_GRE_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET6)) + goto get_failed; + + rta = greinfo[IFLA_GRE_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET6)) + goto get_failed; + + if (greinfo[IFLA_GRE_IKEY]) + ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]); + + if (greinfo[IFLA_GRE_OKEY]) + okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]); + + if (greinfo[IFLA_GRE_IFLAGS]) + iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]); + + if (greinfo[IFLA_GRE_OFLAGS]) + oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]); + + if (greinfo[IFLA_GRE_TTL]) + hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]); + + if (greinfo[IFLA_GRE_LINK]) + link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]); + + if (greinfo[IFLA_GRE_ENCAP_LIMIT]) + encap_limit = rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]); + + if (greinfo[IFLA_GRE_FLOWINFO]) + flowinfo = rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]); + + if (greinfo[IFLA_GRE_FLAGS]) + flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]); + + if (greinfo[IFLA_GRE_ENCAP_TYPE]) + encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]); + + if (greinfo[IFLA_GRE_ENCAP_FLAGS]) + encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]); + + if (greinfo[IFLA_GRE_ENCAP_SPORT]) + encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]); + + if (greinfo[IFLA_GRE_ENCAP_DPORT]) + encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]); + + if (greinfo[IFLA_GRE_COLLECT_METADATA]) + metadata = 1; + + if (greinfo[IFLA_GRE_FWMARK]) + fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]); + + if (greinfo[IFLA_GRE_ERSPAN_INDEX]) + erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]); + + if (greinfo[IFLA_GRE_ERSPAN_VER]) + erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]); + + if (greinfo[IFLA_GRE_ERSPAN_DIR]) + erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]); + + if (greinfo[IFLA_GRE_ERSPAN_HWID]) + erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]); + + free(answer); + } + + while (argc > 0) { + if (!matches(*argv, "key")) { + NEXT_ARG(); + iflags |= GRE_KEY; + oflags |= GRE_KEY; + ikey = okey = tnl_parse_key("key", *argv); + } else if (!matches(*argv, "nokey")) { + iflags &= ~GRE_KEY; + oflags &= ~GRE_KEY; + ikey = okey = 0; + } else if (!matches(*argv, "ikey")) { + NEXT_ARG(); + iflags |= GRE_KEY; + ikey = tnl_parse_key("ikey", *argv); + } else if (!matches(*argv, "noikey")) { + iflags &= ~GRE_KEY; + ikey = 0; + } else if (!matches(*argv, "okey")) { + NEXT_ARG(); + oflags |= GRE_KEY; + okey = tnl_parse_key("okey", *argv); + } else if (!matches(*argv, "nookey")) { + oflags &= ~GRE_KEY; + okey = 0; + } else if (!matches(*argv, "seq")) { + iflags |= GRE_SEQ; + oflags |= GRE_SEQ; + } else if (!matches(*argv, "noseq")) { + iflags &= ~GRE_SEQ; + oflags &= ~GRE_SEQ; + } else if (!matches(*argv, "iseq")) { + iflags |= GRE_SEQ; + } else if (!matches(*argv, "noiseq")) { + iflags &= ~GRE_SEQ; + } else if (!matches(*argv, "oseq")) { + oflags |= GRE_SEQ; + } else if (!matches(*argv, "nooseq")) { + oflags &= ~GRE_SEQ; + } else if (!matches(*argv, "csum")) { + iflags |= GRE_CSUM; + oflags |= GRE_CSUM; + } else if (!matches(*argv, "nocsum")) { + iflags &= ~GRE_CSUM; + oflags &= ~GRE_CSUM; + } else if (!matches(*argv, "icsum")) { + iflags |= GRE_CSUM; + } else if (!matches(*argv, "noicsum")) { + iflags &= ~GRE_CSUM; + } else if (!matches(*argv, "ocsum")) { + oflags |= GRE_CSUM; + } else if (!matches(*argv, "noocsum")) { + oflags &= ~GRE_CSUM; + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET6); + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET6); + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (!matches(*argv, "ttl") || + !matches(*argv, "hoplimit") || + !matches(*argv, "hlim")) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_u8(&hop_limit, *argv, 0)) + invarg("invalid HLIM\n", *argv); + } else + hop_limit = 0; + } else if (!matches(*argv, "tos") || + !matches(*argv, "tclass") || + !matches(*argv, "dsfield")) { + __u8 uval; + + NEXT_ARG(); + flowinfo &= ~IP6_FLOWINFO_TCLASS; + if (strcmp(*argv, "inherit") == 0) + flags |= IP6_TNL_F_USE_ORIG_TCLASS; + else { + if (get_u8(&uval, *argv, 16)) + invarg("invalid TClass", *argv); + flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS; + flags &= ~IP6_TNL_F_USE_ORIG_TCLASS; + } + } else if (strcmp(*argv, "flowlabel") == 0 || + strcmp(*argv, "fl") == 0) { + __u32 uval; + + NEXT_ARG(); + flowinfo &= ~IP6_FLOWINFO_FLOWLABEL; + if (strcmp(*argv, "inherit") == 0) + 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); + flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL; + flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; + } + } else if (strcmp(*argv, "dscp") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) + invarg("not inherit", *argv); + flags |= IP6_TNL_F_RCV_DSCP_COPY; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") == 0) { + flags |= IP6_TNL_F_USE_ORIG_FWMARK; + fwmark = 0; + } else { + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + flags &= ~IP6_TNL_F_USE_ORIG_FWMARK; + } + } else if (strcmp(*argv, "allow-localremote") == 0) { + flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "noallow-localremote") == 0) { + flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "encaplimit") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "none") == 0) { + flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + } else { + __u8 uval; + + if (get_u8(&uval, *argv, 0)) + invarg("invalid ELIM", *argv); + encap_limit = uval; + flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } else if (strcmp(*argv, "erspan") == 0) { + NEXT_ARG(); + if (get_u32(&erspan_idx, *argv, 0)) + invarg("invalid erspan index\n", *argv); + if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0) + invarg("erspan index must be > 0 and <= 20-bit\n", *argv); + } else if (strcmp(*argv, "erspan_ver") == 0) { + NEXT_ARG(); + if (get_u8(&erspan_ver, *argv, 0)) + invarg("invalid erspan version\n", *argv); + if (erspan_ver != 1 && erspan_ver != 2) + invarg("erspan version must be 1 or 2\n", *argv); + } else if (strcmp(*argv, "erspan_dir") == 0) { + NEXT_ARG(); + if (matches(*argv, "ingress") == 0) + erspan_dir = 0; + else if (matches(*argv, "egress") == 0) + erspan_dir = 1; + else + invarg("Invalid erspan direction.", *argv); + } else if (strcmp(*argv, "erspan_hwid") == 0) { + NEXT_ARG(); + if (get_u16(&erspan_hwid, *argv, 0)) + invarg("invalid erspan hwid\n", *argv); + } else { + gre_print_help(lu, argc, argv, stderr); + return -1; + } + argc--; argv++; + } + + if (metadata) { + addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0); + return 0; + } + + addattr32(n, 1024, IFLA_GRE_IKEY, ikey); + addattr32(n, 1024, IFLA_GRE_OKEY, okey); + addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2); + addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2); + if (is_addrtype_inet_not_unspec(&saddr)) + addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen); + if (is_addrtype_inet_not_unspec(&daddr)) + addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen); + if (link) + addattr32(n, 1024, IFLA_GRE_LINK, link); + addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1); + addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1); + addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4); + addattr32(n, 1024, IFLA_GRE_FLAGS, flags); + addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark); + if (erspan_ver) { + addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver); + if (erspan_ver == 1 && erspan_idx != 0) { + addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx); + } else if (erspan_ver == 2) { + addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir); + addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid); + } + } + addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport)); + + return 0; +} + +static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + __u16 iflags = 0; + __u16 oflags = 0; + __u32 flags = 0; + __u32 flowinfo = 0; + __u8 ttl = 0; + + if (!tb) + return; + + if (tb[IFLA_GRE_COLLECT_METADATA]) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + if (tb[IFLA_GRE_FLAGS]) + flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]); + + if (tb[IFLA_GRE_FLOWINFO]) + flowinfo = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]); + + tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET6); + tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET6); + + if (tb[IFLA_GRE_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_GRE_TTL]) + ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "hoplimit %u ", ttl); + else + print_string(PRINT_FP, NULL, "hoplimit %s ", "inherit"); + + if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) { + print_bool(PRINT_ANY, + "ip6_tnl_f_ign_encap_limit", + "encaplimit none ", + true); + } else if (tb[IFLA_GRE_ENCAP_LIMIT]) { + __u8 val = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]); + + print_uint(PRINT_ANY, "encap_limit", "encaplimit %u ", val); + } + + if (flags & IP6_TNL_F_USE_ORIG_TCLASS) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_tclass", + "tclass inherit ", + true); + } else if (tb[IFLA_GRE_FLOWINFO]) { + __u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20; + + snprintf(s2, sizeof(s2), "0x%02x", val); + print_string(PRINT_ANY, "tclass", "tclass %s ", s2); + } + + if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_flowlabel", + "flowlabel inherit ", + true); + } else if (tb[IFLA_GRE_FLOWINFO]) { + __u32 val = ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL); + + snprintf(s2, sizeof(s2), "0x%05x", val); + print_string(PRINT_ANY, "flowlabel", "flowlabel %s ", s2); + } + + if (flags & IP6_TNL_F_RCV_DSCP_COPY) + print_bool(PRINT_ANY, + "ip6_tnl_f_rcv_dscp_copy", + "dscp inherit ", + true); + + if (tb[IFLA_GRE_IFLAGS]) + iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]); + + if (tb[IFLA_GRE_OFLAGS]) + oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]); + + if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) { + inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2)); + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); + } + + if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) { + inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2)); + print_string(PRINT_ANY, "okey", "okey %s ", s2); + } + + if (iflags & GRE_SEQ) + print_bool(PRINT_ANY, "iseq", "iseq ", true); + if (oflags & GRE_SEQ) + print_bool(PRINT_ANY, "oseq", "oseq ", true); + if (iflags & GRE_CSUM) + print_bool(PRINT_ANY, "icsum", "icsum ", true); + if (oflags & GRE_CSUM) + print_bool(PRINT_ANY, "ocsum", "ocsum ", true); + + if (flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) + print_bool(PRINT_ANY, + "ip6_tnl_f_allow_local_remote", + "allow-localremote ", + true); + + if (flags & IP6_TNL_F_USE_ORIG_FWMARK) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_fwmark", + "fwmark inherit ", + true); + } else if (tb[IFLA_GRE_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } + + if (tb[IFLA_GRE_ERSPAN_INDEX]) { + __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]); + + print_uint(PRINT_ANY, + "erspan_index", "erspan_index %u ", erspan_idx); + } + + if (tb[IFLA_GRE_ERSPAN_VER]) { + __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]); + + print_uint(PRINT_ANY, + "erspan_ver", "erspan_ver %u ", erspan_ver); + } + + if (tb[IFLA_GRE_ERSPAN_DIR]) { + __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]); + + if (erspan_dir == 0) + print_string(PRINT_ANY, "erspan_dir", + "erspan_dir ingress ", NULL); + else + print_string(PRINT_ANY, "erspan_dir", + "erspan_dir egress ", NULL); + } + + if (tb[IFLA_GRE_ERSPAN_HWID]) { + __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]); + + print_0xhex(PRINT_ANY, + "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid); + } + + tnl_print_encap(tb, + IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, + IFLA_GRE_ENCAP_DPORT); +} + +struct link_util ip6gre_link_util = { + .id = "ip6gre", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +}; + +struct link_util ip6gretap_link_util = { + .id = "ip6gretap", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +}; + +struct link_util ip6erspan_link_util = { + .id = "ip6erspan", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +};
diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c new file mode 100644 index 0000000..c7b49b0 --- /dev/null +++ b/ip/link_ip6tnl.c
@@ -0,0 +1,466 @@ +/* + * link_ip6tnl.c ip6tnl driver module + * + * 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 <nicolas.dichtel@6wind.com> + * + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include <linux/ip6_tunnel.h> +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000) +#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF) + +#define DEFAULT_TNL_HOP_LIMIT (64) + +static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + fprintf(f, + "Usage: ... %-6s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ encaplimit ELIM ]\n" + " [ hoplimit HLIM ]\n" + " [ tclass TCLASS ]\n" + " [ flowlabel FLOWLABEL ]\n" + " [ dscp inherit ]\n" + " [ [no]allow-localremote ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + " [ external ]\n" + " [ noencap ]\n" + " [ encap { fou | gue | none } ]\n" + " [ encap-sport PORT ]\n" + " [ encap-dport PORT ]\n" + " [ [no]encap-csum ]\n" + " [ [no]encap-csum6 ]\n" + " [ [no]encap-remcsum ]\n" + " [ mode { ip6ip6 | ipip6 | any } ]\n" + "\n" + "Where: ADDR := IPV6_ADDRESS\n" + " ELIM := { none | 0..255 }(default=%d)\n" + " HLIM := 0..255 (default=%d)\n" + " TCLASS := { 0x0..0xff | inherit }\n" + " FLOWLABEL := { 0x0..0xfffff | inherit }\n" + " MARK := { 0x0..0xffffffff | inherit }\n", + lu->id, + IPV6_DEFAULT_TNL_ENCAP_LIMIT, DEFAULT_TNL_HOP_LIMIT); +} + +static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1]; + int len; + inet_prefix saddr, daddr; + __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT; + __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + __u32 flowinfo = 0; + __u32 flags = 0; + __u8 proto = 0; + __u32 link = 0; + __u16 encaptype = 0; + __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6; + __u16 encapsport = 0; + __u16 encapdport = 0; + __u8 metadata = 0; + __u32 fwmark = 0; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = iptuninfo[IFLA_IPTUN_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET6)) + goto get_failed; + + rta = iptuninfo[IFLA_IPTUN_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET6)) + goto get_failed; + + if (iptuninfo[IFLA_IPTUN_TTL]) + hop_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]); + + if (iptuninfo[IFLA_IPTUN_ENCAP_LIMIT]) + encap_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_ENCAP_LIMIT]); + + if (iptuninfo[IFLA_IPTUN_FLOWINFO]) + flowinfo = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLOWINFO]); + + if (iptuninfo[IFLA_IPTUN_FLAGS]) + flags = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLAGS]); + + if (iptuninfo[IFLA_IPTUN_LINK]) + link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]); + + if (iptuninfo[IFLA_IPTUN_PROTO]) + proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]); + if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA]) + metadata = 1; + + if (iptuninfo[IFLA_IPTUN_FWMARK]) + fwmark = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FWMARK]); + + free(answer); + } + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipv6/ipv6") == 0 || + strcmp(*argv, "ip6ip6") == 0) + proto = IPPROTO_IPV6; + else if (strcmp(*argv, "ip/ipv6") == 0 || + strcmp(*argv, "ipv4/ipv6") == 0 || + strcmp(*argv, "ipip6") == 0 || + strcmp(*argv, "ip4ip6") == 0) + proto = IPPROTO_IPIP; + else if (strcmp(*argv, "any/ipv6") == 0 || + strcmp(*argv, "any") == 0) + proto = 0; + else + invarg("Cannot guess tunnel mode.", *argv); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET6); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET6); + } else if (matches(*argv, "dev") == 0) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (strcmp(*argv, "ttl") == 0 || + strcmp(*argv, "hoplimit") == 0 || + strcmp(*argv, "hlim") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_u8(&hop_limit, *argv, 0)) + invarg("invalid HLIM\n", *argv); + } else + hop_limit = 0; + } else if (strcmp(*argv, "encaplimit") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "none") == 0) { + flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + } else { + __u8 uval; + + if (get_u8(&uval, *argv, 0) < -1) + invarg("invalid ELIM", *argv); + encap_limit = uval; + flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } else if (strcmp(*argv, "tos") == 0 || + strcmp(*argv, "tclass") == 0 || + strcmp(*argv, "tc") == 0 || + matches(*argv, "dsfield") == 0) { + __u8 uval; + + NEXT_ARG(); + flowinfo &= ~IP6_FLOWINFO_TCLASS; + if (strcmp(*argv, "inherit") == 0) + flags |= IP6_TNL_F_USE_ORIG_TCLASS; + else { + if (get_u8(&uval, *argv, 16)) + invarg("invalid TClass", *argv); + flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS; + flags &= ~IP6_TNL_F_USE_ORIG_TCLASS; + } + } else if (strcmp(*argv, "flowlabel") == 0 || + strcmp(*argv, "fl") == 0) { + __u32 uval; + + NEXT_ARG(); + flowinfo &= ~IP6_FLOWINFO_FLOWLABEL; + if (strcmp(*argv, "inherit") == 0) + 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); + flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL; + flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; + } + } else if (strcmp(*argv, "dscp") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) + invarg("not inherit", *argv); + flags |= IP6_TNL_F_RCV_DSCP_COPY; + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") == 0) { + flags |= IP6_TNL_F_USE_ORIG_FWMARK; + fwmark = 0; + } else { + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + flags &= ~IP6_TNL_F_USE_ORIG_FWMARK; + } + } else if (strcmp(*argv, "allow-localremote") == 0) { + flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "noallow-localremote") == 0) { + flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; + } else { + ip6tunnel_print_help(lu, argc, argv, stderr); + return -1; + } + argc--, argv++; + } + + addattr8(n, 1024, IFLA_IPTUN_PROTO, proto); + if (metadata) { + addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0); + return 0; + } + + if (is_addrtype_inet_not_unspec(&saddr)) { + addattr_l(n, 1024, IFLA_IPTUN_LOCAL, + saddr.data, saddr.bytelen); + } + if (is_addrtype_inet_not_unspec(&daddr)) { + addattr_l(n, 1024, IFLA_IPTUN_REMOTE, + daddr.data, daddr.bytelen); + } + addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit); + addattr8(n, 1024, IFLA_IPTUN_ENCAP_LIMIT, encap_limit); + addattr32(n, 1024, IFLA_IPTUN_FLOWINFO, flowinfo); + addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags); + addattr32(n, 1024, IFLA_IPTUN_LINK, link); + addattr32(n, 1024, IFLA_IPTUN_FWMARK, fwmark); + + addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_DPORT, htons(encapdport)); + + return 0; +} + +static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + __u32 flags = 0; + __u32 flowinfo = 0; + __u8 ttl = 0; + + if (!tb) + return; + + if (tb[IFLA_IPTUN_COLLECT_METADATA]) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + if (tb[IFLA_IPTUN_FLAGS]) + flags = rta_getattr_u32(tb[IFLA_IPTUN_FLAGS]); + + if (tb[IFLA_IPTUN_FLOWINFO]) + flowinfo = rta_getattr_u32(tb[IFLA_IPTUN_FLOWINFO]); + + if (tb[IFLA_IPTUN_PROTO]) { + switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) { + case IPPROTO_IPIP: + print_string(PRINT_ANY, "proto", "%s ", "ipip6"); + break; + case IPPROTO_IPV6: + print_string(PRINT_ANY, "proto", "%s ", "ip6ip6"); + break; + case 0: + print_string(PRINT_ANY, "proto", "%s ", "any"); + break; + } + } + + tnl_print_endpoint("remote", tb[IFLA_IPTUN_REMOTE], AF_INET6); + tnl_print_endpoint("local", tb[IFLA_IPTUN_LOCAL], AF_INET6); + + if (tb[IFLA_IPTUN_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_IPTUN_TTL]) + ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "hoplimit %u ", ttl); + else + print_string(PRINT_FP, NULL, "hoplimit %s ", "inherit"); + + if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) { + print_bool(PRINT_ANY, + "ip6_tnl_f_ign_encap_limit", + "encaplimit none ", + true); + } else if (tb[IFLA_IPTUN_ENCAP_LIMIT]) { + __u8 val = rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); + + print_uint(PRINT_ANY, "encap_limit", "encaplimit %u ", val); + } + + if (flags & IP6_TNL_F_USE_ORIG_TCLASS) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_tclass", + "tclass inherit ", + true); + } else if (tb[IFLA_IPTUN_FLOWINFO]) { + __u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20; + + snprintf(s2, sizeof(s2), "0x%02x", val); + print_string(PRINT_ANY, "tclass", "tclass %s ", s2); + } + + if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_flowlabel", + "flowlabel inherit ", + true); + } else if (tb[IFLA_IPTUN_FLOWINFO]) { + __u32 val = ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL); + + snprintf(s2, sizeof(s2), "0x%05x", val); + print_string(PRINT_ANY, "flowlabel", "flowlabel %s ", s2); + } + + if (flags & IP6_TNL_F_RCV_DSCP_COPY) + print_bool(PRINT_ANY, + "ip6_tnl_f_rcv_dscp_copy", + "dscp inherit ", + true); + + if (flags & IP6_TNL_F_MIP6_DEV) + print_bool(PRINT_ANY, "ip6_tnl_f_mip6_dev", "mip6 ", true); + + if (flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) + print_bool(PRINT_ANY, + "ip6_tnl_f_allow_local_remote", + "allow-localremote ", + true); + + if (flags & IP6_TNL_F_USE_ORIG_FWMARK) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_fwmark", + "fwmark inherit ", + true); + } else if (tb[IFLA_IPTUN_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } + + tnl_print_encap(tb, + IFLA_IPTUN_ENCAP_TYPE, + IFLA_IPTUN_ENCAP_FLAGS, + IFLA_IPTUN_ENCAP_SPORT, + IFLA_IPTUN_ENCAP_DPORT); +} + +struct link_util ip6tnl_link_util = { + .id = "ip6tnl", + .maxattr = IFLA_IPTUN_MAX, + .parse_opt = ip6tunnel_parse_opt, + .print_opt = ip6tunnel_print_opt, + .print_help = ip6tunnel_print_help, +};
diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c new file mode 100644 index 0000000..636cdb2 --- /dev/null +++ b/ip/link_iptnl.c
@@ -0,0 +1,497 @@ +/* + * link_iptnl.c ipip and sit driver module + * + * 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 <nicolas.dichtel@6wind.com> + * + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void iptunnel_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + const char *mode; + + if (strcmp(lu->id, "sit") == 0) { + mode = "{ ip6ip | ipip | mplsip | any } ]\n" + " [ isatap"; + } else { + mode = "{ ipip | mplsip | any }"; + } + + fprintf(f, + "Usage: ... %-6s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ ttl TTL ]\n" + " [ tos TOS ]\n" + " [ [no]pmtudisc ]\n" + " [ 6rd-prefix ADDR ]\n" + " [ 6rd-relay_prefix ADDR ]\n" + " [ 6rd-reset ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + " [ external ]\n" + " [ noencap ]\n" + " [ encap { fou | gue | none } ]\n" + " [ encap-sport PORT ]\n" + " [ encap-dport PORT ]\n" + " [ [no]encap-csum ]\n" + " [ [no]encap-csum6 ]\n" + " [ [no]encap-remcsum ]\n" + " [ mode %s ]\n" + "\n" + "Where: ADDR := { IP_ADDRESS | any }\n" + " TOS := { NUMBER | inherit }\n" + " TTL := { 1..255 | inherit }\n" + " MARK := { 0x0..0xffffffff }\n", + lu->id, mode); +} + +static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1]; + int len; + inet_prefix saddr, daddr, ip6rdprefix, ip6rdrelayprefix; + __u8 pmtudisc = 1; + __u8 tos = 0; + __u16 iflags = 0; + __u8 ttl = 0; + __u8 proto = 0; + __u32 link = 0; + __u16 encaptype = 0; + __u16 encapflags = 0; + __u16 encapsport = 0; + __u16 encapdport = 0; + __u8 metadata = 0; + __u32 fwmark = 0; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + inet_prefix_reset(&ip6rdprefix); + inet_prefix_reset(&ip6rdrelayprefix); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = iptuninfo[IFLA_IPTUN_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET)) + goto get_failed; + + rta = iptuninfo[IFLA_IPTUN_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET)) + goto get_failed; + + rta = iptuninfo[IFLA_IPTUN_6RD_PREFIX]; + if (rta && get_addr_rta(&ip6rdprefix, rta, AF_INET6)) + goto get_failed; + + rta = iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIX]; + if (rta && get_addr_rta(&ip6rdrelayprefix, rta, AF_INET)) + goto get_failed; + + rta = iptuninfo[IFLA_IPTUN_6RD_PREFIXLEN]; + ip6rdprefix.bitlen = rta ? rta_getattr_u16(rta) : 0; + + rta = iptuninfo[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]; + ip6rdrelayprefix.bitlen = rta ? rta_getattr_u16(rta) : 0; + + if (iptuninfo[IFLA_IPTUN_TTL]) + ttl = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]); + + if (iptuninfo[IFLA_IPTUN_PMTUDISC]) + pmtudisc = + rta_getattr_u8(iptuninfo[IFLA_IPTUN_PMTUDISC]); + + if (iptuninfo[IFLA_IPTUN_TOS]) + tos = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TOS]); + + if (iptuninfo[IFLA_IPTUN_FLAGS]) + iflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_FLAGS]); + + if (iptuninfo[IFLA_IPTUN_LINK]) + link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]); + + if (iptuninfo[IFLA_IPTUN_PROTO]) + proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]); + + if (iptuninfo[IFLA_IPTUN_ENCAP_TYPE]) + encaptype = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_TYPE]); + if (iptuninfo[IFLA_IPTUN_ENCAP_FLAGS]) + encapflags = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_FLAGS]); + if (iptuninfo[IFLA_IPTUN_ENCAP_SPORT]) + encapsport = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_SPORT]); + if (iptuninfo[IFLA_IPTUN_ENCAP_DPORT]) + encapdport = rta_getattr_u16(iptuninfo[IFLA_IPTUN_ENCAP_DPORT]); + + if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA]) + metadata = 1; + + if (iptuninfo[IFLA_IPTUN_FWMARK]) + fwmark = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FWMARK]); + + free(answer); + } + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(lu->id, "sit") == 0 && + (strcmp(*argv, "ipv6/ipv4") == 0 || + strcmp(*argv, "ip6ip") == 0)) + proto = IPPROTO_IPV6; + else if (strcmp(*argv, "ipv4/ipv4") == 0 || + strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip4ip4") == 0) + proto = IPPROTO_IPIP; + else if (strcmp(*argv, "mpls/ipv4") == 0 || + strcmp(*argv, "mplsip") == 0) + proto = IPPROTO_MPLS; + else if (strcmp(*argv, "any/ipv4") == 0 || + strcmp(*argv, "any") == 0) + proto = 0; + else + invarg("Cannot guess tunnel mode.", *argv); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET); + } else if (matches(*argv, "dev") == 0) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (strcmp(*argv, "ttl") == 0 || + strcmp(*argv, "hoplimit") == 0 || + strcmp(*argv, "hlim") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_u8(&ttl, *argv, 0)) + invarg("invalid TTL\n", *argv); + } else + ttl = 0; + } else if (strcmp(*argv, "tos") == 0 || + strcmp(*argv, "tclass") == 0 || + strcmp(*argv, "tc") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 uval; + + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + tos = uval; + } else + tos = 1; + } else if (strcmp(*argv, "nopmtudisc") == 0) { + pmtudisc = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + pmtudisc = 1; + } else if (strcmp(lu->id, "sit") == 0 && + strcmp(*argv, "isatap") == 0) { + iflags |= SIT_ISATAP; + } else if (strcmp(*argv, "noencap") == 0) { + encaptype = TUNNEL_ENCAP_NONE; + } else if (strcmp(*argv, "encap") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "fou") == 0) + encaptype = TUNNEL_ENCAP_FOU; + else if (strcmp(*argv, "gue") == 0) + encaptype = TUNNEL_ENCAP_GUE; + else if (strcmp(*argv, "none") == 0) + encaptype = TUNNEL_ENCAP_NONE; + else + invarg("Invalid encap type.", *argv); + } else if (strcmp(*argv, "encap-sport") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "auto") == 0) + encapsport = 0; + else if (get_u16(&encapsport, *argv, 0)) + invarg("Invalid source port.", *argv); + } else if (strcmp(*argv, "encap-dport") == 0) { + NEXT_ARG(); + if (get_u16(&encapdport, *argv, 0)) + invarg("Invalid destination port.", *argv); + } else if (strcmp(*argv, "encap-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "noencap-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM; + } else if (strcmp(*argv, "encap-udp6-csum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "noencap-udp6-csum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6; + } else if (strcmp(*argv, "encap-remcsum") == 0) { + encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "noencap-remcsum") == 0) { + encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM; + } else if (strcmp(*argv, "external") == 0) { + metadata = 1; + } else if (strcmp(*argv, "6rd-prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&ip6rdprefix, *argv, AF_INET6)) + invarg("invalid 6rd_prefix\n", *argv); + } else if (strcmp(*argv, "6rd-relay_prefix") == 0) { + NEXT_ARG(); + if (get_prefix(&ip6rdrelayprefix, *argv, AF_INET)) + invarg("invalid 6rd-relay_prefix\n", *argv); + } else if (strcmp(*argv, "6rd-reset") == 0) { + get_prefix(&ip6rdprefix, "2002::/16", AF_INET6); + inet_prefix_reset(&ip6rdrelayprefix); + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + } else { + iptunnel_print_help(lu, argc, argv, stderr); + return -1; + } + argc--, argv++; + } + + if (ttl && pmtudisc == 0) { + fprintf(stderr, "ttl != 0 and nopmtudisc are incompatible\n"); + exit(-1); + } + + addattr8(n, 1024, IFLA_IPTUN_PROTO, proto); + if (metadata) { + addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0); + return 0; + } + + if (is_addrtype_inet_not_unspec(&saddr)) { + addattr_l(n, 1024, IFLA_IPTUN_LOCAL, + saddr.data, saddr.bytelen); + } + if (is_addrtype_inet_not_unspec(&daddr)) { + addattr_l(n, 1024, IFLA_IPTUN_REMOTE, + daddr.data, daddr.bytelen); + } + addattr8(n, 1024, IFLA_IPTUN_PMTUDISC, pmtudisc); + addattr8(n, 1024, IFLA_IPTUN_TOS, tos); + addattr8(n, 1024, IFLA_IPTUN_TTL, ttl); + addattr32(n, 1024, IFLA_IPTUN_LINK, link); + addattr32(n, 1024, IFLA_IPTUN_FWMARK, fwmark); + + addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_SPORT, htons(encapsport)); + addattr16(n, 1024, IFLA_IPTUN_ENCAP_DPORT, htons(encapdport)); + + if (strcmp(lu->id, "sit") == 0) { + addattr16(n, 1024, IFLA_IPTUN_FLAGS, iflags); + if (is_addrtype_inet(&ip6rdprefix)) { + addattr_l(n, 1024, IFLA_IPTUN_6RD_PREFIX, + ip6rdprefix.data, ip6rdprefix.bytelen); + addattr16(n, 1024, IFLA_IPTUN_6RD_PREFIXLEN, + ip6rdprefix.bitlen); + } + if (is_addrtype_inet(&ip6rdrelayprefix)) { + addattr32(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIX, + ip6rdrelayprefix.data[0]); + addattr16(n, 1024, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, + ip6rdrelayprefix.bitlen); + } + } + + return 0; +} + +static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + __u16 prefixlen; + __u8 ttl = 0; + __u8 tos = 0; + + if (!tb) + return; + + if (tb[IFLA_IPTUN_COLLECT_METADATA]) { + print_bool(PRINT_ANY, "external", "external ", true); + return; + } + + if (tb[IFLA_IPTUN_PROTO]) { + switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) { + case IPPROTO_IPIP: + print_string(PRINT_ANY, "proto", "%s ", "ipip"); + break; + case IPPROTO_IPV6: + print_string(PRINT_ANY, "proto", "%s ", "ip6ip"); + break; + case IPPROTO_MPLS: + print_string(PRINT_ANY, "proto", "%s ", "mplsip"); + break; + case 0: + print_string(PRINT_ANY, "proto", "%s ", "any"); + break; + } + } + + tnl_print_endpoint("remote", tb[IFLA_IPTUN_REMOTE], AF_INET); + tnl_print_endpoint("local", tb[IFLA_IPTUN_LOCAL], AF_INET); + + if (tb[IFLA_IPTUN_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_IPTUN_TTL]) + ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]); + if (is_json_context() || ttl) + print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl); + else + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + + if (tb[IFLA_IPTUN_TOS]) + tos = rta_getattr_u8(tb[IFLA_IPTUN_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_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC])) + print_bool(PRINT_ANY, "pmtudisc", "pmtudisc ", true); + else + print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false); + + if (tb[IFLA_IPTUN_FLAGS]) { + __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]); + + if (iflags & SIT_ISATAP) + print_bool(PRINT_ANY, "isatap", "isatap ", true); + } + + if (tb[IFLA_IPTUN_6RD_PREFIXLEN] && + (prefixlen = rta_getattr_u16(tb[IFLA_IPTUN_6RD_PREFIXLEN]))) { + __u16 relayprefixlen = + rta_getattr_u16(tb[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]); + __u32 relayprefix = + rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]); + + const char *prefix = inet_ntop(AF_INET6, + RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]), + s2, sizeof(s2)); + + if (is_json_context()) { + print_string(PRINT_JSON, "prefix", NULL, prefix); + print_int(PRINT_JSON, "prefixlen", NULL, prefixlen); + if (relayprefix) { + print_string(PRINT_JSON, + "relay_prefix", + NULL, + format_host(AF_INET, + 4, + &relayprefix)); + print_int(PRINT_JSON, + "relay_prefixlen", + NULL, + relayprefixlen); + } + } else { + printf("6rd-prefix %s/%u ", prefix, prefixlen); + if (relayprefix) { + printf("6rd-relay_prefix %s/%u ", + format_host(AF_INET, 4, &relayprefix), + relayprefixlen); + } + } + } + + if (tb[IFLA_IPTUN_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } + + tnl_print_encap(tb, + IFLA_IPTUN_ENCAP_TYPE, + IFLA_IPTUN_ENCAP_FLAGS, + IFLA_IPTUN_ENCAP_SPORT, + IFLA_IPTUN_ENCAP_DPORT); +} + +struct link_util ipip_link_util = { + .id = "ipip", + .maxattr = IFLA_IPTUN_MAX, + .parse_opt = iptunnel_parse_opt, + .print_opt = iptunnel_print_opt, + .print_help = iptunnel_print_help, +}; + +struct link_util sit_link_util = { + .id = "sit", + .maxattr = IFLA_IPTUN_MAX, + .parse_opt = iptunnel_parse_opt, + .print_opt = iptunnel_print_opt, + .print_help = iptunnel_print_help, +};
diff --git a/ip/link_veth.c b/ip/link_veth.c new file mode 100644 index 0000000..33e8f2b --- /dev/null +++ b/ip/link_veth.c
@@ -0,0 +1,86 @@ +/* + * link_veth.c veth driver module + * + * 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: Pavel Emelianov <xemul@openvz.org> + * + */ + +#include <string.h> +#include <net/if.h> +#include <linux/veth.h> + +#include "utils.h" +#include "ip_common.h" + +static void print_usage(FILE *f) +{ + printf("Usage: ip link <options> type veth [peer <options>]\n" + "To get <options> type 'ip link add help'\n"); +} + +static void usage(void) +{ + print_usage(stderr); +} + +static int veth_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, VETH_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 veth_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + print_usage(f); +} + +struct link_util veth_link_util = { + .id = "veth", + .parse_opt = veth_parse_opt, + .print_help = veth_print_help, +};
diff --git a/ip/link_vti.c b/ip/link_vti.c new file mode 100644 index 0000000..3a52ea8 --- /dev/null +++ b/ip/link_vti.c
@@ -0,0 +1,216 @@ +/* + * link_vti.c VTI driver module + * + * 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: Herbert Xu <herbert@gondor.apana.org.au> + * Saurabh Mohan <saurabh.mohan@vyatta.com> Modified link_gre.c for VTI + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void vti_print_help(struct link_util *lu, int argc, char **argv, FILE *f) +{ + fprintf(f, + "Usage: ... %-4s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ [i|o]key KEY ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + "\n" + "Where: ADDR := { IP_ADDRESS }\n" + " KEY := { DOTTED_QUAD | NUMBER }\n" + " MARK := { 0x0..0xffffffff }\n", + lu->id); +} + +static int vti_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *vtiinfo[IFLA_VTI_MAX + 1]; + __be32 ikey = 0; + __be32 okey = 0; + inet_prefix saddr, daddr; + unsigned int link = 0; + __u32 fwmark = 0; + int len; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = vtiinfo[IFLA_VTI_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET)) + goto get_failed; + + rta = vtiinfo[IFLA_VTI_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET)) + goto get_failed; + + if (vtiinfo[IFLA_VTI_IKEY]) + ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]); + + if (vtiinfo[IFLA_VTI_OKEY]) + okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]); + + if (vtiinfo[IFLA_VTI_LINK]) + link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]); + + if (vtiinfo[IFLA_VTI_FWMARK]) + fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]); + + free(answer); + } + + while (argc > 0) { + if (!matches(*argv, "key")) { + NEXT_ARG(); + ikey = okey = tnl_parse_key("key", *argv); + } else if (!matches(*argv, "ikey")) { + NEXT_ARG(); + ikey = tnl_parse_key("ikey", *argv); + } else if (!matches(*argv, "okey")) { + NEXT_ARG(); + okey = tnl_parse_key("okey", *argv); + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET); + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET); + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + } else { + vti_print_help(lu, argc, argv, stderr); + return -1; + } + argc--; argv++; + } + + addattr32(n, 1024, IFLA_VTI_IKEY, ikey); + addattr32(n, 1024, IFLA_VTI_OKEY, okey); + if (is_addrtype_inet_not_unspec(&saddr)) + addattr_l(n, 1024, IFLA_VTI_LOCAL, saddr.data, saddr.bytelen); + if (is_addrtype_inet_not_unspec(&daddr)) + addattr_l(n, 1024, IFLA_VTI_REMOTE, daddr.data, daddr.bytelen); + addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark); + if (link) + addattr32(n, 1024, IFLA_VTI_LINK, link); + + return 0; +} + +static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + + if (!tb) + return; + + tnl_print_endpoint("remote", tb[IFLA_VTI_REMOTE], AF_INET); + tnl_print_endpoint("local", tb[IFLA_VTI_LOCAL], AF_INET); + + if (tb[IFLA_VTI_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_VTI_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_VTI_IKEY]) { + struct rtattr *rta = tb[IFLA_VTI_IKEY]; + __u32 key = rta_getattr_u32(rta); + + if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2))) + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); + } + + if (tb[IFLA_VTI_OKEY]) { + struct rtattr *rta = tb[IFLA_VTI_OKEY]; + __u32 key = rta_getattr_u32(rta); + + if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2))) + print_string(PRINT_ANY, "okey", "okey %s ", s2); + } + + if (tb[IFLA_VTI_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } +} + +struct link_util vti_link_util = { + .id = "vti", + .maxattr = IFLA_VTI_MAX, + .parse_opt = vti_parse_opt, + .print_opt = vti_print_opt, + .print_help = vti_print_help, +};
diff --git a/ip/link_vti6.c b/ip/link_vti6.c new file mode 100644 index 0000000..0b080fa --- /dev/null +++ b/ip/link_vti6.c
@@ -0,0 +1,218 @@ +/* + * link_vti6.c VTI driver module + * + * 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: Herbert Xu <herbert@gondor.apana.org.au> + * Saurabh Mohan <saurabh.mohan@vyatta.com> Modified link_gre.c for VTI + * Steffen Klassert <steffen.klassert@secunet.com> Modified link_vti.c for IPv6 + */ + +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void vti6_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + fprintf(f, + "Usage: ... %-4s [ remote ADDR ]\n" + " [ local ADDR ]\n" + " [ [i|o]key KEY ]\n" + " [ dev PHYS_DEV ]\n" + " [ fwmark MARK ]\n" + "\n" + "Where: ADDR := { IPV6_ADDRESS }\n" + " KEY := { DOTTED_QUAD | NUMBER }\n" + " MARK := { 0x0..0xffffffff }\n", + lu->id); +} + +static int vti6_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct { + struct nlmsghdr n; + struct ifinfomsg i; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETLINK, + .i.ifi_family = preferred_family, + .i.ifi_index = ifi->ifi_index, + }; + struct nlmsghdr *answer; + struct rtattr *tb[IFLA_MAX + 1]; + struct rtattr *linkinfo[IFLA_INFO_MAX+1]; + struct rtattr *vtiinfo[IFLA_VTI_MAX + 1]; + __be32 ikey = 0; + __be32 okey = 0; + inet_prefix saddr, daddr; + unsigned int link = 0; + __u32 fwmark = 0; + int len; + + inet_prefix_reset(&saddr); + inet_prefix_reset(&daddr); + + if (!(n->nlmsg_flags & NLM_F_CREATE)) { + const struct rtattr *rta; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) { +get_failed: + fprintf(stderr, + "Failed to get existing tunnel info.\n"); + return -1; + } + + len = answer->nlmsg_len; + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + goto get_failed; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len); + + if (!tb[IFLA_LINKINFO]) + goto get_failed; + + parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!linkinfo[IFLA_INFO_DATA]) + goto get_failed; + + parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX, + linkinfo[IFLA_INFO_DATA]); + + rta = vtiinfo[IFLA_VTI_LOCAL]; + if (rta && get_addr_rta(&saddr, rta, AF_INET6)) + goto get_failed; + + rta = vtiinfo[IFLA_VTI_REMOTE]; + if (rta && get_addr_rta(&daddr, rta, AF_INET6)) + goto get_failed; + + if (vtiinfo[IFLA_VTI_IKEY]) + ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]); + + if (vtiinfo[IFLA_VTI_OKEY]) + okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]); + + if (vtiinfo[IFLA_VTI_LINK]) + link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]); + + if (vtiinfo[IFLA_VTI_FWMARK]) + fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]); + + free(answer); + } + + while (argc > 0) { + if (!matches(*argv, "key")) { + NEXT_ARG(); + ikey = okey = tnl_parse_key("key", *argv); + } else if (!matches(*argv, "ikey")) { + NEXT_ARG(); + ikey = tnl_parse_key("ikey", *argv); + } else if (!matches(*argv, "okey")) { + NEXT_ARG(); + okey = tnl_parse_key("okey", *argv); + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + get_addr(&daddr, *argv, AF_INET6); + } else if (!matches(*argv, "local")) { + NEXT_ARG(); + get_addr(&saddr, *argv, AF_INET6); + } else if (!matches(*argv, "dev")) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (strcmp(*argv, "fwmark") == 0) { + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("invalid fwmark\n", *argv); + } else { + vti6_print_help(lu, argc, argv, stderr); + return -1; + } + argc--; argv++; + } + + addattr32(n, 1024, IFLA_VTI_IKEY, ikey); + addattr32(n, 1024, IFLA_VTI_OKEY, okey); + if (is_addrtype_inet_not_unspec(&saddr)) + addattr_l(n, 1024, IFLA_VTI_LOCAL, saddr.data, saddr.bytelen); + if (is_addrtype_inet_not_unspec(&daddr)) + addattr_l(n, 1024, IFLA_VTI_REMOTE, daddr.data, daddr.bytelen); + addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark); + if (link) + addattr32(n, 1024, IFLA_VTI_LINK, link); + + return 0; +} + +static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + char s2[64]; + + if (!tb) + return; + + tnl_print_endpoint("remote", tb[IFLA_VTI_REMOTE], AF_INET6); + tnl_print_endpoint("local", tb[IFLA_VTI_LOCAL], AF_INET6); + + if (tb[IFLA_VTI_LINK]) { + __u32 link = rta_getattr_u32(tb[IFLA_VTI_LINK]); + + if (link) { + print_string(PRINT_ANY, "link", "dev %s ", + ll_index_to_name(link)); + } + } + + if (tb[IFLA_VTI_IKEY]) { + struct rtattr *rta = tb[IFLA_VTI_IKEY]; + __u32 key = rta_getattr_u32(rta); + + if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2))) + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); + } + + if (tb[IFLA_VTI_OKEY]) { + struct rtattr *rta = tb[IFLA_VTI_OKEY]; + __u32 key = rta_getattr_u32(rta); + + if (key && inet_ntop(AF_INET, RTA_DATA(rta), s2, sizeof(s2))) + print_string(PRINT_ANY, "okey", "okey %s ", s2); + } + + if (tb[IFLA_VTI_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]); + + if (fwmark) { + print_0xhex(PRINT_ANY, + "fwmark", "fwmark %#llx ", fwmark); + } + } +} + +struct link_util vti6_link_util = { + .id = "vti6", + .maxattr = IFLA_VTI_MAX, + .parse_opt = vti6_parse_opt, + .print_opt = vti6_print_opt, + .print_help = vti6_print_help, +};
diff --git a/ip/link_xfrm.c b/ip/link_xfrm.c new file mode 100644 index 0000000..a28f308 --- /dev/null +++ b/ip/link_xfrm.c
@@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * link_xfrm.c Virtual XFRM Interface driver module + * + * Authors: Matt Ellison <matt@arroyo.io> + */ + +#include <string.h> +#include <linux/if_link.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" +#include "tunnel.h" + +static void xfrm_print_help(struct link_util *lu, int argc, char **argv, + FILE *f) +{ + fprintf(f, + "Usage: ... %-4s dev [ PHYS_DEV ] [ if_id IF-ID ]\n" + "\n" + "Where: IF-ID := { 0x0..0xffffffff }\n", + lu->id); +} + +static int xfrm_parse_opt(struct link_util *lu, int argc, char **argv, + struct nlmsghdr *n) +{ + unsigned int link = 0; + __u32 if_id = 0; + + while (argc > 0) { + if (!matches(*argv, "dev")) { + NEXT_ARG(); + link = ll_name_to_index(*argv); + if (!link) + exit(nodev(*argv)); + } else if (!matches(*argv, "if_id")) { + NEXT_ARG(); + if (!get_u32(&if_id, *argv, 0)) + addattr32(n, 1024, IFLA_XFRM_IF_ID, if_id); + } else { + xfrm_print_help(lu, argc, argv, stderr); + return -1; + } + argc--; argv++; + } + + if (link) + addattr32(n, 1024, IFLA_XFRM_LINK, link); + + return 0; +} + +static void xfrm_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) +{ + + if (!tb) + return; + + if (tb[IFLA_XFRM_IF_ID]) { + __u32 id = rta_getattr_u32(tb[IFLA_XFRM_IF_ID]); + + print_0xhex(PRINT_ANY, "if_id", "if_id %#llx ", id); + + } + +} + +struct link_util xfrm_link_util = { + .id = "xfrm", + .maxattr = IFLA_XFRM_MAX, + .parse_opt = xfrm_parse_opt, + .print_opt = xfrm_print_opt, + .print_help = xfrm_print_help, +};
diff --git a/ip/routef b/ip/routef new file mode 100644 index 0000000..c251e7b --- /dev/null +++ b/ip/routef
@@ -0,0 +1,10 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 + +if [ -z "$*" ] ; then + exec ip -4 ro flush scope global type unicast +else + echo "Usage: routef" + echo + echo "This script will flush the IPv4 routing table" +fi
diff --git a/ip/routel b/ip/routel new file mode 100644 index 0000000..7056886 --- /dev/null +++ b/ip/routel
@@ -0,0 +1,72 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# +# Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18 +# Donated to the public domain. +# +# This script transforms the output of "ip" into more readable text. +# "ip" is the Linux-advanced-routing configuration tool part of the +# iproute package. +# + +test "X-h" = "X$1" && echo "Usage: $0 [tablenr [raw ip args...]]" && exit 64 + +test -z "$*" && set 0 + +ip route list table "$@" | + while read network rest + do set xx $rest + shift + proto="" + via="" + dev="" + scope="" + src="" + table="" + case $network in + broadcast|local|unreachable) via=$network + network=$1 + shift + ;; + esac + while test $# != 0 + do + case "$1" in + proto|via|dev|scope|src|table) + key=$1 + val=$2 + eval "$key='$val'" + shift 2 + ;; + dead|onlink|pervasive|offload|notify|linkdown|unresolved) + shift + ;; + *) + # avoid infinite loop on unknown keyword without value at line end + shift + shift + ;; + esac + done + echo "$network $via $src $proto $scope $dev $table" + done | awk -F ' ' ' +BEGIN { + format="%15s%-3s %15s %15s %8s %8s%7s %s\n"; + printf(format,"target","","gateway","source","proto","scope","dev","tbl"); + } + { network=$1; + mask=""; + if(match(network,"/")) + { mask=" "substr(network,RSTART+1); + network=substr(network,0,RSTART); + } + via=$2; + src=$3; + proto=$4; + scope=$5; + dev=$6; + table=$7; + printf(format,network,mask,via,src,proto,scope,dev,table); + } +'
diff --git a/ip/rtm_map.c b/ip/rtm_map.c new file mode 100644 index 0000000..8d8eafe --- /dev/null +++ b/ip/rtm_map.c
@@ -0,0 +1,129 @@ +/* + * rtm_map.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "rt_names.h" +#include "utils.h" + +char *rtnl_rtntype_n2a(int id, char *buf, int len) +{ + + if (numeric) { + snprintf(buf, len, "%d", id); + return buf; + } + + switch (id) { + case RTN_UNSPEC: + return "none"; + case RTN_UNICAST: + return "unicast"; + case RTN_LOCAL: + return "local"; + case RTN_BROADCAST: + return "broadcast"; + case RTN_ANYCAST: + return "anycast"; + case RTN_MULTICAST: + return "multicast"; + case RTN_BLACKHOLE: + return "blackhole"; + case RTN_UNREACHABLE: + return "unreachable"; + case RTN_PROHIBIT: + return "prohibit"; + case RTN_THROW: + return "throw"; + case RTN_NAT: + return "nat"; + case RTN_XRESOLVE: + return "xresolve"; + default: + snprintf(buf, len, "%d", id); + return buf; + } +} + + +int rtnl_rtntype_a2n(int *id, char *arg) +{ + char *end; + unsigned long res; + + if (strcmp(arg, "local") == 0) + res = RTN_LOCAL; + else if (strcmp(arg, "nat") == 0) + res = RTN_NAT; + else if (matches(arg, "broadcast") == 0 || + strcmp(arg, "brd") == 0) + res = RTN_BROADCAST; + else if (matches(arg, "anycast") == 0) + res = RTN_ANYCAST; + else if (matches(arg, "multicast") == 0) + res = RTN_MULTICAST; + else if (matches(arg, "prohibit") == 0) + res = RTN_PROHIBIT; + else if (matches(arg, "unreachable") == 0) + res = RTN_UNREACHABLE; + else if (matches(arg, "blackhole") == 0) + res = RTN_BLACKHOLE; + else if (matches(arg, "xresolve") == 0) + res = RTN_XRESOLVE; + else if (matches(arg, "unicast") == 0) + res = RTN_UNICAST; + else if (strcmp(arg, "throw") == 0) + res = RTN_THROW; + else { + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + } + *id = res; + return 0; +} + +static int get_rt_realms(__u32 *realms, char *arg) +{ + __u32 realm = 0; + char *p = strchr(arg, '/'); + + *realms = 0; + if (p) { + *p = 0; + if (rtnl_rtrealm_a2n(realms, arg)) { + *p = '/'; + return -1; + } + *realms <<= 16; + *p = '/'; + arg = p+1; + } + if (*arg && rtnl_rtrealm_a2n(&realm, arg)) + return -1; + *realms |= realm; + return 0; +} + +int get_rt_realms_or_raw(__u32 *realms, char *arg) +{ + if (!get_rt_realms(realms, arg)) + return 0; + + return get_unsigned(realms, arg, 0); +}
diff --git a/ip/rtmon.c b/ip/rtmon.c new file mode 100644 index 0000000..bccdded --- /dev/null +++ b/ip/rtmon.c
@@ -0,0 +1,184 @@ +/* + * rtmon.c RTnetlink listener. + * + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <string.h> + +#include "SNAPSHOT.h" + +#include "utils.h" +#include "libnetlink.h" + +static int init_phase = 1; + +static void write_stamp(FILE *fp) +{ + char buf[128]; + struct nlmsghdr *n1 = (void *)buf; + struct timeval tv; + + n1->nlmsg_type = NLMSG_TSTAMP; + n1->nlmsg_flags = 0; + n1->nlmsg_seq = 0; + n1->nlmsg_pid = 0; + n1->nlmsg_len = NLMSG_LENGTH(4*2); + gettimeofday(&tv, NULL); + ((__u32 *)NLMSG_DATA(n1))[0] = tv.tv_sec; + ((__u32 *)NLMSG_DATA(n1))[1] = tv.tv_usec; + fwrite((void *)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); +} + +static int dump_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + + if (!init_phase) + write_stamp(fp); + fwrite((void *)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); + fflush(fp); + return 0; +} + +static int dump_msg2(struct nlmsghdr *n, void *arg) +{ + return dump_msg(NULL, n, arg); +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: rtmon [ OPTIONS ] file FILE [ all | LISTofOBJECTS ]\n" + "OPTIONS := { -f[amily] { inet | inet6 | link | help } |\n" + " -4 | -6 | -0 | -V[ersion] }\n" + "LISTofOBJECTS := [ link ] [ address ] [ route ]\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + struct rtnl_handle rth; + int family = AF_UNSPEC; + unsigned int groups = ~0U; + int llink = 0; + int laddr = 0; + int lroute = 0; + char *file = NULL; + + while (argc > 1) { + if (matches(argv[1], "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "help") == 0) + usage(); + else { + fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + } else if (strcmp(argv[1], "-4") == 0) { + family = AF_INET; + } else if (strcmp(argv[1], "-6") == 0) { + family = AF_INET6; + } else if (strcmp(argv[1], "-0") == 0) { + family = AF_PACKET; + } else if (matches(argv[1], "-Version") == 0) { + printf("rtmon utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + } else if (matches(argv[1], "file") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + file = argv[1]; + } else if (matches(argv[1], "link") == 0) { + llink = 1; + groups = 0; + } else if (matches(argv[1], "address") == 0) { + laddr = 1; + groups = 0; + } else if (matches(argv[1], "route") == 0) { + lroute = 1; + groups = 0; + } else if (strcmp(argv[1], "all") == 0) { + groups = ~0U; + } else if (matches(argv[1], "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (file == NULL) { + fprintf(stderr, "Not enough information: argument \"file\" is required\n"); + exit(-1); + } + if (llink) + groups |= nl_mgrp(RTNLGRP_LINK); + if (laddr) { + if (!family || family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); + if (!family || family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_IFADDR); + } + if (lroute) { + if (!family || family == AF_INET) + groups |= nl_mgrp(RTNLGRP_IPV4_ROUTE); + if (!family || family == AF_INET6) + groups |= nl_mgrp(RTNLGRP_IPV6_ROUTE); + } + + fp = fopen(file, "w"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + write_stamp(fp); + + if (rtnl_dump_filter(&rth, dump_msg2, fp) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + init_phase = 0; + + if (rtnl_listen(&rth, dump_msg, (void *)fp) < 0) + exit(2); + + exit(0); +}
diff --git a/ip/rtpr b/ip/rtpr new file mode 100644 index 0000000..7e48674 --- /dev/null +++ b/ip/rtpr
@@ -0,0 +1,5 @@ +#! /bin/sh +# SPDX-License-Identifier: GPL-2.0 + +exec tr "[\\\\]" "[ +]"
diff --git a/ip/static-syms.c b/ip/static-syms.c new file mode 100644 index 0000000..47c4092 --- /dev/null +++ b/ip/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 <string.h> +#include "dlfcn.h" + +void *_dlsym(const char *sym) +{ +#include "static-syms.h" + return NULL; +}
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c new file mode 100644 index 0000000..acbd745 --- /dev/null +++ b/ip/tcp_metrics.c
@@ -0,0 +1,542 @@ +/* + * tcp_metrics.c "ip tcp_metrics/tcpmetrics" + * + * 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; + * + * Authors: Julian Anastasov <ja@ssi.bg>, August 2012 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> + +#include <linux/genetlink.h> +#include <linux/tcp_metrics.h> + +#include "utils.h" +#include "ip_common.h" +#include "libgenl.h" + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip tcp_metrics/tcpmetrics { COMMAND | help }\n" + " ip tcp_metrics { show | flush } SELECTOR\n" + " ip tcp_metrics delete [ address ] ADDRESS\n" + "SELECTOR := [ [ address ] PREFIX ]\n"); + exit(-1); +} + +/* netlink socket */ +static struct rtnl_handle grth = { .fd = -1 }; +static int genl_family = -1; +static const double usec_per_sec = 1000000.; + +#define TCPM_REQUEST(_req, _bufsiz, _cmd, _flags) \ + GENL_REQUEST(_req, _bufsiz, genl_family, 0, \ + TCP_METRICS_GENL_VERSION, _cmd, _flags) + +#define CMD_LIST 0x0001 /* list, lst, show */ +#define CMD_DEL 0x0002 /* delete, remove */ +#define CMD_FLUSH 0x0004 /* flush */ + +static const struct { + const char *name; + int code; +} cmds[] = { + { "list", CMD_LIST }, + { "lst", CMD_LIST }, + { "show", CMD_LIST }, + { "delete", CMD_DEL }, + { "remove", CMD_DEL }, + { "flush", CMD_FLUSH }, +}; + +static const char *metric_name[TCP_METRIC_MAX + 1] = { + [TCP_METRIC_RTT] = "rtt", + [TCP_METRIC_RTTVAR] = "rttvar", + [TCP_METRIC_SSTHRESH] = "ssthresh", + [TCP_METRIC_CWND] = "cwnd", + [TCP_METRIC_REORDERING] = "reordering", +}; + +static struct { + int flushed; + char *flushb; + int flushp; + int flushe; + int cmd; + inet_prefix daddr; + inet_prefix saddr; +} f; + +static int flush_update(void) +{ + if (rtnl_send_check(&grth, f.flushb, f.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + f.flushp = 0; + return 0; +} + +static void print_tcp_metrics(struct rtattr *a) +{ + struct rtattr *m[TCP_METRIC_MAX + 1 + 1]; + unsigned long rtt = 0, rttvar = 0; + int i; + + parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a); + + for (i = 0; i < TCP_METRIC_MAX + 1; i++) { + const char *name; + __u32 val; + SPRINT_BUF(b1); + + a = m[i + 1]; + if (!a) + continue; + + val = rta_getattr_u32(a); + + switch (i) { + case TCP_METRIC_RTT: + if (!rtt) + rtt = (val * 1000UL) >> 3; + continue; + case TCP_METRIC_RTTVAR: + if (!rttvar) + rttvar = (val * 1000UL) >> 2; + continue; + case TCP_METRIC_RTT_US: + rtt = val >> 3; + continue; + + case TCP_METRIC_RTTVAR_US: + rttvar = val >> 2; + continue; + + case TCP_METRIC_SSTHRESH: + case TCP_METRIC_CWND: + case TCP_METRIC_REORDERING: + name = metric_name[i]; + break; + + default: + snprintf(b1, sizeof(b1), + " metric_%d ", i); + name = b1; + } + + + print_uint(PRINT_JSON, name, NULL, val); + print_string(PRINT_FP, NULL, " %s ", name); + print_uint(PRINT_FP, NULL, "%u", val); + } + + if (rtt) { + print_float(PRINT_JSON, "rtt", NULL, + (double)rtt / usec_per_sec); + print_u64(PRINT_FP, NULL, + " rtt %luus", rtt); + } + if (rttvar) { + print_float(PRINT_JSON, "rttvar", NULL, + (double) rttvar / usec_per_sec); + print_u64(PRINT_FP, NULL, + " rttvar %luus", rttvar); + } +} + +static int process_msg(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *) arg; + struct genlmsghdr *ghdr; + struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a; + const char *h; + int len = n->nlmsg_len; + inet_prefix daddr, saddr; + int atype, stype; + + 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 != TCP_METRICS_CMD_GET) + return 0; + + parse_rtattr(attrs, TCP_METRICS_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, + len); + + if (attrs[TCP_METRICS_ATTR_ADDR_IPV4]) { + if (f.daddr.family && f.daddr.family != AF_INET) + return 0; + a = attrs[TCP_METRICS_ATTR_ADDR_IPV4]; + daddr.family = AF_INET; + atype = TCP_METRICS_ATTR_ADDR_IPV4; + } else if (attrs[TCP_METRICS_ATTR_ADDR_IPV6]) { + if (f.daddr.family && f.daddr.family != AF_INET6) + return 0; + a = attrs[TCP_METRICS_ATTR_ADDR_IPV6]; + daddr.family = AF_INET6; + atype = TCP_METRICS_ATTR_ADDR_IPV6; + } else { + return 0; + } + + if (get_addr_rta(&daddr, a, daddr.family)) + return 0; + + if (f.daddr.family && f.daddr.bitlen >= 0 && + inet_addr_match(&daddr, &f.daddr, f.daddr.bitlen)) + return 0; + + if (attrs[TCP_METRICS_ATTR_SADDR_IPV4]) { + if (f.saddr.family && f.saddr.family != AF_INET) + return 0; + a = attrs[TCP_METRICS_ATTR_SADDR_IPV4]; + saddr.family = AF_INET; + stype = TCP_METRICS_ATTR_SADDR_IPV4; + } else if (attrs[TCP_METRICS_ATTR_SADDR_IPV6]) { + if (f.saddr.family && f.saddr.family != AF_INET6) + return 0; + a = attrs[TCP_METRICS_ATTR_SADDR_IPV6]; + saddr.family = AF_INET6; + stype = TCP_METRICS_ATTR_SADDR_IPV6; + } else { + saddr.family = AF_UNSPEC; + stype = 0; + } + + /* Only get/check for the source-address if the kernel supports it. */ + if (saddr.family) { + if (get_addr_rta(&saddr, a, saddr.family)) + return 0; + + if (f.saddr.family && f.saddr.bitlen >= 0 && + inet_addr_match(&saddr, &f.saddr, f.saddr.bitlen)) + return 0; + } + + if (f.flushb) { + struct nlmsghdr *fn; + + TCPM_REQUEST(req2, 128, TCP_METRICS_CMD_DEL, NLM_F_REQUEST); + + addattr_l(&req2.n, sizeof(req2), atype, daddr.data, + daddr.bytelen); + if (saddr.family) + addattr_l(&req2.n, sizeof(req2), stype, saddr.data, + saddr.bytelen); + + if (NLMSG_ALIGN(f.flushp) + req2.n.nlmsg_len > f.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr *) (f.flushb + NLMSG_ALIGN(f.flushp)); + memcpy(fn, &req2.n, req2.n.nlmsg_len); + fn->nlmsg_seq = ++grth.seq; + f.flushp = (((char *) fn) + req2.n.nlmsg_len) - f.flushb; + f.flushed++; + if (show_stats < 2) + return 0; + } + + open_json_object(NULL); + if (f.cmd & (CMD_DEL | CMD_FLUSH)) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + h = format_host(daddr.family, daddr.bytelen, daddr.data); + print_color_string(PRINT_ANY, + ifa_family_color(daddr.family), + "dst", "%s", h); + + a = attrs[TCP_METRICS_ATTR_AGE]; + if (a) { + __u64 val = rta_getattr_u64(a); + double age = val / 1000.; + + print_float(PRINT_ANY, "age", + " age %.03fsec", age); + } + + a = attrs[TCP_METRICS_ATTR_TW_TS_STAMP]; + if (a) { + __s32 val = (__s32) rta_getattr_u32(a); + __u32 tsval; + char tw_ts[64]; + + a = attrs[TCP_METRICS_ATTR_TW_TSVAL]; + tsval = a ? rta_getattr_u32(a) : 0; + snprintf(tw_ts, sizeof(tw_ts), + "%u/%d", tsval, val); + print_string(PRINT_ANY, "tw_ts_stamp", + " tw_ts %s ago", tw_ts); + } + + if (attrs[TCP_METRICS_ATTR_VALS]) + print_tcp_metrics(attrs[TCP_METRICS_ATTR_VALS]); + + a = attrs[TCP_METRICS_ATTR_FOPEN_MSS]; + if (a) { + print_uint(PRINT_ANY, "fopen_miss", " fo_mss %u", + rta_getattr_u16(a)); + } + + a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROPS]; + if (a) { + __u16 syn_loss = rta_getattr_u16(a); + double ts; + + a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS]; + ts = a ? rta_getattr_u64(a) : 0; + + print_uint(PRINT_ANY, "fopen_syn_drops", + " fo_syn_drops %u", syn_loss); + print_float(PRINT_ANY, "fopen_syn_drop_ts", + "/%.03fusec ago", + ts / 1000000.); + } + + a = attrs[TCP_METRICS_ATTR_FOPEN_COOKIE]; + if (a) { + char cookie[32 + 1]; + unsigned char *ptr = RTA_DATA(a); + int i, max = RTA_PAYLOAD(a); + + if (max > 16) + max = 16; + cookie[0] = 0; + for (i = 0; i < max; i++) + sprintf(cookie + i + i, "%02x", ptr[i]); + + print_string(PRINT_ANY, "fo_cookie", + " fo_cookie %s", cookie); + } + + if (saddr.family) { + const char *src; + + src = format_host(saddr.family, saddr.bytelen, saddr.data); + print_string(PRINT_ANY, "source", + " source %s", src); + } + + print_string(PRINT_FP, NULL, "\n", ""); + close_json_object(); + fflush(fp); + return 0; +} + +static int tcpm_do_cmd(int cmd, int argc, char **argv) +{ + TCPM_REQUEST(req, 1024, TCP_METRICS_CMD_GET, NLM_F_REQUEST); + struct nlmsghdr *answer; + int atype = -1, stype = -1; + int ack; + + memset(&f, 0, sizeof(f)); + f.daddr.bitlen = -1; + f.daddr.family = preferred_family; + f.saddr.bitlen = -1; + f.saddr.family = preferred_family; + + switch (preferred_family) { + case AF_UNSPEC: + case AF_INET: + case AF_INET6: + break; + default: + fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family); + return -1; + } + + for (; argc > 0; argc--, argv++) { + if (strcmp(*argv, "src") == 0 || + strcmp(*argv, "source") == 0) { + char *who = *argv; + + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (f.saddr.bitlen >= 0) + duparg2(who, *argv); + + get_prefix(&f.saddr, *argv, preferred_family); + if (f.saddr.bytelen && f.saddr.bytelen * 8 == f.saddr.bitlen) { + if (f.saddr.family == AF_INET) + stype = TCP_METRICS_ATTR_SADDR_IPV4; + else if (f.saddr.family == AF_INET6) + stype = TCP_METRICS_ATTR_SADDR_IPV6; + } + + if (stype < 0) { + fprintf(stderr, "Error: a specific IP address is expected rather than \"%s\"\n", + *argv); + return -1; + } + } else { + char *who = "address"; + + if (strcmp(*argv, "addr") == 0 || + strcmp(*argv, "address") == 0) { + who = *argv; + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (f.daddr.bitlen >= 0) + duparg2(who, *argv); + + get_prefix(&f.daddr, *argv, preferred_family); + if (f.daddr.bytelen && f.daddr.bytelen * 8 == f.daddr.bitlen) { + if (f.daddr.family == AF_INET) + atype = TCP_METRICS_ATTR_ADDR_IPV4; + else if (f.daddr.family == AF_INET6) + atype = TCP_METRICS_ATTR_ADDR_IPV6; + } + if ((CMD_DEL & cmd) && atype < 0) { + fprintf(stderr, "Error: a specific IP address is expected rather than \"%s\"\n", + *argv); + return -1; + } + } + argc--; argv++; + } + + if (cmd == CMD_DEL && atype < 0) + missarg("address"); + + /* flush for exact address ? Single del */ + if (cmd == CMD_FLUSH && atype >= 0) + cmd = CMD_DEL; + + /* flush for all addresses ? Single del without address */ + if (cmd == CMD_FLUSH && f.daddr.bitlen <= 0 && + f.saddr.bitlen <= 0 && preferred_family == AF_UNSPEC) { + cmd = CMD_DEL; + req.g.cmd = TCP_METRICS_CMD_DEL; + ack = 1; + } else if (cmd == CMD_DEL) { + req.g.cmd = TCP_METRICS_CMD_DEL; + ack = 1; + } else { /* CMD_FLUSH, CMD_LIST */ + ack = 0; + } + + if (genl_init_handle(&grth, TCP_METRICS_GENL_NAME, &genl_family)) + exit(1); + req.n.nlmsg_type = genl_family; + + if (!(cmd & CMD_FLUSH) && (atype >= 0 || (cmd & CMD_DEL))) { + if (ack) + req.n.nlmsg_flags |= NLM_F_ACK; + if (atype >= 0) + addattr_l(&req.n, sizeof(req), atype, &f.daddr.data, + f.daddr.bytelen); + if (stype >= 0) + addattr_l(&req.n, sizeof(req), stype, &f.saddr.data, + f.saddr.bytelen); + } else { + req.n.nlmsg_flags |= NLM_F_DUMP; + } + + f.cmd = cmd; + if (cmd & CMD_FLUSH) { + int round = 0; + char flushb[4096-512]; + + f.flushb = flushb; + f.flushp = 0; + f.flushe = sizeof(flushb); + + for (;;) { + req.n.nlmsg_seq = grth.dump = ++grth.seq; + if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) { + perror("Failed to send flush request"); + exit(1); + } + f.flushed = 0; + if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (f.flushed == 0) { + if (round == 0) { + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + 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, f.flushed); + fflush(stdout); + } + } + return 0; + } + + if (ack) { + if (rtnl_talk(&grth, &req.n, NULL) < 0) + return -2; + } else if (atype >= 0) { + if (rtnl_talk(&grth, &req.n, &answer) < 0) + return -2; + if (process_msg(answer, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + free(answer); + } else { + 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(); + } + return 0; +} + +int do_tcp_metrics(int argc, char **argv) +{ + int i; + + if (argc < 1) + return tcpm_do_cmd(CMD_LIST, 0, NULL); + for (i = 0; i < ARRAY_SIZE(cmds); i++) { + if (matches(argv[0], cmds[i].name) == 0) + return tcpm_do_cmd(cmds[i].code, argc-1, argv+1); + } + if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tcp_metrics help\".\n", + *argv); + exit(-1); +}
diff --git a/ip/tunnel.c b/ip/tunnel.c new file mode 100644 index 0000000..88585cf --- /dev/null +++ b/ip/tunnel.c
@@ -0,0 +1,452 @@ +/* + * 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 <http://www.gnu.org/licenses>. + */ +/* + * split from ip_tunnel.c + */ +/* + * Author: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/ip.h> +#include <linux/if_tunnel.h> +#include <linux/if_arp.h> + +#include "utils.h" +#include "tunnel.h" +#include "json_print.h" + +const char *tnl_strproto(__u8 proto) +{ + switch (proto) { + case IPPROTO_IPIP: + return "ip"; + case IPPROTO_GRE: + return "gre"; + case IPPROTO_IPV6: + return "ipv6"; + case IPPROTO_ESP: + return "esp"; + case IPPROTO_MPLS: + return "mpls"; + case 0: + return "any"; + default: + return "unknown"; + } +} + +int tnl_get_ioctl(const char *basedev, void *p) +{ + struct ifreq ifr; + int fd; + int err; + + strlcpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void *)p; + + fd = socket(preferred_family, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "create socket failed: %s\n", strerror(errno)); + return -1; + } + + err = ioctl(fd, SIOCGETTUNNEL, &ifr); + if (err) + fprintf(stderr, "get tunnel \"%s\" failed: %s\n", basedev, + strerror(errno)); + + close(fd); + return err; +} + +int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (cmd == SIOCCHGTUNNEL && name[0]) + strlcpy(ifr.ifr_name, name, IFNAMSIZ); + else + strlcpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = p; + + fd = socket(preferred_family, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "create socket failed: %s\n", strerror(errno)); + return -1; + } + + err = ioctl(fd, cmd, &ifr); + if (err) + fprintf(stderr, "add tunnel \"%s\" failed: %s\n", ifr.ifr_name, + strerror(errno)); + close(fd); + return err; +} + +int tnl_del_ioctl(const char *basedev, const char *name, void *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (name[0]) + strlcpy(ifr.ifr_name, name, IFNAMSIZ); + else + strlcpy(ifr.ifr_name, basedev, IFNAMSIZ); + + ifr.ifr_ifru.ifru_data = p; + + fd = socket(preferred_family, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "create socket failed: %s\n", strerror(errno)); + return -1; + } + + err = ioctl(fd, SIOCDELTUNNEL, &ifr); + if (err) + fprintf(stderr, "delete tunnel \"%s\" failed: %s\n", + ifr.ifr_name, strerror(errno)); + close(fd); + return err; +} + +static int tnl_gen_ioctl(int cmd, const char *name, + void *p, int skiperr) +{ + struct ifreq ifr; + int fd; + int err; + + strlcpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = p; + + fd = socket(preferred_family, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "create socket failed: %s\n", strerror(errno)); + return -1; + } + + err = ioctl(fd, cmd, &ifr); + if (err && errno != skiperr) + fprintf(stderr, "%s: ioctl %x failed: %s\n", name, + cmd, strerror(errno)); + close(fd); + return err; +} + +int tnl_prl_ioctl(int cmd, const char *name, void *p) +{ + return tnl_gen_ioctl(cmd, name, p, -1); +} + +int tnl_6rd_ioctl(int cmd, const char *name, void *p) +{ + return tnl_gen_ioctl(cmd, name, p, -1); +} + +int tnl_ioctl_get_6rd(const char *name, void *p) +{ + return tnl_gen_ioctl(SIOCGET6RD, name, p, EINVAL); +} + +__be32 tnl_parse_key(const char *name, const char *key) +{ + unsigned int uval; + + if (strchr(key, '.')) + return get_addr32(key); + + if (get_unsigned(&uval, key, 0) < 0) { + fprintf(stderr, + "invalid value for \"%s\": \"%s\"; it should be an unsigned integer\n", + name, key); + exit(-1); + } + return htonl(uval); +} + +static const char *tnl_encap_str(const char *name, int enabled, int port) +{ + static const char ne[][sizeof("no")] = { + [0] = "no", + [1] = "", + }; + static char buf[32]; + char b1[16]; + const char *val; + + if (!port) { + val = "auto "; + } else if (port < 0) { + val = ""; + } else { + snprintf(b1, sizeof(b1), "%u ", port - 1); + val = b1; + } + + snprintf(buf, sizeof(buf), "%sencap-%s %s", ne[!!enabled], name, val); + return buf; +} + +void tnl_print_encap(struct rtattr *tb[], + int encap_type, int encap_flags, + int encap_sport, int encap_dport) +{ + __u16 type, flags, sport, dport; + + if (!tb[encap_type]) + return; + + type = rta_getattr_u16(tb[encap_type]); + if (type == TUNNEL_ENCAP_NONE) + return; + + flags = rta_getattr_u16(tb[encap_flags]); + sport = rta_getattr_u16(tb[encap_sport]); + dport = rta_getattr_u16(tb[encap_dport]); + + open_json_object("encap"); + print_string(PRINT_FP, NULL, "encap ", NULL); + + switch (type) { + case TUNNEL_ENCAP_FOU: + print_string(PRINT_ANY, "type", "%s ", "fou"); + break; + case TUNNEL_ENCAP_GUE: + print_string(PRINT_ANY, "type", "%s ", "gue"); + break; + default: + print_null(PRINT_ANY, "type", "%s ", "unknown"); + break; + } + + if (is_json_context()) { + print_uint(PRINT_JSON, "sport", NULL, ntohs(sport)); + print_uint(PRINT_JSON, "dport", NULL, ntohs(dport)); + print_bool(PRINT_JSON, "csum", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM); + print_bool(PRINT_JSON, "csum6", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM6); + print_bool(PRINT_JSON, "remcsum", NULL, + flags & TUNNEL_ENCAP_FLAG_REMCSUM); + close_json_object(); + } else { + int t; + + t = sport ? ntohs(sport) + 1 : 0; + print_string(PRINT_FP, NULL, "%s", + tnl_encap_str("sport", 1, t)); + + t = ntohs(dport) + 1; + print_string(PRINT_FP, NULL, "%s", + tnl_encap_str("dport", 1, t)); + + t = flags & TUNNEL_ENCAP_FLAG_CSUM; + print_string(PRINT_FP, NULL, "%s", + tnl_encap_str("csum", t, -1)); + + t = flags & TUNNEL_ENCAP_FLAG_CSUM6; + print_string(PRINT_FP, NULL, "%s", + tnl_encap_str("csum6", t, -1)); + + t = flags & TUNNEL_ENCAP_FLAG_REMCSUM; + print_string(PRINT_FP, NULL, "%s", + tnl_encap_str("remcsum", t, -1)); + } +} + +void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family) +{ + const char *value; + inet_prefix dst; + + if (!rta) { + value = "any"; + } else if (get_addr_rta(&dst, rta, family)) { + value = "unknown"; + } else if (dst.flags & ADDRTYPE_UNSPEC) { + value = "any"; + } else { + value = format_host(family, dst.bytelen, dst.data); + if (!value) + value = "unknown"; + } + + if (is_json_context()) { + print_string(PRINT_JSON, name, NULL, value); + } else { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%s %%s ", name); + print_string(PRINT_FP, NULL, b1, value); + } +} + +void tnl_print_gre_flags(__u8 proto, + __be16 i_flags, __be16 o_flags, + __be32 i_key, __be32 o_key) +{ + if ((i_flags & GRE_KEY) && (o_flags & GRE_KEY) && + o_key == i_key) { + print_uint(PRINT_ANY, "key", " key %u", ntohl(i_key)); + } else { + if (i_flags & GRE_KEY) + print_uint(PRINT_ANY, "ikey", " ikey %u", ntohl(i_key)); + if (o_flags & GRE_KEY) + print_uint(PRINT_ANY, "okey", " okey %u", ntohl(o_key)); + } + + if (proto != IPPROTO_GRE) + return; + + open_json_array(PRINT_JSON, "flags"); + if (i_flags & GRE_SEQ) { + if (is_json_context()) + print_string(PRINT_JSON, NULL, "%s", "rx_drop_ooseq"); + else + printf("%s Drop packets out of sequence.", _SL_); + } + if (i_flags & GRE_CSUM) { + if (is_json_context()) + print_string(PRINT_JSON, NULL, "%s", "rx_csum"); + else + printf("%s Checksum in received packet is required.", _SL_); + } + if (o_flags & GRE_SEQ) { + if (is_json_context()) + print_string(PRINT_JSON, NULL, "%s", "tx_seq"); + else + printf("%s Sequence packets on output.", _SL_); + } + if (o_flags & GRE_CSUM) { + if (is_json_context()) + print_string(PRINT_JSON, NULL, "%s", "tx_csum"); + else + printf("%s Checksum output packets.", _SL_); + } + close_json_array(PRINT_JSON, NULL); +} + +static void tnl_print_stats(const struct rtnl_link_stats64 *s) +{ + printf("%s", _SL_); + printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); + printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-8lld%s", + s->rx_packets, s->rx_bytes, s->rx_errors, s->rx_frame_errors, + s->rx_fifo_errors, s->multicast, _SL_); + printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); + printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-6lld", + s->tx_packets, s->tx_bytes, s->tx_errors, s->collisions, + s->tx_carrier_errors, s->tx_dropped); +} + +static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg) +{ + struct tnl_print_nlmsg_info *info = arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr *tb[IFLA_MAX+1]; + const char *name, *n1; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) + return -1; + + if (preferred_family == AF_INET) { + switch (ifi->ifi_type) { + case ARPHRD_TUNNEL: + case ARPHRD_IPGRE: + case ARPHRD_SIT: + break; + default: + return 0; + } + } else { + switch (ifi->ifi_type) { + case ARPHRD_TUNNEL6: + case ARPHRD_IP6GRE: + break; + default: + return 0; + } + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + + if (!tb[IFLA_IFNAME]) + return 0; + + name = rta_getattr_str(tb[IFLA_IFNAME]); + + /* Assume p1->name[IFNAMSIZ] is first field of structure */ + n1 = info->p1; + if (n1[0] && strcmp(n1, name)) + return 0; + + info->ifi = ifi; + info->init(info); + + /* TODO: parse netlink attributes */ + if (tnl_get_ioctl(name, info->p2)) + return 0; + + if (!info->match(info)) + return 0; + + info->print(info->p2); + if (show_stats) { + struct rtnl_link_stats64 s; + + if (get_rtnl_link_stats_rta(&s, tb) <= 0) + return -1; + + tnl_print_stats(&s); + } + fputc('\n', stdout); + + return 0; +} + +int do_tunnels_list(struct tnl_print_nlmsg_info *info) +{ + new_json_obj(json); + if (rtnl_linkdump_req(&rth, preferred_family) < 0) { + perror("Cannot send dump request\n"); + return -1; + } + + if (rtnl_dump_filter(&rth, print_nlmsg_tunnel, info) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + delete_json_obj(); + + return 0; +}
diff --git a/ip/tunnel.h b/ip/tunnel.h new file mode 100644 index 0000000..604f8cb --- /dev/null +++ b/ip/tunnel.h
@@ -0,0 +1,62 @@ +/* + * 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 <http://www.gnu.org/licenses>. + */ +/* + * Author: + * Masahide NAKAMURA @USAGI + */ +#ifndef __TUNNEL_H__ +#define __TUNNEL_H__ 1 + +#include <stdbool.h> +#include <linux/types.h> + +struct rtattr; +struct ifinfomsg; + +extern struct rtnl_handle rth; + +struct tnl_print_nlmsg_info { + const struct ifinfomsg *ifi; + const void *p1; + void *p2; + + void (*init)(const struct tnl_print_nlmsg_info *info); + bool (*match)(const struct tnl_print_nlmsg_info *info); + void (*print)(const void *t); +}; + +int do_tunnels_list(struct tnl_print_nlmsg_info *info); + +const char *tnl_strproto(__u8 proto); + +int tnl_get_ioctl(const char *basedev, void *p); +int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p); +int tnl_del_ioctl(const char *basedev, const char *name, void *p); +int tnl_prl_ioctl(int cmd, const char *name, void *p); +int tnl_6rd_ioctl(int cmd, const char *name, void *p); +int tnl_ioctl_get_6rd(const char *name, void *p); +__be32 tnl_parse_key(const char *name, const char *key); +void tnl_print_encap(struct rtattr *tb[], + int encap_type, int encap_flags, + int encap_sport, int encap_dport); +void tnl_print_endpoint(const char *name, + const struct rtattr *rta, int family); +void tnl_print_gre_flags(__u8 proto, + __be16 i_flags, __be16 o_flags, + __be32 i_key, __be32 o_key); + +#endif
diff --git a/ip/xfrm.h b/ip/xfrm.h new file mode 100644 index 0000000..9ba5ca6 --- /dev/null +++ b/ip/xfrm.h
@@ -0,0 +1,145 @@ +/* $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 <http://www.gnu.org/licenses>. + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#ifndef __XFRM_H__ +#define __XFRM_H__ 1 + +#include <stdio.h> +#include <sys/socket.h> +#include <linux/in.h> +#include <linux/xfrm.h> +#include <linux/ipsec.h> + +#ifndef IPPROTO_MH +#define IPPROTO_MH 135 +#endif + +#define XFRMS_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info)))) +#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info)) + +#define XFRMP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info)))) +#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info)) + +#define XFRMSID_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_id)))) +#define XFRMSID_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_id)) + +#define XFRMPID_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_id)))) +#define XFRMPID_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_id)) + +#define XFRMACQ_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_acquire)))) +#define XFRMEXP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_expire)))) +#define XFRMPEXP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire)))) + +#define XFRMREP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_user_report)))) + +#define XFRMSAPD_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(__u32)))) +#define XFRM_FLAG_PRINT(fp, flags, f, s) \ + do { \ + if (flags & f) { \ + flags &= ~f; \ + fprintf(fp, s "%s", (flags ? " " : "")); \ + } \ + } while(0) + +struct xfrm_buffer { + char *buf; + int size; + int offset; + + int nlmsg_count; + struct rtnl_handle *rth; +}; + +struct xfrm_filter { + int use; + + struct xfrm_usersa_info xsinfo; + __u8 id_src_mask; + __u8 id_dst_mask; + __u8 id_proto_mask; + __u32 id_spi_mask; + __u8 mode_mask; + __u32 reqid_mask; + __u8 state_flags_mask; + + struct xfrm_userpolicy_info xpinfo; + __u8 dir_mask; + __u8 sel_src_mask; + __u8 sel_dst_mask; + __u32 sel_dev_mask; + __u8 upspec_proto_mask; + __u16 upspec_sport_mask; + __u16 upspec_dport_mask; + __u32 index_mask; + __u8 action_mask; + __u32 priority_mask; + __u8 policy_flags_mask; + __u8 filter_socket; + + __u8 ptype; + __u8 ptype_mask; + +}; +#define XFRM_FILTER_MASK_FULL (~0) + +extern struct xfrm_filter filter; + +int xfrm_state_print(struct nlmsghdr *n, void *arg); +int xfrm_state_print_nokeys(struct nlmsghdr *n, void *arg); +int xfrm_policy_print(struct nlmsghdr *n, void *arg); +int do_xfrm_state(int argc, char **argv); +int do_xfrm_policy(int argc, char **argv); +int do_xfrm_monitor(int argc, char **argv); + +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits); +int xfrm_xfrmproto_is_ipsec(__u8 proto); +int xfrm_xfrmproto_is_ro(__u8 proto); +int xfrm_xfrmproto_getbyname(char *name); +int xfrm_algotype_getbyname(char *name); +int xfrm_parse_mark(struct xfrm_mark *mark, int *argcp, char ***argvp); +const char *strxf_xfrmproto(__u8 proto); +const char *strxf_algotype(int type); +const char *strxf_mask32(__u32 mask); +const char *strxf_proto(__u8 proto); +const char *strxf_ptype(__u8 ptype); +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix); +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix, bool nokeys); +void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo, + struct rtattr *tb[], FILE *fp, const char *prefix, + const char *title, bool nokeys); +void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo, + struct rtattr *tb[], FILE *fp, const char *prefix, + const char *title); +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp); +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp); +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp); +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp); +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp); +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp); +int xfrm_sctx_parse(char *ctxstr, char *context, + struct xfrm_user_sec_ctx *sctx); +#endif
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c new file mode 100644 index 0000000..e34b5fb --- /dev/null +++ b/ip/xfrm_monitor.c
@@ -0,0 +1,426 @@ +/* $USAGI: $ */ + +/* + * 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 <http://www.gnu.org/licenses>. + */ +/* + * based on ipmonitor.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +static void usage(void) __attribute__((noreturn)); +static int listen_all_nsid; +static bool nokeys; + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm monitor [ nokeys ] [ all-nsid ] [ all | OBJECTS | help ]\n" + "OBJECTS := { acquire | expire | SA | aevent | policy | report }\n"); + exit(-1); +} + +static int xfrm_acquire_print(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct xfrm_user_acquire *xacq = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[XFRMA_MAX+1]; + __u16 family; + + len -= NLMSG_LENGTH(sizeof(*xacq)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + parse_rtattr(tb, XFRMA_MAX, XFRMACQ_RTA(xacq), len); + + family = xacq->sel.family; + if (family == AF_UNSPEC) + family = xacq->policy.sel.family; + if (family == AF_UNSPEC) + family = preferred_family; + + fprintf(fp, "acquire "); + + fprintf(fp, "proto %s ", strxf_xfrmproto(xacq->id.proto)); + if (show_stats > 0 || xacq->id.spi) { + __u32 spi = ntohl(xacq->id.spi); + + fprintf(fp, "spi 0x%08x", spi); + if (show_stats > 0) + fprintf(fp, "(%u)", spi); + fprintf(fp, " "); + } + fprintf(fp, "%s", _SL_); + + xfrm_selector_print(&xacq->sel, family, fp, " sel "); + + xfrm_policy_info_print(&xacq->policy, tb, fp, " ", " policy "); + + if (show_stats > 0) + fprintf(fp, " seq 0x%08u ", xacq->seq); + if (show_stats > 0) { + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_mask32(xacq->ealgos)); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_AUTH), + strxf_mask32(xacq->aalgos)); + fprintf(fp, "%s-mask %s", + strxf_algotype(XFRMA_ALG_COMP), + strxf_mask32(xacq->calgos)); + } + fprintf(fp, "%s", _SL_); + + if (oneline) + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +static int xfrm_state_flush_print(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct xfrm_usersa_flush *xsf = NLMSG_DATA(n); + int len = n->nlmsg_len; + const char *str; + + len -= NLMSG_SPACE(sizeof(*xsf)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + fprintf(fp, "Flushed state "); + + str = strxf_xfrmproto(xsf->proto); + if (str) + fprintf(fp, "proto %s", str); + else + fprintf(fp, "proto %u", xsf->proto); + fprintf(fp, "%s", _SL_); + + if (oneline) + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +static int xfrm_policy_flush_print(struct nlmsghdr *n, void *arg) +{ + struct rtattr *tb[XFRMA_MAX+1]; + FILE *fp = (FILE *)arg; + int len = n->nlmsg_len; + + len -= NLMSG_SPACE(0); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + fprintf(fp, "Flushed policy "); + + parse_rtattr(tb, XFRMA_MAX, NLMSG_DATA(n), len); + + if (tb[XFRMA_POLICY_TYPE]) { + struct xfrm_userpolicy_type *upt; + + fprintf(fp, "ptype "); + + if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) + fprintf(fp, "(ERROR truncated)"); + + upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]); + fprintf(fp, "%s ", strxf_ptype(upt->type)); + } + + fprintf(fp, "%s", _SL_); + + if (oneline) + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +static int xfrm_report_print(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct xfrm_user_report *xrep = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[XFRMA_MAX+1]; + __u16 family; + + len -= NLMSG_LENGTH(sizeof(*xrep)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + family = xrep->sel.family; + if (family == AF_UNSPEC) + family = preferred_family; + + fprintf(fp, "report "); + + fprintf(fp, "proto %s ", strxf_xfrmproto(xrep->proto)); + fprintf(fp, "%s", _SL_); + + xfrm_selector_print(&xrep->sel, family, fp, " sel "); + + parse_rtattr(tb, XFRMA_MAX, XFRMREP_RTA(xrep), len); + + xfrm_xfrma_print(tb, family, fp, " ", nokeys); + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static void xfrm_ae_flags_print(__u32 flags, void *arg) +{ + FILE *fp = (FILE *)arg; + + fprintf(fp, " (0x%x) ", flags); + if (!flags) + return; + if (flags & XFRM_AE_CR) + fprintf(fp, " replay update "); + if (flags & XFRM_AE_CE) + fprintf(fp, " timer expired "); + if (flags & XFRM_AE_CU) + fprintf(fp, " policy updated "); + +} + +static void xfrm_usersa_print(const struct xfrm_usersa_id *sa_id, __u32 reqid, FILE *fp) +{ + fprintf(fp, "dst %s ", + rt_addr_n2a(sa_id->family, sizeof(sa_id->daddr), &sa_id->daddr)); + + fprintf(fp, " reqid 0x%x", reqid); + + fprintf(fp, " protocol %s ", strxf_proto(sa_id->proto)); + fprintf(fp, " SPI 0x%x", ntohl(sa_id->spi)); +} + +static int xfrm_ae_print(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct xfrm_aevent_id *id = NLMSG_DATA(n); + + fprintf(fp, "Async event "); + xfrm_ae_flags_print(id->flags, arg); + fprintf(fp, "\n\t"); + fprintf(fp, "src %s ", rt_addr_n2a(id->sa_id.family, + sizeof(id->saddr), &id->saddr)); + + xfrm_usersa_print(&id->sa_id, id->reqid, fp); + + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +static void xfrm_print_addr(FILE *fp, int family, xfrm_address_t *a) +{ + fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*a), a)); +} + +static int xfrm_mapping_print(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct xfrm_user_mapping *map = NLMSG_DATA(n); + + fprintf(fp, "Mapping change "); + xfrm_print_addr(fp, map->id.family, &map->old_saddr); + + fprintf(fp, ":%d -> ", ntohs(map->old_sport)); + xfrm_print_addr(fp, map->id.family, &map->new_saddr); + fprintf(fp, ":%d\n\t", ntohs(map->new_sport)); + + xfrm_usersa_print(&map->id, map->reqid, fp); + + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int xfrm_accept_msg(struct rtnl_ctrl_data *ctrl, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + + 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); + } + + switch (n->nlmsg_type) { + case XFRM_MSG_NEWSA: + case XFRM_MSG_DELSA: + case XFRM_MSG_UPDSA: + case XFRM_MSG_EXPIRE: + xfrm_state_print(n, arg); + return 0; + case XFRM_MSG_NEWPOLICY: + case XFRM_MSG_DELPOLICY: + case XFRM_MSG_UPDPOLICY: + case XFRM_MSG_POLEXPIRE: + xfrm_policy_print(n, arg); + return 0; + case XFRM_MSG_ACQUIRE: + xfrm_acquire_print(n, arg); + return 0; + case XFRM_MSG_FLUSHSA: + xfrm_state_flush_print(n, arg); + return 0; + case XFRM_MSG_FLUSHPOLICY: + xfrm_policy_flush_print(n, arg); + return 0; + case XFRM_MSG_REPORT: + xfrm_report_print(n, arg); + return 0; + case XFRM_MSG_NEWAE: + xfrm_ae_print(n, arg); + return 0; + case XFRM_MSG_MAPPING: + xfrm_mapping_print(n, arg); + return 0; + default: + break; + } + + if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP && + n->nlmsg_type != NLMSG_DONE) { + fprintf(fp, "Unknown message: %08d 0x%08x 0x%08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + } + return 0; +} + +extern struct rtnl_handle rth; + +int do_xfrm_monitor(int argc, char **argv) +{ + char *file = NULL; + unsigned int groups = ~((unsigned)0); /* XXX */ + int lacquire = 0; + int lexpire = 0; + int laevent = 0; + int lpolicy = 0; + int lsa = 0; + int lreport = 0; + + rtnl_close(&rth); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (strcmp(*argv, "nokeys") == 0) { + nokeys = true; + } else if (strcmp(*argv, "all") == 0) { + /* fall out */ + } else if (matches(*argv, "all-nsid") == 0) { + listen_all_nsid = 1; + } else if (matches(*argv, "acquire") == 0) { + lacquire = 1; + groups = 0; + } else if (matches(*argv, "expire") == 0) { + lexpire = 1; + groups = 0; + } else if (matches(*argv, "SA") == 0) { + lsa = 1; + groups = 0; + } else if (matches(*argv, "aevent") == 0) { + laevent = 1; + groups = 0; + } else if (matches(*argv, "policy") == 0) { + lpolicy = 1; + groups = 0; + } else if (matches(*argv, "report") == 0) { + lreport = 1; + groups = 0; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"ip xfrm monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + if (lacquire) + groups |= nl_mgrp(XFRMNLGRP_ACQUIRE); + if (lexpire) + groups |= nl_mgrp(XFRMNLGRP_EXPIRE); + if (lsa) + groups |= nl_mgrp(XFRMNLGRP_SA); + if (lpolicy) + groups |= nl_mgrp(XFRMNLGRP_POLICY); + if (laevent) + groups |= nl_mgrp(XFRMNLGRP_AEVENTS); + if (lreport) + groups |= nl_mgrp(XFRMNLGRP_REPORT); + + if (file) { + FILE *fp; + int err; + + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + err = rtnl_from_file(fp, xfrm_accept_msg, stdout); + fclose(fp); + return err; + } + + if (rtnl_open_byproto(&rth, groups, NETLINK_XFRM) < 0) + exit(1); + if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0) + exit(1); + + if (rtnl_listen(&rth, xfrm_accept_msg, (void *)stdout) < 0) + exit(2); + + return 0; +}
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c new file mode 100644 index 0000000..7c0233c --- /dev/null +++ b/ip/xfrm_policy.c
@@ -0,0 +1,1204 @@ +/* $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 <http://www.gnu.org/licenses>. + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/netlink.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */ +#define NLMSG_DELETEALL_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_userpolicy_info + * rtattr + * data = struct xfrm_user_tmpl[] + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_TMPLS_BUF_SIZE 1024 +#define CTX_BUF_SIZE 256 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm policy { add | update } SELECTOR dir DIR [ ctx CTX ]\n" + " [ mark MARK [ mask MASK ] ] [ index INDEX ] [ ptype PTYPE ]\n" + " [ action ACTION ] [ priority PRIORITY ] [ flag FLAG-LIST ]\n" + " [ if_id IF_ID ] [ LIMIT-LIST ] [ TMPL-LIST ]\n" + "Usage: ip xfrm policy { delete | get } { SELECTOR | index INDEX } dir DIR\n" + " [ ctx CTX ] [ mark MARK [ mask MASK ] ] [ ptype PTYPE ]\n" + "Usage: ip xfrm policy { deleteall | list } [ nosock ] [ SELECTOR ] [ dir DIR ]\n" + " [ index INDEX ] [ ptype PTYPE ] [ action ACTION ] [ priority PRIORITY ]\n" + " [ flag FLAG-LIST ]\n" + "Usage: ip xfrm policy flush [ ptype PTYPE ]\n" + "Usage: ip xfrm policy count\n" + "Usage: ip xfrm policy set [ hthresh4 LBITS RBITS ] [ hthresh6 LBITS RBITS ]\n" + "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n" + "UPSPEC := proto { { "); + fprintf(stderr, "%s | %s | %s | %s } ", + strxf_proto(IPPROTO_TCP), + strxf_proto(IPPROTO_UDP), + strxf_proto(IPPROTO_SCTP), + strxf_proto(IPPROTO_DCCP)); + fprintf(stderr, + "[ sport PORT ] [ dport PORT ] |\n" + " { %s | %s | %s } ", + strxf_proto(IPPROTO_ICMP), + strxf_proto(IPPROTO_ICMPV6), + strxf_proto(IPPROTO_MH)); + fprintf(stderr, + "[ type NUMBER ] [ code NUMBER ] |\n" + " %s", + strxf_proto(IPPROTO_GRE)); + fprintf(stderr, + " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n" + "DIR := in | out | fwd\n" + "PTYPE := main | sub\n" + "ACTION := allow | block\n" + "FLAG-LIST := [ FLAG-LIST ] FLAG\n" + "FLAG := localok | icmp\n" + "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n" + "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n" + " { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n" + "TMPL-LIST := [ TMPL-LIST ] tmpl TMPL\n" + "TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n" + "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n" + "XFRM-PROTO := "); + fprintf(stderr, + "%s | %s | %s | %s | %s\n", + strxf_xfrmproto(IPPROTO_ESP), + strxf_xfrmproto(IPPROTO_AH), + strxf_xfrmproto(IPPROTO_COMP), + strxf_xfrmproto(IPPROTO_ROUTING), + strxf_xfrmproto(IPPROTO_DSTOPTS)); + fprintf(stderr, + "MODE := transport | tunnel | beet | ro | in_trigger\n" + "LEVEL := required | use\n"); + + exit(-1); +} + +static int xfrm_policy_dir_parse(__u8 *dir, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "in") == 0) + *dir = XFRM_POLICY_IN; + else if (strcmp(*argv, "out") == 0) + *dir = XFRM_POLICY_OUT; + else if (strcmp(*argv, "fwd") == 0) + *dir = XFRM_POLICY_FWD; + else + invarg("DIR value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_policy_ptype_parse(__u8 *ptype, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "main") == 0) + *ptype = XFRM_POLICY_TYPE_MAIN; + else if (strcmp(*argv, "sub") == 0) + *ptype = XFRM_POLICY_TYPE_SUB; + else + invarg("PTYPE value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_policy_flag_parse(__u8 *flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int len = strlen(*argv); + + if (len > 2 && strncmp(*argv, "0x", 2) == 0) { + __u8 val = 0; + + if (get_u8(&val, *argv, 16)) + invarg("FLAG value is invalid", *argv); + *flags = val; + } else { + while (1) { + if (strcmp(*argv, "localok") == 0) + *flags |= XFRM_POLICY_LOCALOK; + else if (strcmp(*argv, "icmp") == 0) + *flags |= XFRM_POLICY_ICMP; + else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *idp = NULL; + + while (1) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&tmpl->mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&tmpl->reqid, &argc, &argv); + } else if (strcmp(*argv, "level") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "required") == 0) + tmpl->optional = 0; + else if (strcmp(*argv, "use") == 0) + tmpl->optional = 1; + else + invarg("LEVEL value is invalid\n", *argv); + + } else { + if (idp) { + PREV_ARG(); /* back track */ + break; + } + idp = *argv; + preferred_family = AF_UNSPEC; + xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family, + 0, &argc, &argv); + preferred_family = tmpl->family; + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + if (argc == *argcp) + missarg("TMPL"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_sctx_parse(char *ctxstr, char *s, + struct xfrm_user_sec_ctx *sctx) +{ + int slen; + + slen = strlen(s) + 1; + + sctx->exttype = XFRMA_SEC_CTX; + sctx->ctx_doi = 1; + sctx->ctx_alg = 1; + sctx->ctx_len = slen; + sctx->len = sizeof(struct xfrm_user_sec_ctx) + slen; + memcpy(ctxstr, s, slen); + + return 0; +} + +static int xfrm_policy_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_info xpinfo; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .xpinfo.sel.family = preferred_family, + .xpinfo.lft.soft_byte_limit = XFRM_INF, + .xpinfo.lft.hard_byte_limit = XFRM_INF, + .xpinfo.lft.soft_packet_limit = XFRM_INF, + .xpinfo.lft.hard_packet_limit = XFRM_INF, + }; + char *dirp = NULL; + char *selp = NULL; + char *ptypep = NULL; + char *sctxp = NULL; + struct xfrm_userpolicy_type upt = {}; + char tmpls_buf[XFRM_TMPLS_BUF_SIZE] = {}; + int tmpls_len = 0; + struct xfrm_mark mark = {0, 0}; + struct { + struct xfrm_user_sec_ctx sctx; + char str[CTX_BUF_SIZE]; + } ctx = {}; + bool is_if_id_set = false; + __u32 if_id = 0; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv); + } else if (strcmp(*argv, "ctx") == 0) { + char *context; + + if (sctxp) + duparg("ctx", *argv); + sctxp = *argv; + NEXT_ARG(); + context = *argv; + xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); + } else if (strcmp(*argv, "mark") == 0) { + xfrm_parse_mark(&mark, &argc, &argv); + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.index, *argv, 0)) + invarg("INDEX value is invalid", *argv); + } else if (strcmp(*argv, "ptype") == 0) { + if (ptypep) + duparg("ptype", *argv); + ptypep = *argv; + + NEXT_ARG(); + xfrm_policy_ptype_parse(&upt.type, &argc, &argv); + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + req.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + req.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("ACTION value is invalid\n", *argv); + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.priority, *argv, 0)) + invarg("PRIORITY value is invalid", *argv); + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_policy_flag_parse(&req.xpinfo.flags, &argc, + &argv); + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xpinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "tmpl") == 0) { + struct xfrm_user_tmpl *tmpl; + + if (tmpls_len + sizeof(*tmpl) > sizeof(tmpls_buf)) { + fprintf(stderr, "Too many tmpls: buffer overflow\n"); + exit(1); + } + tmpl = (struct xfrm_user_tmpl *)((char *)tmpls_buf + tmpls_len); + + tmpl->family = preferred_family; + tmpl->aalgos = (~(__u32)0); + tmpl->ealgos = (~(__u32)0); + tmpl->calgos = (~(__u32)0); + + NEXT_ARG(); + xfrm_tmpl_parse(tmpl, &argc, &argv); + + tmpls_len += sizeof(*tmpl); + } else if (strcmp(*argv, "if_id") == 0) { + NEXT_ARG(); + if (get_u32(&if_id, *argv, 0)) + invarg("IF_ID value is invalid", *argv); + is_if_id_set = true; + } else { + if (selp) + duparg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpinfo.sel.family; + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: DIR is required.\n"); + exit(1); + } + + if (ptypep) { + addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE, + (void *)&upt, sizeof(upt)); + } + + if (tmpls_len > 0) { + addattr_l(&req.n, sizeof(req), XFRMA_TMPL, + (void *)tmpls_buf, tmpls_len); + } + + if (mark.m) { + int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, + (void *)&mark, sizeof(mark)); + if (r < 0) { + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); + exit(1); + } + } + + if (sctxp) { + addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX, + (void *)&ctx, ctx.sctx.len); + } + + if (is_if_id_set) + addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpinfo.sel.family == AF_UNSPEC) + req.xpinfo.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo, + __u8 ptype) +{ + if (!filter.use) + return 1; + + if (filter.xpinfo.sel.family != AF_UNSPEC && + filter.xpinfo.sel.family != xpinfo->sel.family) + return 0; + + if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask) + return 0; + + if (filter.filter_socket && (xpinfo->dir >= XFRM_POLICY_MAX)) + return 0; + + if ((ptype^filter.ptype)&filter.ptype_mask) + return 0; + + if (filter.sel_src_mask) { + if (xfrm_addr_match(&xpinfo->sel.saddr, &filter.xpinfo.sel.saddr, + filter.sel_src_mask)) + return 0; + } + + if (filter.sel_dst_mask) { + if (xfrm_addr_match(&xpinfo->sel.daddr, &filter.xpinfo.sel.daddr, + filter.sel_dst_mask)) + return 0; + } + + if ((xpinfo->sel.ifindex^filter.xpinfo.sel.ifindex)&filter.sel_dev_mask) + return 0; + + if ((xpinfo->sel.proto^filter.xpinfo.sel.proto)&filter.upspec_proto_mask) + return 0; + + if (filter.upspec_sport_mask) { + if ((xpinfo->sel.sport^filter.xpinfo.sel.sport)&filter.upspec_sport_mask) + return 0; + } + + if (filter.upspec_dport_mask) { + if ((xpinfo->sel.dport^filter.xpinfo.sel.dport)&filter.upspec_dport_mask) + return 0; + } + + if ((xpinfo->index^filter.xpinfo.index)&filter.index_mask) + return 0; + + if ((xpinfo->action^filter.xpinfo.action)&filter.action_mask) + return 0; + + if ((xpinfo->priority^filter.xpinfo.priority)&filter.priority_mask) + return 0; + + if (filter.policy_flags_mask) + if ((xpinfo->flags & filter.xpinfo.flags) == 0) + return 0; + + return 1; +} + +int xfrm_policy_print(struct nlmsghdr *n, void *arg) +{ + struct rtattr *tb[XFRMA_MAX+1]; + struct rtattr *rta; + struct xfrm_userpolicy_info *xpinfo = NULL; + struct xfrm_user_polexpire *xpexp = NULL; + struct xfrm_userpolicy_id *xpid = NULL; + __u8 ptype = XFRM_POLICY_TYPE_MAIN; + FILE *fp = (FILE *)arg; + int len = n->nlmsg_len; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY && + n->nlmsg_type != XFRM_MSG_DELPOLICY && + n->nlmsg_type != XFRM_MSG_UPDPOLICY && + n->nlmsg_type != XFRM_MSG_POLEXPIRE) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) { + xpid = NLMSG_DATA(n); + len -= NLMSG_SPACE(sizeof(*xpid)); + } else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) { + xpexp = NLMSG_DATA(n); + xpinfo = &xpexp->pol; + len -= NLMSG_SPACE(sizeof(*xpexp)); + } else { + xpexp = NULL; + xpinfo = NLMSG_DATA(n); + len -= NLMSG_SPACE(sizeof(*xpinfo)); + } + + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + rta = XFRMPID_RTA(xpid); + else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) + rta = XFRMPEXP_RTA(xpexp); + else + rta = XFRMP_RTA(xpinfo); + + parse_rtattr(tb, XFRMA_MAX, rta, len); + + if (tb[XFRMA_POLICY_TYPE]) { + struct xfrm_userpolicy_type *upt; + + if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) { + fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n"); + return -1; + } + upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]); + ptype = upt->type; + } + + if (xpinfo && !xfrm_policy_filter_match(xpinfo, ptype)) + return 0; + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + fprintf(fp, "Deleted "); + else if (n->nlmsg_type == XFRM_MSG_UPDPOLICY) + fprintf(fp, "Updated "); + else if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) + fprintf(fp, "Expired "); + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) { + /* xfrm_policy_id_print(); */ + if (!tb[XFRMA_POLICY]) { + fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: no XFRMA_POLICY\n"); + return -1; + } + if (RTA_PAYLOAD(tb[XFRMA_POLICY]) < sizeof(*xpinfo)) { + fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n"); + return -1; + } + xpinfo = RTA_DATA(tb[XFRMA_POLICY]); + } + + xfrm_policy_info_print(xpinfo, tb, fp, NULL, NULL); + + if (n->nlmsg_type == XFRM_MSG_POLEXPIRE) { + fprintf(fp, "\t"); + fprintf(fp, "hard %u", xpexp->hard); + fprintf(fp, "%s", _SL_); + } + + if (oneline) + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, + struct nlmsghdr **answer) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id xpid; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY + : XFRM_MSG_GETPOLICY, + }; + char *dirp = NULL; + char *selp = NULL; + char *indexp = NULL; + char *ptypep = NULL; + char *sctxp = NULL; + struct xfrm_userpolicy_type upt = {}; + struct xfrm_mark mark = {0, 0}; + struct { + struct xfrm_user_sec_ctx sctx; + char str[CTX_BUF_SIZE]; + } ctx = {}; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv); + + } else if (strcmp(*argv, "ctx") == 0) { + char *context; + + if (sctxp) + duparg("ctx", *argv); + sctxp = *argv; + NEXT_ARG(); + context = *argv; + xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); + } else if (strcmp(*argv, "mark") == 0) { + xfrm_parse_mark(&mark, &argc, &argv); + } else if (strcmp(*argv, "index") == 0) { + if (indexp) + duparg("index", *argv); + indexp = *argv; + + NEXT_ARG(); + if (get_u32(&req.xpid.index, *argv, 0)) + invarg("INDEX value is invalid", *argv); + + } else if (strcmp(*argv, "ptype") == 0) { + if (ptypep) + duparg("ptype", *argv); + ptypep = *argv; + + NEXT_ARG(); + xfrm_policy_ptype_parse(&upt.type, &argc, &argv); + + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpid.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpid.sel.family; + + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: DIR is required.\n"); + exit(1); + } + if (ptypep) { + addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE, + (void *)&upt, sizeof(upt)); + } + if (!selp && !indexp) { + fprintf(stderr, "Not enough information: either SELECTOR or INDEX is required.\n"); + exit(1); + } + if (selp && indexp) + duparg2("SELECTOR", "INDEX"); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpid.sel.family == AF_UNSPEC) + req.xpid.sel.family = AF_INET; + + if (mark.m & mark.v) { + int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, + (void *)&mark, sizeof(mark)); + if (r < 0) { + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); + exit(1); + } + } + + if (sctxp) { + addattr_l(&req.n, sizeof(req), XFRMA_SEC_CTX, + (void *)&ctx, ctx.sctx.len); + } + + if (rtnl_talk(&rth, &req.n, answer) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_delete(int argc, char **argv) +{ + return xfrm_policy_get_or_delete(argc, argv, 1, NULL); +} + +static int xfrm_policy_get(int argc, char **argv) +{ + struct nlmsghdr *n = NULL; + + xfrm_policy_get_or_delete(argc, argv, 0, &n); + + if (xfrm_policy_print(n, (void *)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + free(n); + return 0; +} + +/* + * With an existing policy of nlmsg, make new nlmsg for deleting the policy + * and store it to buffer. + */ +static int xfrm_policy_keep(struct nlmsghdr *n, void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[XFRMA_MAX+1]; + __u8 ptype = XFRM_POLICY_TYPE_MAIN; + struct nlmsghdr *new_n; + struct xfrm_userpolicy_id *xpid; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + parse_rtattr(tb, XFRMA_MAX, XFRMP_RTA(xpinfo), len); + + if (tb[XFRMA_POLICY_TYPE]) { + struct xfrm_userpolicy_type *upt; + + if (RTA_PAYLOAD(tb[XFRMA_POLICY_TYPE]) < sizeof(*upt)) { + fprintf(stderr, "too short XFRMA_POLICY_TYPE len\n"); + return -1; + } + upt = RTA_DATA(tb[XFRMA_POLICY_TYPE]); + ptype = upt->type; + } + + if (!xfrm_policy_filter_match(xpinfo, ptype)) + return 0; + + /* can't delete socket policies */ + if (xpinfo->dir >= XFRM_POLICY_MAX) + return 0; + + if (xb->offset + NLMSG_LENGTH(sizeof(*xpid)) > xb->size) + return 0; + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELPOLICY; + new_n->nlmsg_seq = ++rth->seq; + + xpid = NLMSG_DATA(new_n); + memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel)); + xpid->dir = xpinfo->dir; + xpid->index = xpinfo->index; + + if (tb[XFRMA_MARK]) { + int r = addattr_l(new_n, xb->size, XFRMA_MARK, + (void *)RTA_DATA(tb[XFRMA_MARK]), tb[XFRMA_MARK]->rta_len); + if (r < 0) { + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); + exit(1); + } + } + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count++; + + return 0; +} + +static int xfrm_policy_list_or_deleteall(int argc, char **argv, int deleteall) +{ + char *selp = NULL; + struct rtnl_handle rth; + + if (argc > 0 || preferred_family != AF_UNSPEC) + filter.use = 1; + filter.xpinfo.sel.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + NEXT_ARG(); + xfrm_policy_dir_parse(&filter.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.index, *argv, 0)) + invarg("INDEX value is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "ptype") == 0) { + NEXT_ARG(); + xfrm_policy_ptype_parse(&filter.ptype, &argc, &argv); + + filter.ptype_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + filter.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + filter.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("ACTION value is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.priority, *argv, 0)) + invarg("PRIORITY value is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_policy_flag_parse(&filter.xpinfo.flags, &argc, + &argv); + + filter.policy_flags_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "nosock") == 0) { + /* filter all socket-based policies */ + filter.filter_socket = 1; + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&filter.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xpinfo.sel.family; + + } + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (deleteall) { + struct xfrm_buffer xb; + char buf[NLMSG_DELETEALL_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + struct { + struct nlmsghdr n; + char buf[NLMSG_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_HDRLEN, + .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETPOLICY, + .n.nlmsg_seq = rth.dump = ++rth.seq, + }; + + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Delete-all round = %d\n", i); + + if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb) < 0) { + fprintf(stderr, "Delete-all terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Delete-all completed\n"); + break; + } + + if (rtnl_send_check(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send delete-all request"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + } else { + struct { + struct nlmsghdr n; + char buf[NLMSG_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_HDRLEN, + .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETPOLICY, + .n.nlmsg_seq = rth.dump = ++rth.seq, + }; + + if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int print_spdinfo(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + __u32 *f = NLMSG_DATA(n); + struct rtattr *tb[XFRMA_SPD_MAX+1]; + struct rtattr *rta; + + int len = n->nlmsg_len; + + len -= NLMSG_LENGTH(sizeof(__u32)); + if (len < 0) { + fprintf(stderr, "SPDinfo: Wrong len %d\n", len); + return -1; + } + + rta = XFRMSAPD_RTA(f); + parse_rtattr(tb, XFRMA_SPD_MAX, rta, len); + + fprintf(fp, "\t SPD"); + if (tb[XFRMA_SPD_INFO]) { + struct xfrmu_spdinfo *si; + + if (RTA_PAYLOAD(tb[XFRMA_SPD_INFO]) < sizeof(*si)) { + fprintf(stderr, "SPDinfo: Wrong len %d\n", len); + return -1; + } + si = RTA_DATA(tb[XFRMA_SPD_INFO]); + fprintf(fp, " IN %d", si->incnt); + fprintf(fp, " OUT %d", si->outcnt); + fprintf(fp, " FWD %d", si->fwdcnt); + + if (show_stats) { + fprintf(fp, " (Sock:"); + fprintf(fp, " IN %d", si->inscnt); + fprintf(fp, " OUT %d", si->outscnt); + fprintf(fp, " FWD %d", si->fwdscnt); + fprintf(fp, ")"); + } + + fprintf(fp, "%s", _SL_); + } + if (show_stats > 1) { + struct xfrmu_spdhinfo *sh; + + if (tb[XFRMA_SPD_HINFO]) { + if (RTA_PAYLOAD(tb[XFRMA_SPD_HINFO]) < sizeof(*sh)) { + fprintf(stderr, "SPDinfo: Wrong len %d\n", len); + return -1; + } + sh = RTA_DATA(tb[XFRMA_SPD_HINFO]); + fprintf(fp, "\t SPD buckets:"); + fprintf(fp, " count %d", sh->spdhcnt); + fprintf(fp, " Max %d", sh->spdhmcnt); + fprintf(fp, "%s", _SL_); + } + if (tb[XFRMA_SPD_IPV4_HTHRESH]) { + struct xfrmu_spdhthresh *th; + + if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV4_HTHRESH]) < sizeof(*th)) { + fprintf(stderr, "SPDinfo: Wrong len %d\n", len); + return -1; + } + th = RTA_DATA(tb[XFRMA_SPD_IPV4_HTHRESH]); + fprintf(fp, "\t SPD IPv4 thresholds:"); + fprintf(fp, " local %d", th->lbits); + fprintf(fp, " remote %d", th->rbits); + fprintf(fp, "%s", _SL_); + + } + if (tb[XFRMA_SPD_IPV6_HTHRESH]) { + struct xfrmu_spdhthresh *th; + + if (RTA_PAYLOAD(tb[XFRMA_SPD_IPV6_HTHRESH]) < sizeof(*th)) { + fprintf(stderr, "SPDinfo: Wrong len %d\n", len); + return -1; + } + th = RTA_DATA(tb[XFRMA_SPD_IPV6_HTHRESH]); + fprintf(fp, "\t SPD IPv6 thresholds:"); + fprintf(fp, " local %d", th->lbits); + fprintf(fp, " remote %d", th->rbits); + fprintf(fp, "%s", _SL_); + } + } + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_spd_setinfo(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + __u32 flags; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_NEWSPDINFO, + .flags = 0XFFFFFFFF, + }; + + char *thr4 = NULL; + char *thr6 = NULL; + + while (argc > 0) { + if (strcmp(*argv, "hthresh4") == 0) { + struct xfrmu_spdhthresh thr; + + if (thr4) + duparg("hthresh4", *argv); + thr4 = *argv; + NEXT_ARG(); + if (get_u8(&thr.lbits, *argv, 0) || thr.lbits > 32) + invarg("hthresh4 LBITS value is invalid", *argv); + NEXT_ARG(); + if (get_u8(&thr.rbits, *argv, 0) || thr.rbits > 32) + invarg("hthresh4 RBITS value is invalid", *argv); + + addattr_l(&req.n, sizeof(req), XFRMA_SPD_IPV4_HTHRESH, + (void *)&thr, sizeof(thr)); + } else if (strcmp(*argv, "hthresh6") == 0) { + struct xfrmu_spdhthresh thr; + + if (thr6) + duparg("hthresh6", *argv); + thr6 = *argv; + NEXT_ARG(); + if (get_u8(&thr.lbits, *argv, 0) || thr.lbits > 128) + invarg("hthresh6 LBITS value is invalid", *argv); + NEXT_ARG(); + if (get_u8(&thr.rbits, *argv, 0) || thr.rbits > 128) + invarg("hthresh6 RBITS value is invalid", *argv); + + addattr_l(&req.n, sizeof(req), XFRMA_SPD_IPV6_HTHRESH, + (void *)&thr, sizeof(thr)); + } else { + invarg("unknown", *argv); + } + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_spd_getinfo(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + __u32 flags; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(__u32)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSPDINFO, + .flags = 0XFFFFFFFF, + }; + struct nlmsghdr *answer; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + exit(2); + + print_spdinfo(answer, (void *)stdout); + + free(answer); + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_flush(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(0), /* nlmsg data is nothing */ + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_FLUSHPOLICY, + }; + char *ptypep = NULL; + struct xfrm_userpolicy_type upt = {}; + + while (argc > 0) { + if (strcmp(*argv, "ptype") == 0) { + if (ptypep) + duparg("ptype", *argv); + ptypep = *argv; + + NEXT_ARG(); + xfrm_policy_ptype_parse(&upt.type, &argc, &argv); + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (ptypep) { + addattr_l(&req.n, sizeof(req), XFRMA_POLICY_TYPE, + (void *)&upt, sizeof(upt)); + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush policy\n"); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_policy(int argc, char **argv) +{ + if (argc < 1) + return xfrm_policy_list_or_deleteall(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_policy_modify(XFRM_MSG_UPDPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return xfrm_policy_delete(argc-1, argv+1); + if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0) + return xfrm_policy_list_or_deleteall(argc-1, argv+1, 1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_policy_list_or_deleteall(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_policy_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return xfrm_policy_flush(argc-1, argv+1); + if (matches(*argv, "count") == 0) + return xfrm_spd_getinfo(argc, argv); + if (matches(*argv, "set") == 0) + return xfrm_spd_setinfo(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv); + exit(-1); +}
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c new file mode 100644 index 0000000..b03ccc5 --- /dev/null +++ b/ip/xfrm_state.c
@@ -0,0 +1,1488 @@ +/* $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 <http://www.gnu.org/licenses>. + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +/* #define NLMSG_DELETEALL_BUF_SIZE (4096-512) */ +#define NLMSG_DELETEALL_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_usersa_info + * rtattr + * rtattr + * ... (max count of rtattr is XFRM_MAX+1 + * + * each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_ALGO_KEY_BUF_SIZE 512 +#define CTX_BUF_SIZE 256 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n" + " [ mark MARK [ mask MASK ] ] [ reqid REQID ] [ seq SEQ ]\n" + " [ replay-window SIZE ] [ replay-seq SEQ ] [ replay-oseq SEQ ]\n" + " [ replay-seq-hi SEQ ] [ replay-oseq-hi SEQ ]\n" + " [ flag FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ] [ encap ENCAP ]\n" + " [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n" + " [ offload [dev DEV] dir DIR ]\n" + " [ output-mark OUTPUT-MARK ]\n" + " [ if_id IF_ID ]\n" + "Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n" + " [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n" + "Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n" + "Usage: ip xfrm state deleteall [ ID ] [ mode MODE ] [ reqid REQID ]\n" + " [ flag FLAG-LIST ]\n" + "Usage: ip xfrm state list [ nokeys ] [ ID ] [ mode MODE ] [ reqid REQID ]\n" + " [ flag FLAG-LIST ]\n" + "Usage: ip xfrm state flush [ proto XFRM-PROTO ]\n" + "Usage: ip xfrm state count\n" + "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM-PROTO ] [ spi SPI ]\n" + "XFRM-PROTO := "); + fprintf(stderr, + "%s | %s | %s | %s | %s\n", + strxf_xfrmproto(IPPROTO_ESP), + strxf_xfrmproto(IPPROTO_AH), + strxf_xfrmproto(IPPROTO_COMP), + strxf_xfrmproto(IPPROTO_ROUTING), + strxf_xfrmproto(IPPROTO_DSTOPTS)); + fprintf(stderr, + "ALGO-LIST := [ ALGO-LIST ] ALGO\n" + "ALGO := { "); + fprintf(stderr, + "%s | %s", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_algotype(XFRMA_ALG_AUTH)); + fprintf(stderr, + " } ALGO-NAME ALGO-KEYMAT |\n" + " %s", strxf_algotype(XFRMA_ALG_AUTH_TRUNC)); + fprintf(stderr, + " ALGO-NAME ALGO-KEYMAT ALGO-TRUNC-LEN |\n" + " %s", strxf_algotype(XFRMA_ALG_AEAD)); + fprintf(stderr, + " ALGO-NAME ALGO-KEYMAT ALGO-ICV-LEN |\n" + " %s", strxf_algotype(XFRMA_ALG_COMP)); + fprintf(stderr, + " ALGO-NAME\n" + "MODE := transport | tunnel | beet | ro | in_trigger\n" + "FLAG-LIST := [ FLAG-LIST ] FLAG\n" + "FLAG := noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4 | esn\n" + "EXTRA-FLAG-LIST := [ EXTRA-FLAG-LIST ] EXTRA-FLAG\n" + "EXTRA-FLAG := dont-encap-dscp\n" + "SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n" + "UPSPEC := proto { { "); + fprintf(stderr, + "%s | %s | %s | %s", + strxf_proto(IPPROTO_TCP), + strxf_proto(IPPROTO_UDP), + strxf_proto(IPPROTO_SCTP), + strxf_proto(IPPROTO_DCCP)); + fprintf(stderr, + " } [ sport PORT ] [ dport PORT ] |\n" + " { "); + fprintf(stderr, + "%s | %s | %s", + strxf_proto(IPPROTO_ICMP), + strxf_proto(IPPROTO_ICMPV6), + strxf_proto(IPPROTO_MH)); + fprintf(stderr, + " } [ type NUMBER ] [ code NUMBER ] |\n"); + fprintf(stderr, + " %s", strxf_proto(IPPROTO_GRE)); + fprintf(stderr, + " [ key { DOTTED-QUAD | NUMBER } ] | PROTO }\n" + "LIMIT-LIST := [ LIMIT-LIST ] limit LIMIT\n" + "LIMIT := { time-soft | time-hard | time-use-soft | time-use-hard } SECONDS |\n" + " { byte-soft | byte-hard } SIZE | { packet-soft | packet-hard } COUNT\n" + "ENCAP := { espinudp | espinudp-nonike } SPORT DPORT OADDR\n" + "DIR := in | out\n"); + + exit(-1); +} + +static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, + char *name, char *key, char *buf, int max) +{ + int len; + int slen = strlen(key); + +#if 0 + /* XXX: verifying both name and key is required! */ + fprintf(stderr, "warning: ALGO-NAME/ALGO-KEYMAT values will be sent to the kernel promiscuously! (verifying them isn't implemented yet)\n"); +#endif + + strlcpy(alg->alg_name, name, sizeof(alg->alg_name)); + + if (slen > 2 && strncmp(key, "0x", 2) == 0) { + /* split two chars "0x" from the top */ + char *p = key + 2; + int plen = slen - 2; + int i; + int j; + + /* Converting hexadecimal numbered string into real key; + * Convert each two chars into one char(value). If number + * of the length is odd, add zero on the top for rounding. + */ + + /* calculate length of the converted values(real key) */ + len = (plen + 1) / 2; + if (len > max) + invarg("ALGO-KEYMAT value makes buffer overflow\n", key); + + for (i = -(plen % 2), j = 0; j < len; i += 2, j++) { + char vbuf[3]; + __u8 val; + + vbuf[0] = i >= 0 ? p[i] : '0'; + vbuf[1] = p[i + 1]; + vbuf[2] = '\0'; + + if (get_u8(&val, vbuf, 16)) + invarg("ALGO-KEYMAT value is invalid", key); + + buf[j] = val; + } + } else { + len = slen; + if (len > 0) { + if (len > max) + invarg("ALGO-KEYMAT value makes buffer overflow\n", key); + + memcpy(buf, key, len); + } + } + + alg->alg_key_len = len * 8; + + return 0; +} + +static int xfrm_seq_parse(__u32 *seq, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (get_be32(seq, *argv, 0)) + invarg("SEQ value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int len = strlen(*argv); + + if (len > 2 && strncmp(*argv, "0x", 2) == 0) { + __u8 val = 0; + + if (get_u8(&val, *argv, 16)) + invarg("FLAG value is invalid", *argv); + *flags = val; + } else { + while (1) { + if (strcmp(*argv, "noecn") == 0) + *flags |= XFRM_STATE_NOECN; + else if (strcmp(*argv, "decap-dscp") == 0) + *flags |= XFRM_STATE_DECAP_DSCP; + else if (strcmp(*argv, "nopmtudisc") == 0) + *flags |= XFRM_STATE_NOPMTUDISC; + else if (strcmp(*argv, "wildrecv") == 0) + *flags |= XFRM_STATE_WILDRECV; + else if (strcmp(*argv, "icmp") == 0) + *flags |= XFRM_STATE_ICMP; + else if (strcmp(*argv, "af-unspec") == 0) + *flags |= XFRM_STATE_AF_UNSPEC; + else if (strcmp(*argv, "align4") == 0) + *flags |= XFRM_STATE_ALIGN4; + else if (strcmp(*argv, "esn") == 0) + *flags |= XFRM_STATE_ESN; + else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_extra_flag_parse(__u32 *extra_flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int len = strlen(*argv); + + if (len > 2 && strncmp(*argv, "0x", 2) == 0) { + __u32 val = 0; + + if (get_u32(&val, *argv, 16)) + invarg("\"EXTRA-FLAG\" is invalid", *argv); + *extra_flags = val; + } else { + while (1) { + if (strcmp(*argv, "dont-encap-dscp") == 0) + *extra_flags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP; + else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_offload_dir_parse(__u8 *dir, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "in") == 0) + *dir = XFRM_OFFLOAD_INBOUND; + else if (strcmp(*argv, "out") == 0) + *dir = 0; + else + invarg("DIR value is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_info xsinfo; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .xsinfo.family = preferred_family, + .xsinfo.lft.soft_byte_limit = XFRM_INF, + .xsinfo.lft.hard_byte_limit = XFRM_INF, + .xsinfo.lft.soft_packet_limit = XFRM_INF, + .xsinfo.lft.hard_packet_limit = XFRM_INF, + }; + struct xfrm_replay_state replay = {}; + struct xfrm_replay_state_esn replay_esn = {}; + struct xfrm_user_offload xuo = {}; + unsigned int ifindex = 0; + __u8 dir = 0; + bool is_offload = false; + __u32 replay_window = 0; + __u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0; + char *idp = NULL; + char *aeadop = NULL; + char *ealgop = NULL; + char *aalgop = NULL; + char *calgop = NULL; + char *coap = NULL; + char *sctxp = NULL; + __u32 extra_flags = 0; + struct xfrm_mark mark = {0, 0}; + struct { + struct xfrm_user_sec_ctx sctx; + char str[CTX_BUF_SIZE]; + } ctx = {}; + __u32 output_mark = 0; + bool is_if_id_set = false; + __u32 if_id = 0; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); + } else if (strcmp(*argv, "mark") == 0) { + xfrm_parse_mark(&mark, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); + } else if (strcmp(*argv, "seq") == 0) { + NEXT_ARG(); + xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); + } else if (strcmp(*argv, "replay-window") == 0) { + NEXT_ARG(); + if (get_u32(&replay_window, *argv, 0)) + invarg("value after \"replay-window\" is invalid", *argv); + } else if (strcmp(*argv, "replay-seq") == 0) { + NEXT_ARG(); + if (get_u32(&seq, *argv, 0)) + invarg("value after \"replay-seq\" is invalid", *argv); + } else if (strcmp(*argv, "replay-seq-hi") == 0) { + NEXT_ARG(); + if (get_u32(&seq_hi, *argv, 0)) + invarg("value after \"replay-seq-hi\" is invalid", *argv); + } else if (strcmp(*argv, "replay-oseq") == 0) { + NEXT_ARG(); + if (get_u32(&oseq, *argv, 0)) + invarg("value after \"replay-oseq\" is invalid", *argv); + } else if (strcmp(*argv, "replay-oseq-hi") == 0) { + NEXT_ARG(); + if (get_u32(&oseq_hi, *argv, 0)) + invarg("value after \"replay-oseq-hi\" is invalid", *argv); + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); + } else if (strcmp(*argv, "extra-flag") == 0) { + NEXT_ARG(); + xfrm_state_extra_flag_parse(&extra_flags, &argc, &argv); + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + preferred_family = AF_UNSPEC; + xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); + preferred_family = req.xsinfo.sel.family; + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "encap") == 0) { + struct xfrm_encap_tmpl encap; + inet_prefix oa; + NEXT_ARG(); + xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); + NEXT_ARG(); + if (get_be16(&encap.encap_sport, *argv, 0)) + invarg("SPORT value after \"encap\" is invalid", *argv); + NEXT_ARG(); + if (get_be16(&encap.encap_dport, *argv, 0)) + invarg("DPORT value after \"encap\" is invalid", *argv); + NEXT_ARG(); + get_addr(&oa, *argv, AF_UNSPEC); + memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); + addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, + (void *)&encap, sizeof(encap)); + } else if (strcmp(*argv, "coa") == 0) { + inet_prefix coa; + xfrm_address_t xcoa = {}; + + if (coap) + duparg("coa", *argv); + coap = *argv; + + NEXT_ARG(); + + get_prefix(&coa, *argv, preferred_family); + if (coa.family == AF_UNSPEC) + invarg("value after \"coa\" has an unrecognized address family", *argv); + if (coa.bytelen > sizeof(xcoa)) + invarg("value after \"coa\" is too large", *argv); + + memcpy(&xcoa, &coa.data, coa.bytelen); + + addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR, + (void *)&xcoa, sizeof(xcoa)); + } else if (strcmp(*argv, "ctx") == 0) { + char *context; + + if (sctxp) + duparg("ctx", *argv); + sctxp = *argv; + + NEXT_ARG(); + context = *argv; + + xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); + addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX, + (void *)&ctx, ctx.sctx.len); + } else if (strcmp(*argv, "offload") == 0) { + is_offload = true; + NEXT_ARG(); + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) { + invarg("value after \"offload dev\" is invalid", *argv); + is_offload = false; + } + NEXT_ARG(); + } + if (strcmp(*argv, "dir") == 0) { + NEXT_ARG(); + xfrm_offload_dir_parse(&dir, &argc, &argv); + } else { + invarg("value after \"offload dir\" is invalid", *argv); + is_offload = false; + } + } else if (strcmp(*argv, "output-mark") == 0) { + NEXT_ARG(); + if (get_u32(&output_mark, *argv, 0)) + invarg("value after \"output-mark\" is invalid", *argv); + } else if (strcmp(*argv, "if_id") == 0) { + NEXT_ARG(); + if (get_u32(&if_id, *argv, 0)) + invarg("value after \"if_id\" is invalid", *argv); + is_if_id_set = true; + } else { + /* try to assume ALGO */ + int type = xfrm_algotype_getbyname(*argv); + + switch (type) { + case XFRMA_ALG_AEAD: + case XFRMA_ALG_CRYPT: + case XFRMA_ALG_AUTH: + case XFRMA_ALG_AUTH_TRUNC: + case XFRMA_ALG_COMP: + { + /* ALGO */ + struct { + union { + struct xfrm_algo alg; + struct xfrm_algo_aead aead; + struct xfrm_algo_auth auth; + } u; + char buf[XFRM_ALGO_KEY_BUF_SIZE]; + } alg = {}; + int len; + __u32 icvlen, trunclen; + char *name; + char *key = ""; + char *buf; + + switch (type) { + case XFRMA_ALG_AEAD: + if (ealgop || aalgop || aeadop) + duparg("ALGO-TYPE", *argv); + aeadop = *argv; + break; + case XFRMA_ALG_CRYPT: + if (ealgop || aeadop) + duparg("ALGO-TYPE", *argv); + ealgop = *argv; + break; + case XFRMA_ALG_AUTH: + case XFRMA_ALG_AUTH_TRUNC: + if (aalgop || aeadop) + duparg("ALGO-TYPE", *argv); + aalgop = *argv; + break; + case XFRMA_ALG_COMP: + if (calgop) + duparg("ALGO-TYPE", *argv); + calgop = *argv; + break; + default: + /* not reached */ + invarg("ALGO-TYPE value is invalid\n", *argv); + } + + if (!NEXT_ARG_OK()) + missarg("ALGO-NAME"); + NEXT_ARG(); + name = *argv; + + switch (type) { + case XFRMA_ALG_AEAD: + case XFRMA_ALG_CRYPT: + case XFRMA_ALG_AUTH: + case XFRMA_ALG_AUTH_TRUNC: + if (!NEXT_ARG_OK()) + missarg("ALGO-KEYMAT"); + NEXT_ARG(); + key = *argv; + break; + } + + buf = alg.u.alg.alg_key; + len = sizeof(alg.u.alg); + + switch (type) { + case XFRMA_ALG_AEAD: + if (!NEXT_ARG_OK()) + missarg("ALGO-ICV-LEN"); + NEXT_ARG(); + if (get_u32(&icvlen, *argv, 0)) + invarg("ALGO-ICV-LEN value is invalid", + *argv); + alg.u.aead.alg_icv_len = icvlen; + + buf = alg.u.aead.alg_key; + len = sizeof(alg.u.aead); + break; + case XFRMA_ALG_AUTH_TRUNC: + if (!NEXT_ARG_OK()) + missarg("ALGO-TRUNC-LEN"); + NEXT_ARG(); + if (get_u32(&trunclen, *argv, 0)) + invarg("ALGO-TRUNC-LEN value is invalid", + *argv); + alg.u.auth.alg_trunc_len = trunclen; + + buf = alg.u.auth.alg_key; + len = sizeof(alg.u.auth); + break; + } + + xfrm_algo_parse((void *)&alg, type, name, key, + buf, sizeof(alg.buf)); + len += alg.u.alg.alg_key_len / 8; + + addattr_l(&req.n, sizeof(req.buf), type, + (void *)&alg, len); + break; + } + default: + /* try to assume ID */ + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, + &req.xsinfo.family, 0, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xsinfo.family; + } + } + argc--; argv++; + } + + if (req.xsinfo.flags & XFRM_STATE_ESN && + replay_window == 0) { + fprintf(stderr, "Error: esn flag set without replay-window.\n"); + exit(-1); + } + + if (replay_window > XFRMA_REPLAY_ESN_MAX) { + fprintf(stderr, + "Error: replay-window (%u) > XFRMA_REPLAY_ESN_MAX (%u).\n", + replay_window, XFRMA_REPLAY_ESN_MAX); + exit(-1); + } + + if (is_offload) { + xuo.ifindex = ifindex; + xuo.flags = dir; + addattr_l(&req.n, sizeof(req.buf), XFRMA_OFFLOAD_DEV, &xuo, + sizeof(xuo)); + } + if (req.xsinfo.flags & XFRM_STATE_ESN || + replay_window > (sizeof(replay.bitmap) * 8)) { + replay_esn.seq = seq; + replay_esn.oseq = oseq; + replay_esn.seq_hi = seq_hi; + replay_esn.oseq_hi = oseq_hi; + replay_esn.replay_window = replay_window; + replay_esn.bmp_len = (replay_window + sizeof(__u32) * 8 - 1) / + (sizeof(__u32) * 8); + addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_ESN_VAL, + &replay_esn, sizeof(replay_esn)); + } else { + if (seq || oseq) { + replay.seq = seq; + replay.oseq = oseq; + addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL, + &replay, sizeof(replay)); + } + req.xsinfo.replay_window = replay_window; + } + + if (extra_flags) + addattr32(&req.n, sizeof(req.buf), XFRMA_SA_EXTRA_FLAGS, + extra_flags); + + if (!idp) { + fprintf(stderr, "Not enough information: ID is required\n"); + exit(1); + } + + if (mark.m) { + int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, + (void *)&mark, sizeof(mark)); + if (r < 0) { + fprintf(stderr, "XFRMA_MARK failed\n"); + exit(1); + } + } + + if (is_if_id_set) + addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id); + + if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { + switch (req.xsinfo.mode) { + case XFRM_MODE_TRANSPORT: + case XFRM_MODE_TUNNEL: + break; + case XFRM_MODE_BEET: + if (req.xsinfo.id.proto == IPPROTO_ESP) + break; + default: + fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + + switch (req.xsinfo.id.proto) { + case IPPROTO_ESP: + if (calgop) { + fprintf(stderr, "ALGO-TYPE value \"%s\" is invalid with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_COMP), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + if (!ealgop && !aeadop) { + fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_algotype(XFRMA_ALG_AEAD), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + break; + case IPPROTO_AH: + if (ealgop || aeadop || calgop) { + fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_algotype(XFRMA_ALG_AEAD), + strxf_algotype(XFRMA_ALG_COMP), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + if (!aalgop) { + fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_AUTH), + strxf_algotype(XFRMA_ALG_AUTH_TRUNC), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + break; + case IPPROTO_COMP: + if (ealgop || aalgop || aeadop) { + fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_algotype(XFRMA_ALG_AUTH), + strxf_algotype(XFRMA_ALG_AUTH_TRUNC), + strxf_algotype(XFRMA_ALG_AEAD), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + if (!calgop) { + fprintf(stderr, "ALGO-TYPE value \"%s\" is required with XFRM-PROTO value \"%s\"\n", + strxf_algotype(XFRMA_ALG_COMP), + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + break; + } + } else { + if (ealgop || aalgop || aeadop || calgop) { + fprintf(stderr, "ALGO is invalid with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + } + + if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { + switch (req.xsinfo.mode) { + case XFRM_MODE_ROUTEOPTIMIZATION: + case XFRM_MODE_IN_TRIGGER: + break; + case 0: + fprintf(stderr, "\"mode\" is required with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + default: + fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + + if (!coap) { + fprintf(stderr, "\"coa\" is required with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + } else { + if (coap) { + fprintf(stderr, "\"coa\" is invalid with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + } + + if (output_mark) + addattr32(&req.n, sizeof(req.buf), XFRMA_OUTPUT_MARK, output_mark); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsinfo.family == AF_UNSPEC) + req.xsinfo.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_allocspi(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userspi_info xspi; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xspi)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_ALLOCSPI, + .xspi.info.family = preferred_family, +#if 0 + .xspi.lft.soft_byte_limit = XFRM_INF, + .xspi.lft.hard_byte_limit = XFRM_INF, + .xspi.lft.soft_packet_limit = XFRM_INF, + .xspi.lft.hard_packet_limit = XFRM_INF, +#endif + }; + char *idp = NULL; + char *minp = NULL; + char *maxp = NULL; + struct xfrm_mark mark = {0, 0}; + struct nlmsghdr *answer; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&req.xspi.info.mode, &argc, &argv); + } else if (strcmp(*argv, "mark") == 0) { + xfrm_parse_mark(&mark, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&req.xspi.info.reqid, &argc, &argv); + } else if (strcmp(*argv, "seq") == 0) { + NEXT_ARG(); + xfrm_seq_parse(&req.xspi.info.seq, &argc, &argv); + } else if (strcmp(*argv, "min") == 0) { + if (minp) + duparg("min", *argv); + minp = *argv; + + NEXT_ARG(); + + if (get_u32(&req.xspi.min, *argv, 0)) + invarg("value after \"min\" is invalid", *argv); + } else if (strcmp(*argv, "max") == 0) { + if (maxp) + duparg("max", *argv); + maxp = *argv; + + NEXT_ARG(); + + if (get_u32(&req.xspi.max, *argv, 0)) + invarg("value after \"max\" is invalid", *argv); + } else { + /* try to assume ID */ + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&req.xspi.info.saddr, &req.xspi.info.id, + &req.xspi.info.family, 0, &argc, &argv); + if (req.xspi.info.id.spi) { + fprintf(stderr, "\"spi\" is invalid\n"); + exit(1); + } + if (preferred_family == AF_UNSPEC) + preferred_family = req.xspi.info.family; + } + argc--; argv++; + } + + if (!idp) { + fprintf(stderr, "Not enough information: ID is required\n"); + exit(1); + } + + if (minp) { + if (!maxp) { + fprintf(stderr, "\"max\" is missing\n"); + exit(1); + } + if (req.xspi.min > req.xspi.max) { + fprintf(stderr, "value after \"min\" is larger than value after \"max\"\n"); + exit(1); + } + } else { + if (maxp) { + fprintf(stderr, "\"min\" is missing\n"); + exit(1); + } + + /* XXX: Default value defined in PF_KEY; + * See kernel's net/key/af_key.c(pfkey_getspi). + */ + req.xspi.min = 0x100; + req.xspi.max = 0x0fffffff; + + /* XXX: IPCOMP spi is 16-bits; + * See kernel's net/xfrm/xfrm_user(verify_userspi_info). + */ + if (req.xspi.info.id.proto == IPPROTO_COMP) + req.xspi.max = 0xffff; + } + + if (mark.m & mark.v) { + int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, + (void *)&mark, sizeof(mark)); + if (r < 0) { + fprintf(stderr, "XFRMA_MARK failed\n"); + exit(1); + } + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xspi.info.family == AF_UNSPEC) + req.xspi.info.family = AF_INET; + + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + exit(2); + + if (xfrm_state_print(answer, (void *)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + free(answer); + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) +{ + if (!filter.use) + return 1; + + if (filter.xsinfo.family != AF_UNSPEC && + filter.xsinfo.family != xsinfo->family) + return 0; + + if (filter.id_src_mask) + if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr, + filter.id_src_mask)) + return 0; + if (filter.id_dst_mask) + if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, + filter.id_dst_mask)) + return 0; + if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) + return 0; + if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) + return 0; + if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) + return 0; + if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) + return 0; + if (filter.state_flags_mask) + if ((xsinfo->flags & filter.xsinfo.flags) == 0) + return 0; + + return 1; +} + +static int __do_xfrm_state_print(struct nlmsghdr *n, void *arg, bool nokeys) +{ + FILE *fp = (FILE *)arg; + struct rtattr *tb[XFRMA_MAX+1]; + struct rtattr *rta; + struct xfrm_usersa_info *xsinfo = NULL; + struct xfrm_user_expire *xexp = NULL; + struct xfrm_usersa_id *xsid = NULL; + int len = n->nlmsg_len; + + if (n->nlmsg_type != XFRM_MSG_NEWSA && + n->nlmsg_type != XFRM_MSG_DELSA && + n->nlmsg_type != XFRM_MSG_UPDSA && + n->nlmsg_type != XFRM_MSG_EXPIRE) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + if (n->nlmsg_type == XFRM_MSG_DELSA) { + /* Don't blame me for this .. Herbert made me do it */ + xsid = NLMSG_DATA(n); + len -= NLMSG_SPACE(sizeof(*xsid)); + } else if (n->nlmsg_type == XFRM_MSG_EXPIRE) { + xexp = NLMSG_DATA(n); + xsinfo = &xexp->state; + len -= NLMSG_SPACE(sizeof(*xexp)); + } else { + xexp = NULL; + xsinfo = NLMSG_DATA(n); + len -= NLMSG_SPACE(sizeof(*xsinfo)); + } + + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (xsinfo && !xfrm_state_filter_match(xsinfo)) + return 0; + + if (n->nlmsg_type == XFRM_MSG_DELSA) + fprintf(fp, "Deleted "); + else if (n->nlmsg_type == XFRM_MSG_UPDSA) + fprintf(fp, "Updated "); + else if (n->nlmsg_type == XFRM_MSG_EXPIRE) + fprintf(fp, "Expired "); + + if (n->nlmsg_type == XFRM_MSG_DELSA) + rta = XFRMSID_RTA(xsid); + else if (n->nlmsg_type == XFRM_MSG_EXPIRE) + rta = XFRMEXP_RTA(xexp); + else + rta = XFRMS_RTA(xsinfo); + + parse_rtattr(tb, XFRMA_MAX, rta, len); + + if (n->nlmsg_type == XFRM_MSG_DELSA) { + /* xfrm_policy_id_print(); */ + + if (!tb[XFRMA_SA]) { + fprintf(stderr, "Buggy XFRM_MSG_DELSA: no XFRMA_SA\n"); + return -1; + } + if (RTA_PAYLOAD(tb[XFRMA_SA]) < sizeof(*xsinfo)) { + fprintf(stderr, "Buggy XFRM_MSG_DELPOLICY: too short XFRMA_POLICY len\n"); + return -1; + } + xsinfo = RTA_DATA(tb[XFRMA_SA]); + } + + xfrm_state_info_print(xsinfo, tb, fp, NULL, NULL, nokeys); + + if (n->nlmsg_type == XFRM_MSG_EXPIRE) { + fprintf(fp, "\t"); + fprintf(fp, "hard %u", xexp->hard); + fprintf(fp, "%s", _SL_); + } + + if (oneline) + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + +int xfrm_state_print(struct nlmsghdr *n, void *arg) +{ + return __do_xfrm_state_print(n, arg, false); +} + +int xfrm_state_print_nokeys(struct nlmsghdr *n, void *arg) +{ + return __do_xfrm_state_print(n, arg, true); +} + +static int xfrm_state_get_or_delete(int argc, char **argv, int delete) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_id xsid; + char buf[RTA_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA, + .xsid.family = preferred_family, + }; + struct xfrm_id id; + char *idp = NULL; + struct xfrm_mark mark = {0, 0}; + + while (argc > 0) { + xfrm_address_t saddr; + + if (strcmp(*argv, "mark") == 0) { + xfrm_parse_mark(&mark, &argc, &argv); + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + memset(&id, 0, sizeof(id)); + memset(&saddr, 0, sizeof(saddr)); + xfrm_id_parse(&saddr, &id, &req.xsid.family, 0, + &argc, &argv); + + memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); + req.xsid.spi = id.spi; + req.xsid.proto = id.proto; + + addattr_l(&req.n, sizeof(req.buf), XFRMA_SRCADDR, + (void *)&saddr, sizeof(saddr)); + } + + argc--; argv++; + } + + if (mark.m & mark.v) { + int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, + (void *)&mark, sizeof(mark)); + if (r < 0) { + fprintf(stderr, "XFRMA_MARK failed\n"); + exit(1); + } + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsid.family == AF_UNSPEC) + req.xsid.family = AF_INET; + + if (delete) { + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + } else { + struct nlmsghdr *answer; + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + exit(2); + + if (xfrm_state_print(answer, (void *)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + free(answer); + } + + rtnl_close(&rth); + + return 0; +} + +/* + * With an existing state of nlmsg, make new nlmsg for deleting the state + * and store it to buffer. + */ +static int xfrm_state_keep(struct nlmsghdr *n, void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_usersa_id *xsid; + struct rtattr *tb[XFRMA_MAX+1]; + + if (n->nlmsg_type != XFRM_MSG_NEWSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "State buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELSA; + new_n->nlmsg_seq = ++rth->seq; + + xsid = NLMSG_DATA(new_n); + xsid->family = xsinfo->family; + memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); + xsid->spi = xsinfo->id.spi; + xsid->proto = xsinfo->id.proto; + + addattr_l(new_n, xb->size, XFRMA_SRCADDR, &xsinfo->saddr, + sizeof(xsid->daddr)); + + parse_rtattr(tb, XFRMA_MAX, XFRMS_RTA(xsinfo), len); + + if (tb[XFRMA_MARK]) { + int r = addattr_l(new_n, xb->size, XFRMA_MARK, + (void *)RTA_DATA(tb[XFRMA_MARK]), tb[XFRMA_MARK]->rta_len); + if (r < 0) { + fprintf(stderr, "%s: XFRMA_MARK failed\n", __func__); + exit(1); + } + } + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count++; + + return 0; +} + +static int xfrm_state_list_or_deleteall(int argc, char **argv, int deleteall) +{ + char *idp = NULL; + struct rtnl_handle rth; + bool nokeys = false; + + if (argc > 0 || preferred_family != AF_UNSPEC) + filter.use = 1; + filter.xsinfo.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "nokeys") == 0) { + nokeys = true; + } else if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); + + filter.mode_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); + + filter.reqid_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id, + &filter.xsinfo.family, 1, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xsinfo.family; + } + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (deleteall) { + struct xfrm_buffer xb; + char buf[NLMSG_DELETEALL_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + struct { + struct nlmsghdr n; + char buf[NLMSG_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_HDRLEN, + .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSA, + .n.nlmsg_seq = rth.dump = ++rth.seq, + }; + + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Delete-all round = %d\n", i); + + if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb) < 0) { + fprintf(stderr, "Delete-all terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Delete-all completed\n"); + break; + } + + if (rtnl_send_check(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send delete-all request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Delete-all nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + + } else { + struct xfrm_address_filter addrfilter = { + .saddr = filter.xsinfo.saddr, + .daddr = filter.xsinfo.id.daddr, + .family = filter.xsinfo.family, + .splen = filter.id_src_mask, + .dplen = filter.id_dst_mask, + }; + struct { + struct nlmsghdr n; + char buf[NLMSG_BUF_SIZE]; + } req = { + .n.nlmsg_len = NLMSG_HDRLEN, + .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSA, + .n.nlmsg_seq = rth.dump = ++rth.seq, + }; + + if (filter.xsinfo.id.proto) + addattr8(&req.n, sizeof(req), XFRMA_PROTO, + filter.xsinfo.id.proto); + addattr_l(&req.n, sizeof(req), XFRMA_ADDRESS_FILTER, + &addrfilter, sizeof(addrfilter)); + + if (rtnl_send(&rth, (void *)&req, req.n.nlmsg_len) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + rtnl_filter_t filter = nokeys ? + xfrm_state_print_nokeys : xfrm_state_print; + if (rtnl_dump_filter(&rth, filter, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int print_sadinfo(struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + __u32 *f = NLMSG_DATA(n); + struct rtattr *tb[XFRMA_SAD_MAX+1]; + struct rtattr *rta; + int len = n->nlmsg_len; + + len -= NLMSG_LENGTH(sizeof(__u32)); + if (len < 0) { + fprintf(stderr, "SADinfo: Wrong len %d\n", len); + return -1; + } + + rta = XFRMSAPD_RTA(f); + parse_rtattr(tb, XFRMA_SAD_MAX, rta, len); + + if (tb[XFRMA_SAD_CNT]) { + __u32 cnt; + + fprintf(fp, "\t SAD"); + cnt = rta_getattr_u32(tb[XFRMA_SAD_CNT]); + fprintf(fp, " count %u", cnt); + } else { + fprintf(fp, "BAD SAD info returned\n"); + return -1; + } + + if (show_stats) { + if (tb[XFRMA_SAD_HINFO]) { + struct xfrmu_sadhinfo *si; + + if (RTA_PAYLOAD(tb[XFRMA_SAD_HINFO]) < sizeof(*si)) { + fprintf(fp, "BAD SAD length returned\n"); + return -1; + } + + si = RTA_DATA(tb[XFRMA_SAD_HINFO]); + fprintf(fp, " (buckets "); + fprintf(fp, "count %d", si->sadhcnt); + fprintf(fp, " Max %d", si->sadhmcnt); + fprintf(fp, ")"); + } + } + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_sad_getinfo(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + __u32 flags; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.flags)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_GETSADINFO, + .flags = 0XFFFFFFFF, + }; + struct nlmsghdr *answer; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + exit(2); + + print_sadinfo(answer, (void *)stdout); + + free(answer); + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_flush(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_flush xsf; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = XFRM_MSG_FLUSHSA, + }; + char *protop = NULL; + + while (argc > 0) { + if (strcmp(*argv, "proto") == 0) { + int ret; + + if (protop) + duparg("proto", *argv); + protop = *argv; + + NEXT_ARG(); + + ret = xfrm_xfrmproto_getbyname(*argv); + if (ret < 0) + invarg("XFRM-PROTO value is invalid", *argv); + + req.xsf.proto = (__u8)ret; + } else + invarg("unknown", *argv); + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush state with XFRM-PROTO value \"%s\"\n", + strxf_xfrmproto(req.xsf.proto)); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_state(int argc, char **argv) +{ + if (argc < 1) + return xfrm_state_list_or_deleteall(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_state_modify(XFRM_MSG_NEWSA, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_state_modify(XFRM_MSG_UPDSA, 0, + argc-1, argv+1); + if (matches(*argv, "allocspi") == 0) + return xfrm_state_allocspi(argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 1); + if (matches(*argv, "deleteall") == 0 || matches(*argv, "delall") == 0) + return xfrm_state_list_or_deleteall(argc-1, argv+1, 1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_state_list_or_deleteall(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return xfrm_state_flush(argc-1, argv+1); + if (matches(*argv, "count") == 0) { + return xfrm_sad_getinfo(argc, argv); + } + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); + exit(-1); +}
diff --git a/lib/Android.mk b/lib/Android.mk new file mode 100644 index 0000000..b6bcb6f --- /dev/null +++ b/lib/Android.mk
@@ -0,0 +1,77 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := utils.c rt_names.c ll_map.c ll_types.c ll_proto.c ll_addr.c i\ + inet_proto.c namespace.c json_writer.c json_print.c \ + names.c color.c bpf.c exec.c fs.c mpls_ntop.c mpls_pton.c +LOCAL_MODULE := libiprouteutil +LOCAL_MODULE_TAGS := eng +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/uapi +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -DHAVE_UNISTD_H \ + -DHAVE_ERRNO_H \ + -DHAVE_NETINET_IN_H \ + -DHAVE_SYS_IOCTL_H \ + -DHAVE_SYS_MMAN_H \ + -DHAVE_SYS_MOUNT_H \ + -DHAVE_SYS_PRCTL_H \ + -DHAVE_SYS_RESOURCE_H \ + -DHAVE_SYS_SELECT_H \ + -DHAVE_SYS_STAT_H \ + -DHAVE_SYS_TYPES_H \ + -DHAVE_STDLIB_H \ + -DHAVE_STRDUP \ + -DHAVE_MMAP \ + -DHAVE_UTIME_H \ + -DHAVE_GETPAGESIZE \ + -DHAVE_LSEEK64 \ + -DHAVE_LSEEK64_PROTOTYPE \ + -DHAVE_EXT2_IOCTLS \ + -DHAVE_LINUX_FD_H \ + -DHAVE_TYPE_SSIZE_T \ + -DHAVE_SETNS \ + -DNEED_STRLCPY \ + -Wno-pointer-arith \ + -Wno-sign-compare \ + -Wno-unused-parameter \ + -Werror + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := libgenl.c libnetlink.c +LOCAL_MODULE := libnetlink +LOCAL_MODULE_TAGS := eng +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../include/uapi + +LOCAL_CFLAGS := -O2 -g -W -Wall \ + -DHAVE_UNISTD_H \ + -DHAVE_ERRNO_H \ + -DHAVE_NETINET_IN_H \ + -DHAVE_SYS_IOCTL_H \ + -DHAVE_SYS_MMAN_H \ + -DHAVE_SYS_MOUNT_H \ + -DHAVE_SYS_PRCTL_H \ + -DHAVE_SYS_RESOURCE_H \ + -DHAVE_SYS_SELECT_H \ + -DHAVE_SYS_STAT_H \ + -DHAVE_SYS_TYPES_H \ + -DHAVE_STDLIB_H \ + -DHAVE_STRDUP \ + -DHAVE_MMAP \ + -DHAVE_UTIME_H \ + -DHAVE_GETPAGESIZE \ + -DHAVE_LSEEK64 \ + -DHAVE_LSEEK64_PROTOTYPE \ + -DHAVE_EXT2_IOCTLS \ + -DHAVE_LINUX_FD_H \ + -DHAVE_TYPE_SSIZE_T \ + -Wno-pointer-arith \ + -Wno-sign-compare \ + -Wno-unused-parameter \ + -Werror \ + -Wno-\#warnings + +include $(BUILD_STATIC_LIBRARY)
diff --git a/lib/MODULE_LICENSE_GPL b/lib/MODULE_LICENSE_GPL new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/MODULE_LICENSE_GPL
diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..bab8cbf --- /dev/null +++ b/lib/Makefile
@@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../config.mk + +CFLAGS += -fPIC + +UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \ + inet_proto.o namespace.o json_writer.o json_print.o \ + names.o color.o bpf.o exec.o fs.o + +NLOBJ=libgenl.o libnetlink.o + +all: libnetlink.a libutil.a + +libnetlink.a: $(NLOBJ) + $(QUIET_AR)$(AR) rcs $@ $^ + +libutil.a: $(UTILOBJ) $(ADDLIB) + $(QUIET_AR)$(AR) rcs $@ $^ + +install: + +clean: + rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a
diff --git a/lib/NOTICE b/lib/NOTICE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/lib/NOTICE
@@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 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. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.
diff --git a/lib/bpf.c b/lib/bpf.c new file mode 100644 index 0000000..10cf9bf --- /dev/null +++ b/lib/bpf.c
@@ -0,0 +1,3240 @@ +/* + * bpf.c 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 <daniel@iogearbox.net> + * Jiri Pirko <jiri@resnulli.us> + * Alexei Starovoitov <ast@kernel.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdbool.h> +#include <stdint.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <limits.h> +#include <assert.h> + +#ifdef HAVE_ELF +#include <libelf.h> +#include <gelf.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/vfs.h> +#include <sys/mount.h> +#include <sys/syscall.h> +#include <sys/sendfile.h> +#include <sys/resource.h> + +#include <arpa/inet.h> + +#include "utils.h" +#include "json_print.h" + +#include "bpf_util.h" +#include "bpf_elf.h" +#include "bpf_scm.h" + +struct bpf_prog_meta { + const char *type; + const char *subdir; + const char *section; + bool may_uds_export; +}; + +static const enum bpf_prog_type __bpf_types[] = { + BPF_PROG_TYPE_SCHED_CLS, + BPF_PROG_TYPE_SCHED_ACT, + BPF_PROG_TYPE_XDP, + BPF_PROG_TYPE_LWT_IN, + BPF_PROG_TYPE_LWT_OUT, + BPF_PROG_TYPE_LWT_XMIT, +}; + +static const struct bpf_prog_meta __bpf_prog_meta[] = { + [BPF_PROG_TYPE_SCHED_CLS] = { + .type = "cls", + .subdir = "tc", + .section = ELF_SECTION_CLASSIFIER, + .may_uds_export = true, + }, + [BPF_PROG_TYPE_SCHED_ACT] = { + .type = "act", + .subdir = "tc", + .section = ELF_SECTION_ACTION, + .may_uds_export = true, + }, + [BPF_PROG_TYPE_XDP] = { + .type = "xdp", + .subdir = "xdp", + .section = ELF_SECTION_PROG, + }, + [BPF_PROG_TYPE_LWT_IN] = { + .type = "lwt_in", + .subdir = "ip", + .section = ELF_SECTION_PROG, + }, + [BPF_PROG_TYPE_LWT_OUT] = { + .type = "lwt_out", + .subdir = "ip", + .section = ELF_SECTION_PROG, + }, + [BPF_PROG_TYPE_LWT_XMIT] = { + .type = "lwt_xmit", + .subdir = "ip", + .section = ELF_SECTION_PROG, + }, + [BPF_PROG_TYPE_LWT_SEG6LOCAL] = { + .type = "lwt_seg6local", + .subdir = "ip", + .section = ELF_SECTION_PROG, + }, +}; + +static const char *bpf_prog_to_subdir(enum bpf_prog_type type) +{ + assert(type < ARRAY_SIZE(__bpf_prog_meta) && + __bpf_prog_meta[type].subdir); + return __bpf_prog_meta[type].subdir; +} + +const char *bpf_prog_to_default_section(enum bpf_prog_type type) +{ + assert(type < ARRAY_SIZE(__bpf_prog_meta) && + __bpf_prog_meta[type].section); + return __bpf_prog_meta[type].section; +} + +#ifdef HAVE_ELF +static int bpf_obj_open(const char *path, enum bpf_prog_type type, + const char *sec, __u32 ifindex, bool verbose); +#else +static int bpf_obj_open(const char *path, enum bpf_prog_type type, + const char *sec, __u32 ifindex, bool verbose) +{ + fprintf(stderr, "No ELF library support compiled in.\n"); + errno = ENOSYS; + return -1; +} +#endif + +static inline __u64 bpf_ptr_to_u64(const void *ptr) +{ + return (__u64)(unsigned long)ptr; +} + +static int bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ +#ifdef __NR_bpf + return syscall(__NR_bpf, cmd, attr, size); +#else + fprintf(stderr, "No bpf syscall, kernel headers too old?\n"); + errno = ENOSYS; + return -1; +#endif +} + +static int bpf_map_update(int fd, const void *key, const void *value, + uint64_t flags) +{ + union bpf_attr attr = {}; + + attr.map_fd = fd; + attr.key = bpf_ptr_to_u64(key); + attr.value = bpf_ptr_to_u64(value); + attr.flags = flags; + + return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); +} + +static int bpf_prog_fd_by_id(uint32_t id) +{ + union bpf_attr attr = {}; + + attr.prog_id = id; + + return bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr)); +} + +static int bpf_prog_info_by_fd(int fd, struct bpf_prog_info *info, + uint32_t *info_len) +{ + union bpf_attr attr = {}; + int ret; + + attr.info.bpf_fd = fd; + attr.info.info = bpf_ptr_to_u64(info); + attr.info.info_len = *info_len; + + *info_len = 0; + ret = bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)); + if (!ret) + *info_len = attr.info.info_len; + + return ret; +} + +int bpf_dump_prog_info(FILE *f, uint32_t id) +{ + struct bpf_prog_info info = {}; + uint32_t len = sizeof(info); + int fd, ret, dump_ok = 0; + SPRINT_BUF(tmp); + + open_json_object("prog"); + print_uint(PRINT_ANY, "id", "id %u ", id); + + fd = bpf_prog_fd_by_id(id); + if (fd < 0) + goto out; + + ret = bpf_prog_info_by_fd(fd, &info, &len); + if (!ret && len) { + int jited = !!info.jited_prog_len; + + print_string(PRINT_ANY, "tag", "tag %s ", + hexstring_n2a(info.tag, sizeof(info.tag), + tmp, sizeof(tmp))); + print_uint(PRINT_JSON, "jited", NULL, jited); + if (jited && !is_json_context()) + fprintf(f, "jited "); + dump_ok = 1; + } + + close(fd); +out: + close_json_object(); + return dump_ok; +} + +static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len, + char **bpf_string, bool *need_release, + const char separator) +{ + char sp; + + if (from_file) { + size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,"); + char *tmp_string, *pos, c_prev = ' '; + FILE *fp; + int c; + + tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len; + tmp_string = pos = calloc(1, tmp_len); + if (tmp_string == NULL) + return -ENOMEM; + + fp = fopen(arg, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + free(tmp_string); + return -ENOENT; + } + + while ((c = fgetc(fp)) != EOF) { + switch (c) { + case '\n': + if (c_prev != ',') + *(pos++) = ','; + c_prev = ','; + break; + case ' ': + case '\t': + if (c_prev != ' ') + *(pos++) = c; + c_prev = ' '; + break; + default: + *(pos++) = c; + c_prev = c; + } + if (pos - tmp_string == tmp_len) + break; + } + + if (!feof(fp)) { + free(tmp_string); + fclose(fp); + return -E2BIG; + } + + fclose(fp); + *pos = 0; + + *need_release = true; + *bpf_string = tmp_string; + } else { + *need_release = false; + *bpf_string = arg; + } + + if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 || + sp != separator) { + if (*need_release) + free(*bpf_string); + return -EINVAL; + } + + return 0; +} + +static int bpf_ops_parse(int argc, char **argv, struct sock_filter *bpf_ops, + bool from_file) +{ + char *bpf_string, *token, separator = ','; + int ret = 0, i = 0; + bool need_release; + __u16 bpf_len = 0; + + if (argc < 1) + return -EINVAL; + if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string, + &need_release, separator)) + return -EINVAL; + if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) { + ret = -EINVAL; + goto out; + } + + token = bpf_string; + while ((token = strchr(token, separator)) && (++token)[0]) { + if (i >= bpf_len) { + fprintf(stderr, "Real program length exceeds encoded length parameter!\n"); + ret = -EINVAL; + goto out; + } + + if (sscanf(token, "%hu %hhu %hhu %u,", + &bpf_ops[i].code, &bpf_ops[i].jt, + &bpf_ops[i].jf, &bpf_ops[i].k) != 4) { + fprintf(stderr, "Error at instruction %d!\n", i); + ret = -EINVAL; + goto out; + } + + i++; + } + + if (i != bpf_len) { + fprintf(stderr, "Parsed program length is less than encoded length parameter!\n"); + ret = -EINVAL; + goto out; + } + ret = bpf_len; +out: + if (need_release) + free(bpf_string); + + return ret; +} + +void bpf_print_ops(struct rtattr *bpf_ops, __u16 len) +{ + struct sock_filter *ops = RTA_DATA(bpf_ops); + int i; + + if (len == 0) + return; + + open_json_object("bytecode"); + print_uint(PRINT_ANY, "length", "bytecode \'%u,", len); + open_json_array(PRINT_JSON, "insns"); + + for (i = 0; i < len; i++) { + open_json_object(NULL); + print_hu(PRINT_ANY, "code", "%hu ", ops[i].code); + print_hhu(PRINT_ANY, "jt", "%hhu ", ops[i].jt); + print_hhu(PRINT_ANY, "jf", "%hhu ", ops[i].jf); + if (i == len - 1) + print_uint(PRINT_ANY, "k", "%u\'", ops[i].k); + else + print_uint(PRINT_ANY, "k", "%u,", ops[i].k); + close_json_object(); + } + + close_json_array(PRINT_JSON, NULL); + close_json_object(); +} + +static void bpf_map_pin_report(const struct bpf_elf_map *pin, + const struct bpf_elf_map *obj) +{ + fprintf(stderr, "Map specification differs from pinned file!\n"); + + if (obj->type != pin->type) + fprintf(stderr, " - Type: %u (obj) != %u (pin)\n", + obj->type, pin->type); + if (obj->size_key != pin->size_key) + fprintf(stderr, " - Size key: %u (obj) != %u (pin)\n", + obj->size_key, pin->size_key); + if (obj->size_value != pin->size_value) + fprintf(stderr, " - Size value: %u (obj) != %u (pin)\n", + obj->size_value, pin->size_value); + if (obj->max_elem != pin->max_elem) + fprintf(stderr, " - Max elems: %u (obj) != %u (pin)\n", + obj->max_elem, pin->max_elem); + if (obj->flags != pin->flags) + fprintf(stderr, " - Flags: %#x (obj) != %#x (pin)\n", + obj->flags, pin->flags); + + fprintf(stderr, "\n"); +} + +struct bpf_prog_data { + unsigned int type; + unsigned int jited; +}; + +struct bpf_map_ext { + struct bpf_prog_data owner; + unsigned int btf_id_key; + unsigned int btf_id_val; +}; + +static int bpf_derive_elf_map_from_fdinfo(int fd, struct bpf_elf_map *map, + struct bpf_map_ext *ext) +{ + unsigned int val, owner_type = 0, owner_jited = 0; + char *file = NULL; + char buff[4096]; + FILE *fp; + int ret; + + ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + free(file); + return ret; + } + memset(map, 0, sizeof(*map)); + + fp = fopen(file, "r"); + free(file); + if (!fp) { + fprintf(stderr, "No procfs support?!\n"); + return -EIO; + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "map_type:\t%u", &val) == 1) + map->type = val; + else if (sscanf(buff, "key_size:\t%u", &val) == 1) + map->size_key = val; + else if (sscanf(buff, "value_size:\t%u", &val) == 1) + map->size_value = val; + else if (sscanf(buff, "max_entries:\t%u", &val) == 1) + map->max_elem = val; + else if (sscanf(buff, "map_flags:\t%i", &val) == 1) + map->flags = val; + else if (sscanf(buff, "owner_prog_type:\t%i", &val) == 1) + owner_type = val; + else if (sscanf(buff, "owner_jited:\t%i", &val) == 1) + owner_jited = val; + } + + fclose(fp); + if (ext) { + memset(ext, 0, sizeof(*ext)); + ext->owner.type = owner_type; + ext->owner.jited = owner_jited; + } + + return 0; +} + +static int bpf_map_selfcheck_pinned(int fd, const struct bpf_elf_map *map, + struct bpf_map_ext *ext, int length, + enum bpf_prog_type type) +{ + struct bpf_elf_map tmp, zero = {}; + int ret; + + ret = bpf_derive_elf_map_from_fdinfo(fd, &tmp, ext); + if (ret < 0) + return ret; + + /* The decision to reject this is on kernel side eventually, but + * at least give the user a chance to know what's wrong. + */ + if (ext->owner.type && ext->owner.type != type) + fprintf(stderr, "Program array map owner types differ: %u (obj) != %u (pin)\n", + type, ext->owner.type); + + if (!memcmp(&tmp, map, length)) { + return 0; + } else { + /* If kernel doesn't have eBPF-related fdinfo, we cannot do much, + * so just accept it. We know we do have an eBPF fd and in this + * case, everything is 0. It is guaranteed that no such map exists + * since map type of 0 is unloadable BPF_MAP_TYPE_UNSPEC. + */ + if (!memcmp(&tmp, &zero, length)) + return 0; + + bpf_map_pin_report(&tmp, map); + return -EINVAL; + } +} + +static int bpf_mnt_fs(const char *target) +{ + bool bind_done = false; + + while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) { + if (errno != EINVAL || bind_done) { + fprintf(stderr, "mount --make-private %s failed: %s\n", + target, strerror(errno)); + return -1; + } + + if (mount(target, target, "none", MS_BIND, NULL)) { + fprintf(stderr, "mount --bind %s %s failed: %s\n", + target, target, strerror(errno)); + return -1; + } + + bind_done = true; + } + + if (mount("bpf", target, "bpf", 0, "mode=0700")) { + fprintf(stderr, "mount -t bpf bpf %s failed: %s\n", + target, strerror(errno)); + return -1; + } + + return 0; +} + +static int bpf_mnt_check_target(const char *target) +{ + struct stat sb = {}; + int ret; + + ret = stat(target, &sb); + if (ret) { + ret = mkdir(target, S_IRWXU); + if (ret) { + fprintf(stderr, "mkdir %s failed: %s\n", target, + strerror(errno)); + return ret; + } + } + + return 0; +} + +static int bpf_valid_mntpt(const char *mnt, unsigned long magic) +{ + struct statfs st_fs; + + if (statfs(mnt, &st_fs) < 0) + return -ENOENT; + if ((unsigned long)st_fs.f_type != magic) + return -ENOENT; + + return 0; +} + +static const char *bpf_find_mntpt_single(unsigned long magic, char *mnt, + int len, const char *mntpt) +{ + int ret; + + ret = bpf_valid_mntpt(mntpt, magic); + if (!ret) { + strlcpy(mnt, mntpt, len); + return mnt; + } + + return NULL; +} + +static const char *bpf_find_mntpt(const char *fstype, unsigned long magic, + char *mnt, int len, + const char * const *known_mnts) +{ + const char * const *ptr; + char type[100]; + FILE *fp; + + if (known_mnts) { + ptr = known_mnts; + while (*ptr) { + if (bpf_find_mntpt_single(magic, mnt, len, *ptr)) + return mnt; + ptr++; + } + } + + if (len != PATH_MAX) + return NULL; + + fp = fopen("/proc/mounts", "r"); + if (fp == NULL) + return NULL; + + while (fscanf(fp, "%*s %" textify(PATH_MAX) "s %99s %*s %*d %*d\n", + mnt, type) == 2) { + if (strcmp(type, fstype) == 0) + break; + } + + fclose(fp); + if (strcmp(type, fstype) != 0) + return NULL; + + return mnt; +} + +int bpf_trace_pipe(void) +{ + char tracefs_mnt[PATH_MAX] = TRACE_DIR_MNT; + static const char * const tracefs_known_mnts[] = { + TRACE_DIR_MNT, + "/sys/kernel/debug/tracing", + "/tracing", + "/trace", + 0, + }; + int fd_in, fd_out = STDERR_FILENO; + char *tpipe = NULL; + const char *mnt; + int ret; + + mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt, + sizeof(tracefs_mnt), tracefs_known_mnts); + if (!mnt) { + fprintf(stderr, "tracefs not mounted?\n"); + return -1; + } + + ret = asprintf(&tpipe, "%s/trace_pipe", mnt); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + free(tpipe); + return ret; + } + + fd_in = open(tpipe, O_RDONLY); + free(tpipe); + if (fd_in < 0) + return -1; + + fprintf(stderr, "Running! Hang up with ^C!\n\n"); + while (1) { + static char buff[4096]; + ssize_t ret; + + ret = read(fd_in, buff, sizeof(buff)); + if (ret > 0 && write(fd_out, buff, ret) == ret) + continue; + break; + } + + close(fd_in); + return -1; +} + +static int bpf_gen_global(const char *bpf_sub_dir) +{ + char *bpf_glo_dir = NULL; + int ret; + + ret = asprintf(&bpf_glo_dir, "%s/%s/", bpf_sub_dir, BPF_DIR_GLOBALS); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = mkdir(bpf_glo_dir, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir, + strerror(errno)); + goto out; + } + + ret = 0; +out: + free(bpf_glo_dir); + return ret; +} + +static int bpf_gen_master(const char *base, const char *name) +{ + char *bpf_sub_dir = NULL; + int ret; + + ret = asprintf(&bpf_sub_dir, "%s%s/", base, name); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = mkdir(bpf_sub_dir, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", bpf_sub_dir, + strerror(errno)); + goto out; + } + + ret = bpf_gen_global(bpf_sub_dir); +out: + free(bpf_sub_dir); + return ret; +} + +static int bpf_slave_via_bind_mnt(const char *full_name, + const char *full_link) +{ + int ret; + + ret = mkdir(full_name, S_IRWXU); + if (ret) { + assert(errno != EEXIST); + fprintf(stderr, "mkdir %s failed: %s\n", full_name, + strerror(errno)); + return ret; + } + + ret = mount(full_link, full_name, "none", MS_BIND, NULL); + if (ret) { + rmdir(full_name); + fprintf(stderr, "mount --bind %s %s failed: %s\n", + full_link, full_name, strerror(errno)); + } + + return ret; +} + +static int bpf_gen_slave(const char *base, const char *name, + const char *link) +{ + char *bpf_lnk_dir = NULL; + char *bpf_sub_dir = NULL; + struct stat sb = {}; + int ret; + + ret = asprintf(&bpf_lnk_dir, "%s%s/", base, link); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = asprintf(&bpf_sub_dir, "%s%s", base, name); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = symlink(bpf_lnk_dir, bpf_sub_dir); + if (ret) { + if (errno != EEXIST) { + if (errno != EPERM) { + fprintf(stderr, "symlink %s failed: %s\n", + bpf_sub_dir, strerror(errno)); + goto out; + } + + ret = bpf_slave_via_bind_mnt(bpf_sub_dir, bpf_lnk_dir); + goto out; + } + + ret = lstat(bpf_sub_dir, &sb); + if (ret) { + fprintf(stderr, "lstat %s failed: %s\n", + bpf_sub_dir, strerror(errno)); + goto out; + } + + if ((sb.st_mode & S_IFMT) != S_IFLNK) { + ret = bpf_gen_global(bpf_sub_dir); + goto out; + } + } + +out: + free(bpf_lnk_dir); + free(bpf_sub_dir); + return ret; +} + +static int bpf_gen_hierarchy(const char *base) +{ + int ret, i; + + ret = bpf_gen_master(base, bpf_prog_to_subdir(__bpf_types[0])); + for (i = 1; i < ARRAY_SIZE(__bpf_types) && !ret; i++) + ret = bpf_gen_slave(base, + bpf_prog_to_subdir(__bpf_types[i]), + bpf_prog_to_subdir(__bpf_types[0])); + return ret; +} + +static const char *bpf_get_work_dir(enum bpf_prog_type type) +{ + static char bpf_tmp[PATH_MAX] = BPF_DIR_MNT; + static char *bpf_wrk_dir; + static const char *mnt; + static bool bpf_mnt_cached; + const char *mnt_env = getenv(BPF_ENV_MNT); + static const char * const bpf_known_mnts[] = { + BPF_DIR_MNT, + "/bpf", + 0, + }; + int ret; + + if (bpf_mnt_cached) { + const char *out = mnt; + + if (out && type) { + snprintf(bpf_tmp, sizeof(bpf_tmp), "%s%s/", + out, bpf_prog_to_subdir(type)); + out = bpf_tmp; + } + return out; + } + + if (mnt_env) + mnt = bpf_find_mntpt_single(BPF_FS_MAGIC, bpf_tmp, + sizeof(bpf_tmp), mnt_env); + else + mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_tmp, + sizeof(bpf_tmp), bpf_known_mnts); + if (!mnt) { + mnt = mnt_env ? : BPF_DIR_MNT; + ret = bpf_mnt_check_target(mnt); + if (!ret) + ret = bpf_mnt_fs(mnt); + if (ret) { + mnt = NULL; + goto out; + } + } + + ret = asprintf(&bpf_wrk_dir, "%s/", mnt); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + free(bpf_wrk_dir); + goto out; + } + + ret = bpf_gen_hierarchy(bpf_wrk_dir); + if (ret) { + mnt = NULL; + goto out; + } + + mnt = bpf_wrk_dir; +out: + bpf_mnt_cached = true; + return mnt; +} + +static int bpf_obj_get(const char *pathname, enum bpf_prog_type type) +{ + union bpf_attr attr = {}; + char tmp[PATH_MAX]; + + if (strlen(pathname) > 2 && pathname[0] == 'm' && + pathname[1] == ':' && bpf_get_work_dir(type)) { + snprintf(tmp, sizeof(tmp), "%s/%s", + bpf_get_work_dir(type), pathname + 2); + pathname = tmp; + } + + attr.pathname = bpf_ptr_to_u64(pathname); + + return bpf(BPF_OBJ_GET, &attr, sizeof(attr)); +} + +static int bpf_obj_pinned(const char *pathname, enum bpf_prog_type type) +{ + int prog_fd = bpf_obj_get(pathname, type); + + if (prog_fd < 0) + fprintf(stderr, "Couldn\'t retrieve pinned program \'%s\': %s\n", + pathname, strerror(errno)); + return prog_fd; +} + +static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl) +{ + const char *file, *section, *uds_name; + bool verbose = false; + int i, ret, argc; + char **argv; + + argv = cfg->argv; + argc = cfg->argc; + + if (opt_tbl[CBPF_BYTECODE] && + (matches(*argv, "bytecode") == 0 || + strcmp(*argv, "bc") == 0)) { + cfg->mode = CBPF_BYTECODE; + } else if (opt_tbl[CBPF_FILE] && + (matches(*argv, "bytecode-file") == 0 || + strcmp(*argv, "bcf") == 0)) { + cfg->mode = CBPF_FILE; + } else if (opt_tbl[EBPF_OBJECT] && + (matches(*argv, "object-file") == 0 || + strcmp(*argv, "obj") == 0)) { + cfg->mode = EBPF_OBJECT; + } else if (opt_tbl[EBPF_PINNED] && + (matches(*argv, "object-pinned") == 0 || + matches(*argv, "pinned") == 0 || + matches(*argv, "fd") == 0)) { + cfg->mode = EBPF_PINNED; + } else { + fprintf(stderr, "What mode is \"%s\"?\n", *argv); + return -1; + } + + NEXT_ARG(); + file = section = uds_name = NULL; + if (cfg->mode == EBPF_OBJECT || cfg->mode == EBPF_PINNED) { + file = *argv; + NEXT_ARG_FWD(); + + if (cfg->type == BPF_PROG_TYPE_UNSPEC) { + if (argc > 0 && matches(*argv, "type") == 0) { + NEXT_ARG(); + for (i = 0; i < ARRAY_SIZE(__bpf_prog_meta); + i++) { + if (!__bpf_prog_meta[i].type) + continue; + if (!matches(*argv, + __bpf_prog_meta[i].type)) { + cfg->type = i; + break; + } + } + + if (cfg->type == BPF_PROG_TYPE_UNSPEC) { + fprintf(stderr, "What type is \"%s\"?\n", + *argv); + return -1; + } + NEXT_ARG_FWD(); + } else { + cfg->type = BPF_PROG_TYPE_SCHED_CLS; + } + } + + section = bpf_prog_to_default_section(cfg->type); + if (argc > 0 && matches(*argv, "section") == 0) { + NEXT_ARG(); + section = *argv; + NEXT_ARG_FWD(); + } + + if (__bpf_prog_meta[cfg->type].may_uds_export) { + uds_name = getenv(BPF_ENV_UDS); + if (argc > 0 && !uds_name && + matches(*argv, "export") == 0) { + NEXT_ARG(); + uds_name = *argv; + NEXT_ARG_FWD(); + } + } + + if (argc > 0 && matches(*argv, "verbose") == 0) { + verbose = true; + NEXT_ARG_FWD(); + } + + PREV_ARG(); + } + + if (cfg->mode == CBPF_BYTECODE || cfg->mode == CBPF_FILE) { + ret = bpf_ops_parse(argc, argv, cfg->opcodes, + cfg->mode == CBPF_FILE); + cfg->n_opcodes = ret; + } else if (cfg->mode == EBPF_OBJECT) { + ret = 0; /* program will be loaded by load stage */ + } else if (cfg->mode == EBPF_PINNED) { + ret = bpf_obj_pinned(file, cfg->type); + cfg->prog_fd = ret; + } else { + return -1; + } + + cfg->object = file; + cfg->section = section; + cfg->uds = uds_name; + cfg->argc = argc; + cfg->argv = argv; + cfg->verbose = verbose; + + return ret; +} + +static int bpf_do_load(struct bpf_cfg_in *cfg) +{ + if (cfg->mode == EBPF_OBJECT) { + cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type, + cfg->section, cfg->ifindex, + cfg->verbose); + return cfg->prog_fd; + } + return 0; +} + +int bpf_load_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops, + void *nl) +{ + char annotation[256]; + int ret; + + ret = bpf_do_load(cfg); + if (ret < 0) + return ret; + + if (cfg->mode == CBPF_BYTECODE || cfg->mode == CBPF_FILE) + ops->cbpf_cb(nl, cfg->opcodes, cfg->n_opcodes); + if (cfg->mode == EBPF_OBJECT || cfg->mode == EBPF_PINNED) { + snprintf(annotation, sizeof(annotation), "%s:[%s]", + basename(cfg->object), cfg->mode == EBPF_PINNED ? + "*fsobj" : cfg->section); + ops->ebpf_cb(nl, cfg->prog_fd, annotation); + } + + return 0; +} + +int bpf_parse_common(struct bpf_cfg_in *cfg, const struct bpf_cfg_ops *ops) +{ + bool opt_tbl[BPF_MODE_MAX] = {}; + + if (ops->cbpf_cb) { + opt_tbl[CBPF_BYTECODE] = true; + opt_tbl[CBPF_FILE] = true; + } + + if (ops->ebpf_cb) { + opt_tbl[EBPF_OBJECT] = true; + opt_tbl[EBPF_PINNED] = true; + } + + return bpf_do_parse(cfg, opt_tbl); +} + +int bpf_parse_and_load_common(struct bpf_cfg_in *cfg, + const struct bpf_cfg_ops *ops, void *nl) +{ + int ret; + + ret = bpf_parse_common(cfg, ops); + if (ret < 0) + return ret; + + return bpf_load_common(cfg, ops, nl); +} + +int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv) +{ + const bool opt_tbl[BPF_MODE_MAX] = { + [EBPF_OBJECT] = true, + [EBPF_PINNED] = true, + }; + const struct bpf_elf_map test = { + .type = BPF_MAP_TYPE_PROG_ARRAY, + .size_key = sizeof(int), + .size_value = sizeof(int), + }; + struct bpf_cfg_in cfg = { + .type = BPF_PROG_TYPE_UNSPEC, + .argc = argc, + .argv = argv, + }; + struct bpf_map_ext ext = {}; + int ret, prog_fd, map_fd; + uint32_t map_key; + + ret = bpf_do_parse(&cfg, opt_tbl); + if (ret < 0) + return ret; + + ret = bpf_do_load(&cfg); + if (ret < 0) + return ret; + + prog_fd = cfg.prog_fd; + + if (key) { + map_key = *key; + } else { + ret = sscanf(cfg.section, "%*i/%i", &map_key); + if (ret != 1) { + fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n"); + ret = -EINVAL; + goto out_prog; + } + } + + map_fd = bpf_obj_get(map_path, cfg.type); + if (map_fd < 0) { + fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n", + map_path, strerror(errno)); + ret = map_fd; + goto out_prog; + } + + ret = bpf_map_selfcheck_pinned(map_fd, &test, &ext, + offsetof(struct bpf_elf_map, max_elem), + cfg.type); + if (ret < 0) { + fprintf(stderr, "Map \'%s\' self-check failed!\n", map_path); + goto out_map; + } + + ret = bpf_map_update(map_fd, &map_key, &prog_fd, BPF_ANY); + if (ret < 0) + fprintf(stderr, "Map update failed: %s\n", strerror(errno)); +out_map: + close(map_fd); +out_prog: + close(prog_fd); + return ret; +} + +int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = {}; + + attr.target_fd = target_fd; + attr.attach_bpf_fd = prog_fd; + attr.attach_type = type; + + return bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)); +} + +int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = {}; + + attr.target_fd = target_fd; + attr.attach_type = type; + + return bpf(BPF_PROG_DETACH, &attr, sizeof(attr)); +} + +static int bpf_prog_load_dev(enum bpf_prog_type type, + const struct bpf_insn *insns, size_t size_insns, + const char *license, __u32 ifindex, + char *log, size_t size_log) +{ + union bpf_attr attr = {}; + + attr.prog_type = type; + attr.insns = bpf_ptr_to_u64(insns); + attr.insn_cnt = size_insns / sizeof(struct bpf_insn); + attr.license = bpf_ptr_to_u64(license); + attr.prog_ifindex = ifindex; + + if (size_log > 0) { + attr.log_buf = bpf_ptr_to_u64(log); + attr.log_size = size_log; + attr.log_level = 1; + } + + return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); +} + +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) +{ + return bpf_prog_load_dev(type, insns, size_insns, license, 0, + log, size_log); +} + +#ifdef HAVE_ELF +struct bpf_elf_prog { + enum bpf_prog_type type; + struct bpf_insn *insns; + unsigned int insns_num; + size_t size; + const char *license; +}; + +struct bpf_hash_entry { + unsigned int pinning; + const char *subpath; + struct bpf_hash_entry *next; +}; + +struct bpf_config { + unsigned int jit_enabled; +}; + +struct bpf_btf { + const struct btf_header *hdr; + const void *raw; + const char *strings; + const struct btf_type **types; + int types_num; +}; + +struct bpf_elf_ctx { + struct bpf_config cfg; + Elf *elf_fd; + GElf_Ehdr elf_hdr; + Elf_Data *sym_tab; + Elf_Data *str_tab; + Elf_Data *btf_data; + char obj_uid[64]; + int obj_fd; + int btf_fd; + int map_fds[ELF_MAX_MAPS]; + struct bpf_elf_map maps[ELF_MAX_MAPS]; + struct bpf_map_ext maps_ext[ELF_MAX_MAPS]; + struct bpf_elf_prog prog_text; + struct bpf_btf btf; + int sym_num; + int map_num; + int map_len; + bool *sec_done; + int sec_maps; + int sec_text; + int sec_btf; + char license[ELF_MAX_LICENSE_LEN]; + enum bpf_prog_type type; + __u32 ifindex; + bool verbose; + bool noafalg; + struct bpf_elf_st stat; + struct bpf_hash_entry *ht[256]; + char *log; + size_t log_size; +}; + +struct bpf_elf_sec_data { + GElf_Shdr sec_hdr; + Elf_Data *sec_data; + const char *sec_name; +}; + +struct bpf_map_data { + int *fds; + const char *obj; + struct bpf_elf_st *st; + struct bpf_elf_map *ent; +}; + +static bool bpf_log_has_data(struct bpf_elf_ctx *ctx) +{ + return ctx->log && ctx->log[0]; +} + +static __check_format_string(2, 3) void +bpf_dump_error(struct bpf_elf_ctx *ctx, const char *format, ...) +{ + va_list vl; + + va_start(vl, format); + vfprintf(stderr, format, vl); + va_end(vl); + + if (bpf_log_has_data(ctx)) { + if (ctx->verbose) { + fprintf(stderr, "%s\n", ctx->log); + } else { + unsigned int off = 0, len = strlen(ctx->log); + + if (len > BPF_MAX_LOG) { + off = len - BPF_MAX_LOG; + fprintf(stderr, "Skipped %u bytes, use \'verb\' option for the full verbose log.\n[...]\n", + off); + } + fprintf(stderr, "%s\n", ctx->log + off); + } + + memset(ctx->log, 0, ctx->log_size); + } +} + +static int bpf_log_realloc(struct bpf_elf_ctx *ctx) +{ + const size_t log_max = UINT_MAX >> 8; + size_t log_size = ctx->log_size; + char *ptr; + + if (!ctx->log) { + log_size = 65536; + } else if (log_size < log_max) { + log_size <<= 1; + if (log_size > log_max) + log_size = log_max; + } else { + return -EINVAL; + } + + ptr = realloc(ctx->log, log_size); + if (!ptr) + return -ENOMEM; + + ptr[0] = 0; + ctx->log = ptr; + ctx->log_size = log_size; + + return 0; +} + +static int bpf_map_create(enum bpf_map_type type, uint32_t size_key, + uint32_t size_value, uint32_t max_elem, + uint32_t flags, int inner_fd, int btf_fd, + uint32_t ifindex, uint32_t btf_id_key, + uint32_t btf_id_val) +{ + union bpf_attr attr = {}; + + attr.map_type = type; + attr.key_size = size_key; + attr.value_size = inner_fd ? sizeof(int) : size_value; + attr.max_entries = max_elem; + attr.map_flags = flags; + attr.inner_map_fd = inner_fd; + attr.map_ifindex = ifindex; + attr.btf_fd = btf_fd; + attr.btf_key_type_id = btf_id_key; + attr.btf_value_type_id = btf_id_val; + + return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); +} + +static int bpf_btf_load(void *btf, size_t size_btf, + char *log, size_t size_log) +{ + union bpf_attr attr = {}; + + attr.btf = bpf_ptr_to_u64(btf); + attr.btf_size = size_btf; + + if (size_log > 0) { + attr.btf_log_buf = bpf_ptr_to_u64(log); + attr.btf_log_size = size_log; + attr.btf_log_level = 1; + } + + return bpf(BPF_BTF_LOAD, &attr, sizeof(attr)); +} + +static int bpf_obj_pin(int fd, const char *pathname) +{ + union bpf_attr attr = {}; + + attr.pathname = bpf_ptr_to_u64(pathname); + attr.bpf_fd = fd; + + return bpf(BPF_OBJ_PIN, &attr, sizeof(attr)); +} + +static int bpf_obj_hash(const char *object, uint8_t *out, size_t len) +{ + struct sockaddr_alg alg = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = "sha1", + }; + int ret, cfd, ofd, ffd; + struct stat stbuff; + ssize_t size; + + if (!object || len != 20) + return -EINVAL; + + cfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (cfd < 0) + return cfd; + + ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg)); + if (ret < 0) + goto out_cfd; + + ofd = accept(cfd, NULL, 0); + if (ofd < 0) { + ret = ofd; + goto out_cfd; + } + + ffd = open(object, O_RDONLY); + if (ffd < 0) { + fprintf(stderr, "Error opening object %s: %s\n", + object, strerror(errno)); + ret = ffd; + goto out_ofd; + } + + ret = fstat(ffd, &stbuff); + if (ret < 0) { + fprintf(stderr, "Error doing fstat: %s\n", + strerror(errno)); + goto out_ffd; + } + + size = sendfile(ofd, ffd, NULL, stbuff.st_size); + if (size != stbuff.st_size) { + fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n", + size, stbuff.st_size, strerror(errno)); + ret = -1; + goto out_ffd; + } + + size = read(ofd, out, len); + if (size != len) { + fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n", + size, len, strerror(errno)); + ret = -1; + } else { + ret = 0; + } +out_ffd: + close(ffd); +out_ofd: + close(ofd); +out_cfd: + close(cfd); + return ret; +} + +static void bpf_init_env(void) +{ + struct rlimit limit = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY, + }; + + /* Don't bother in case we fail! */ + setrlimit(RLIMIT_MEMLOCK, &limit); + + if (!bpf_get_work_dir(BPF_PROG_TYPE_UNSPEC)) + fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n"); +} + +static const char *bpf_custom_pinning(const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + struct bpf_hash_entry *entry; + + entry = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)]; + while (entry && entry->pinning != pinning) + entry = entry->next; + + return entry ? entry->subpath : NULL; +} + +static bool bpf_no_pinning(const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + switch (pinning) { + case PIN_OBJECT_NS: + case PIN_GLOBAL_NS: + return false; + case PIN_NONE: + return true; + default: + return !bpf_custom_pinning(ctx, pinning); + } +} + +static void bpf_make_pathname(char *pathname, size_t len, const char *name, + const struct bpf_elf_ctx *ctx, uint32_t pinning) +{ + switch (pinning) { + case PIN_OBJECT_NS: + snprintf(pathname, len, "%s/%s/%s", + bpf_get_work_dir(ctx->type), + ctx->obj_uid, name); + break; + case PIN_GLOBAL_NS: + snprintf(pathname, len, "%s/%s/%s", + bpf_get_work_dir(ctx->type), + BPF_DIR_GLOBALS, name); + break; + default: + snprintf(pathname, len, "%s/../%s/%s", + bpf_get_work_dir(ctx->type), + bpf_custom_pinning(ctx, pinning), name); + break; + } +} + +static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx, + uint32_t pinning) +{ + char pathname[PATH_MAX]; + + if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type)) + return 0; + + bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning); + return bpf_obj_get(pathname, ctx->type); +} + +static int bpf_make_obj_path(const struct bpf_elf_ctx *ctx) +{ + char *tmp = NULL; + int ret; + + ret = asprintf(&tmp, "%s/%s", bpf_get_work_dir(ctx->type), ctx->obj_uid); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = mkdir(tmp, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno)); + goto out; + } + + ret = 0; +out: + free(tmp); + return ret; +} + +static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx, + const char *todo) +{ + char *tmp = NULL; + char *rem = NULL; + char *sub; + int ret; + + ret = asprintf(&tmp, "%s/../", bpf_get_work_dir(ctx->type)); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + ret = asprintf(&rem, "%s/", todo); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + goto out; + } + + sub = strtok(rem, "/"); + while (sub) { + if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX) + return -EINVAL; + + strcat(tmp, sub); + strcat(tmp, "/"); + + ret = mkdir(tmp, S_IRWXU); + if (ret && errno != EEXIST) { + fprintf(stderr, "mkdir %s failed: %s\n", tmp, + strerror(errno)); + goto out; + } + + sub = strtok(NULL, "/"); + } + + ret = 0; +out: + free(rem); + free(tmp); + return ret; +} + +static int bpf_place_pinned(int fd, const char *name, + const struct bpf_elf_ctx *ctx, uint32_t pinning) +{ + char pathname[PATH_MAX]; + const char *tmp; + int ret = 0; + + if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type)) + return 0; + + if (pinning == PIN_OBJECT_NS) + ret = bpf_make_obj_path(ctx); + else if ((tmp = bpf_custom_pinning(ctx, pinning))) + ret = bpf_make_custom_path(ctx, tmp); + if (ret < 0) + return ret; + + bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning); + return bpf_obj_pin(fd, pathname); +} + +static void bpf_prog_report(int fd, const char *section, + const struct bpf_elf_prog *prog, + struct bpf_elf_ctx *ctx) +{ + unsigned int insns = prog->size / sizeof(struct bpf_insn); + + fprintf(stderr, "\nProg section \'%s\' %s%s (%d)!\n", section, + fd < 0 ? "rejected: " : "loaded", + fd < 0 ? strerror(errno) : "", + fd < 0 ? errno : fd); + + fprintf(stderr, " - Type: %u\n", prog->type); + fprintf(stderr, " - Instructions: %u (%u over limit)\n", + insns, insns > BPF_MAXINSNS ? insns - BPF_MAXINSNS : 0); + fprintf(stderr, " - License: %s\n\n", prog->license); + + bpf_dump_error(ctx, "Verifier analysis:\n\n"); +} + +static int bpf_prog_attach(const char *section, + const struct bpf_elf_prog *prog, + struct bpf_elf_ctx *ctx) +{ + int tries = 0, fd; +retry: + errno = 0; + fd = bpf_prog_load_dev(prog->type, prog->insns, prog->size, + prog->license, ctx->ifindex, + ctx->log, ctx->log_size); + if (fd < 0 || ctx->verbose) { + /* The verifier log is pretty chatty, sometimes so chatty + * on larger programs, that we could fail to dump everything + * into our buffer. Still, try to give a debuggable error + * log for the user, so enlarge it and re-fail. + */ + if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) { + if (tries++ < 10 && !bpf_log_realloc(ctx)) + goto retry; + + fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n", + ctx->log_size, tries); + return fd; + } + + bpf_prog_report(fd, section, prog, ctx); + } + + return fd; +} + +static void bpf_map_report(int fd, const char *name, + const struct bpf_elf_map *map, + struct bpf_elf_ctx *ctx, int inner_fd) +{ + fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name, + fd < 0 ? "rejected: " : "loaded", + fd < 0 ? strerror(errno) : "", + fd < 0 ? errno : fd); + + fprintf(stderr, " - Type: %u\n", map->type); + fprintf(stderr, " - Identifier: %u\n", map->id); + fprintf(stderr, " - Pinning: %u\n", map->pinning); + fprintf(stderr, " - Size key: %u\n", map->size_key); + fprintf(stderr, " - Size value: %u\n", + inner_fd ? (int)sizeof(int) : map->size_value); + fprintf(stderr, " - Max elems: %u\n", map->max_elem); + fprintf(stderr, " - Flags: %#x\n\n", map->flags); +} + +static int bpf_find_map_id(const struct bpf_elf_ctx *ctx, uint32_t id) +{ + int i; + + for (i = 0; i < ctx->map_num; i++) { + if (ctx->maps[i].id != id) + continue; + if (ctx->map_fds[i] < 0) + return -EINVAL; + + return ctx->map_fds[i]; + } + + return -ENOENT; +} + +static void bpf_report_map_in_map(int outer_fd, uint32_t idx) +{ + struct bpf_elf_map outer_map; + int ret; + + fprintf(stderr, "Cannot insert map into map! "); + + ret = bpf_derive_elf_map_from_fdinfo(outer_fd, &outer_map, NULL); + if (!ret) { + if (idx >= outer_map.max_elem && + outer_map.type == BPF_MAP_TYPE_ARRAY_OF_MAPS) { + fprintf(stderr, "Outer map has %u elements, index %u is invalid!\n", + outer_map.max_elem, idx); + return; + } + } + + fprintf(stderr, "Different map specs used for outer and inner map?\n"); +} + +static bool bpf_is_map_in_map_type(const struct bpf_elf_map *map) +{ + return map->type == BPF_MAP_TYPE_ARRAY_OF_MAPS || + map->type == BPF_MAP_TYPE_HASH_OF_MAPS; +} + +static bool bpf_map_offload_neutral(enum bpf_map_type type) +{ + return type == BPF_MAP_TYPE_PERF_EVENT_ARRAY; +} + +static int bpf_map_attach(const char *name, struct bpf_elf_ctx *ctx, + const struct bpf_elf_map *map, struct bpf_map_ext *ext, + int *have_map_in_map) +{ + int fd, ifindex, ret, map_inner_fd = 0; + bool retried = false; + +probe: + fd = bpf_probe_pinned(name, ctx, map->pinning); + if (fd > 0) { + ret = bpf_map_selfcheck_pinned(fd, map, ext, + offsetof(struct bpf_elf_map, + id), ctx->type); + if (ret < 0) { + close(fd); + fprintf(stderr, "Map \'%s\' self-check failed!\n", + name); + return ret; + } + if (ctx->verbose) + fprintf(stderr, "Map \'%s\' loaded as pinned!\n", + name); + return fd; + } + + if (have_map_in_map && bpf_is_map_in_map_type(map)) { + (*have_map_in_map)++; + if (map->inner_id) + return 0; + fprintf(stderr, "Map \'%s\' cannot be created since no inner map ID defined!\n", + name); + return -EINVAL; + } + + if (!have_map_in_map && bpf_is_map_in_map_type(map)) { + map_inner_fd = bpf_find_map_id(ctx, map->inner_id); + if (map_inner_fd < 0) { + fprintf(stderr, "Map \'%s\' cannot be loaded. Inner map with ID %u not found!\n", + name, map->inner_id); + return -EINVAL; + } + } + + ifindex = bpf_map_offload_neutral(map->type) ? 0 : ctx->ifindex; + errno = 0; + fd = bpf_map_create(map->type, map->size_key, map->size_value, + map->max_elem, map->flags, map_inner_fd, ctx->btf_fd, + ifindex, ext->btf_id_key, ext->btf_id_val); + + if (fd < 0 || ctx->verbose) { + bpf_map_report(fd, name, map, ctx, map_inner_fd); + if (fd < 0) + return fd; + } + + ret = bpf_place_pinned(fd, name, ctx, map->pinning); + if (ret < 0) { + close(fd); + if (!retried && errno == EEXIST) { + retried = true; + goto probe; + } + fprintf(stderr, "Could not pin %s map: %s\n", name, + strerror(errno)); + return ret; + } + + return fd; +} + +static const char *bpf_str_tab_name(const struct bpf_elf_ctx *ctx, + const GElf_Sym *sym) +{ + return ctx->str_tab->d_buf + sym->st_name; +} + +static int bpf_btf_find(struct bpf_elf_ctx *ctx, const char *name) +{ + const struct btf_type *type; + const char *res; + int id; + + for (id = 1; id < ctx->btf.types_num; id++) { + type = ctx->btf.types[id]; + if (type->name_off >= ctx->btf.hdr->str_len) + continue; + res = &ctx->btf.strings[type->name_off]; + if (!strcmp(res, name)) + return id; + } + + return -ENOENT; +} + +static int bpf_btf_find_kv(struct bpf_elf_ctx *ctx, const struct bpf_elf_map *map, + const char *name, uint32_t *id_key, uint32_t *id_val) +{ + const struct btf_member *key, *val; + const struct btf_type *type; + char btf_name[512]; + const char *res; + int id; + + snprintf(btf_name, sizeof(btf_name), "____btf_map_%s", name); + id = bpf_btf_find(ctx, btf_name); + if (id < 0) + return id; + + type = ctx->btf.types[id]; + if (BTF_INFO_KIND(type->info) != BTF_KIND_STRUCT) + return -EINVAL; + if (BTF_INFO_VLEN(type->info) != 2) + return -EINVAL; + + key = ((void *) type) + sizeof(*type); + val = key + 1; + if (!key->type || key->type >= ctx->btf.types_num || + !val->type || val->type >= ctx->btf.types_num) + return -EINVAL; + + if (key->name_off >= ctx->btf.hdr->str_len || + val->name_off >= ctx->btf.hdr->str_len) + return -EINVAL; + + res = &ctx->btf.strings[key->name_off]; + if (strcmp(res, "key")) + return -EINVAL; + + res = &ctx->btf.strings[val->name_off]; + if (strcmp(res, "value")) + return -EINVAL; + + *id_key = key->type; + *id_val = val->type; + return 0; +} + +static void bpf_btf_annotate(struct bpf_elf_ctx *ctx, int which, const char *name) +{ + uint32_t id_key = 0, id_val = 0; + + if (!bpf_btf_find_kv(ctx, &ctx->maps[which], name, &id_key, &id_val)) { + ctx->maps_ext[which].btf_id_key = id_key; + ctx->maps_ext[which].btf_id_val = id_val; + } +} + +static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which) +{ + const char *name; + GElf_Sym sym; + int i; + + for (i = 0; i < ctx->sym_num; i++) { + int type; + + if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym) + continue; + + type = GELF_ST_TYPE(sym.st_info); + if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL || + (type != STT_NOTYPE && type != STT_OBJECT) || + sym.st_shndx != ctx->sec_maps || + sym.st_value / ctx->map_len != which) + continue; + + name = bpf_str_tab_name(ctx, &sym); + bpf_btf_annotate(ctx, which, name); + return name; + } + + return NULL; +} + +static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx) +{ + int i, j, ret, fd, inner_fd, inner_idx, have_map_in_map = 0; + const char *map_name; + + for (i = 0; i < ctx->map_num; i++) { + if (ctx->maps[i].pinning == PIN_OBJECT_NS && + ctx->noafalg) { + fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n"); + return -ENOTSUP; + } + + map_name = bpf_map_fetch_name(ctx, i); + if (!map_name) + return -EIO; + + fd = bpf_map_attach(map_name, ctx, &ctx->maps[i], + &ctx->maps_ext[i], &have_map_in_map); + if (fd < 0) + return fd; + + ctx->map_fds[i] = !fd ? -1 : fd; + } + + for (i = 0; have_map_in_map && i < ctx->map_num; i++) { + if (ctx->map_fds[i] >= 0) + continue; + + map_name = bpf_map_fetch_name(ctx, i); + if (!map_name) + return -EIO; + + fd = bpf_map_attach(map_name, ctx, &ctx->maps[i], + &ctx->maps_ext[i], NULL); + if (fd < 0) + return fd; + + ctx->map_fds[i] = fd; + } + + for (i = 0; have_map_in_map && i < ctx->map_num; i++) { + if (!ctx->maps[i].id || + ctx->maps[i].inner_id || + ctx->maps[i].inner_idx == -1) + continue; + + inner_fd = ctx->map_fds[i]; + inner_idx = ctx->maps[i].inner_idx; + + for (j = 0; j < ctx->map_num; j++) { + if (!bpf_is_map_in_map_type(&ctx->maps[j])) + continue; + if (ctx->maps[j].inner_id != ctx->maps[i].id) + continue; + + ret = bpf_map_update(ctx->map_fds[j], &inner_idx, + &inner_fd, BPF_ANY); + if (ret < 0) { + bpf_report_map_in_map(ctx->map_fds[j], + inner_idx); + return ret; + } + } + } + + return 0; +} + +static int bpf_map_num_sym(struct bpf_elf_ctx *ctx) +{ + int i, num = 0; + GElf_Sym sym; + + for (i = 0; i < ctx->sym_num; i++) { + int type; + + if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym) + continue; + + type = GELF_ST_TYPE(sym.st_info); + if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL || + (type != STT_NOTYPE && type != STT_OBJECT) || + sym.st_shndx != ctx->sec_maps) + continue; + num++; + } + + return num; +} + +static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + Elf_Data *sec_edata; + GElf_Shdr sec_hdr; + Elf_Scn *sec_fd; + char *sec_name; + + memset(data, 0, sizeof(*data)); + + sec_fd = elf_getscn(ctx->elf_fd, section); + if (!sec_fd) + return -EINVAL; + if (gelf_getshdr(sec_fd, &sec_hdr) != &sec_hdr) + return -EIO; + + sec_name = elf_strptr(ctx->elf_fd, ctx->elf_hdr.e_shstrndx, + sec_hdr.sh_name); + if (!sec_name || !sec_hdr.sh_size) + return -ENOENT; + + sec_edata = elf_getdata(sec_fd, NULL); + if (!sec_edata || elf_getdata(sec_fd, sec_edata)) + return -EIO; + + memcpy(&data->sec_hdr, &sec_hdr, sizeof(sec_hdr)); + + data->sec_name = sec_name; + data->sec_data = sec_edata; + return 0; +} + +struct bpf_elf_map_min { + __u32 type; + __u32 size_key; + __u32 size_value; + __u32 max_elem; +}; + +static int bpf_fetch_maps_begin(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->map_num = data->sec_data->d_size; + ctx->sec_maps = section; + ctx->sec_done[section] = true; + + if (ctx->map_num > sizeof(ctx->maps)) { + fprintf(stderr, "Too many BPF maps in ELF section!\n"); + return -ENOMEM; + } + + memcpy(ctx->maps, data->sec_data->d_buf, ctx->map_num); + return 0; +} + +static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end) +{ + GElf_Sym sym; + int off, i; + + for (off = 0; off < end; off += ctx->map_len) { + /* Order doesn't need to be linear here, hence we walk + * the table again. + */ + for (i = 0; i < ctx->sym_num; i++) { + int type; + + if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym) + continue; + + type = GELF_ST_TYPE(sym.st_info); + if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL || + (type != STT_NOTYPE && type != STT_OBJECT) || + sym.st_shndx != ctx->sec_maps) + continue; + if (sym.st_value == off) + break; + if (i == ctx->sym_num - 1) + return -1; + } + } + + return off == end ? 0 : -1; +} + +static int bpf_fetch_maps_end(struct bpf_elf_ctx *ctx) +{ + struct bpf_elf_map fixup[ARRAY_SIZE(ctx->maps)] = {}; + int i, sym_num = bpf_map_num_sym(ctx); + __u8 *buff; + + if (sym_num == 0 || sym_num > ARRAY_SIZE(ctx->maps)) { + fprintf(stderr, "%u maps not supported in current map section!\n", + sym_num); + return -EINVAL; + } + + if (ctx->map_num % sym_num != 0 || + ctx->map_num % sizeof(__u32) != 0) { + fprintf(stderr, "Number BPF map symbols are not multiple of struct bpf_elf_map!\n"); + return -EINVAL; + } + + ctx->map_len = ctx->map_num / sym_num; + if (bpf_map_verify_all_offs(ctx, ctx->map_num)) { + fprintf(stderr, "Different struct bpf_elf_map in use!\n"); + return -EINVAL; + } + + if (ctx->map_len == sizeof(struct bpf_elf_map)) { + ctx->map_num = sym_num; + return 0; + } else if (ctx->map_len > sizeof(struct bpf_elf_map)) { + fprintf(stderr, "struct bpf_elf_map not supported, coming from future version?\n"); + return -EINVAL; + } else if (ctx->map_len < sizeof(struct bpf_elf_map_min)) { + fprintf(stderr, "struct bpf_elf_map too small, not supported!\n"); + return -EINVAL; + } + + ctx->map_num = sym_num; + for (i = 0, buff = (void *)ctx->maps; i < ctx->map_num; + i++, buff += ctx->map_len) { + /* The fixup leaves the rest of the members as zero, which + * is fine currently, but option exist to set some other + * default value as well when needed in future. + */ + memcpy(&fixup[i], buff, ctx->map_len); + } + + memcpy(ctx->maps, fixup, sizeof(fixup)); + if (ctx->verbose) + printf("%zu bytes struct bpf_elf_map fixup performed due to size mismatch!\n", + sizeof(struct bpf_elf_map) - ctx->map_len); + return 0; +} + +static int bpf_fetch_license(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + if (data->sec_data->d_size > sizeof(ctx->license)) + return -ENOMEM; + + memcpy(ctx->license, data->sec_data->d_buf, data->sec_data->d_size); + ctx->sec_done[section] = true; + return 0; +} + +static int bpf_fetch_symtab(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->sym_tab = data->sec_data; + ctx->sym_num = data->sec_hdr.sh_size / data->sec_hdr.sh_entsize; + ctx->sec_done[section] = true; + return 0; +} + +static int bpf_fetch_strtab(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->str_tab = data->sec_data; + ctx->sec_done[section] = true; + return 0; +} + +static int bpf_fetch_text(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->sec_text = section; + ctx->sec_done[section] = true; + return 0; +} + +static void bpf_btf_report(int fd, struct bpf_elf_ctx *ctx) +{ + fprintf(stderr, "\nBTF debug data section \'.BTF\' %s%s (%d)!\n", + fd < 0 ? "rejected: " : "loaded", + fd < 0 ? strerror(errno) : "", + fd < 0 ? errno : fd); + + fprintf(stderr, " - Length: %zu\n", ctx->btf_data->d_size); + + bpf_dump_error(ctx, "Verifier analysis:\n\n"); +} + +static int bpf_btf_attach(struct bpf_elf_ctx *ctx) +{ + int tries = 0, fd; +retry: + errno = 0; + fd = bpf_btf_load(ctx->btf_data->d_buf, ctx->btf_data->d_size, + ctx->log, ctx->log_size); + if (fd < 0 || ctx->verbose) { + if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) { + if (tries++ < 10 && !bpf_log_realloc(ctx)) + goto retry; + + fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n", + ctx->log_size, tries); + return fd; + } + + if (bpf_log_has_data(ctx)) + bpf_btf_report(fd, ctx); + } + + return fd; +} + +static int bpf_fetch_btf_begin(struct bpf_elf_ctx *ctx, int section, + struct bpf_elf_sec_data *data) +{ + ctx->btf_data = data->sec_data; + ctx->sec_btf = section; + ctx->sec_done[section] = true; + return 0; +} + +static int bpf_btf_check_header(struct bpf_elf_ctx *ctx) +{ + const struct btf_header *hdr = ctx->btf_data->d_buf; + const char *str_start, *str_end; + unsigned int data_len; + + if (hdr->magic != BTF_MAGIC) { + fprintf(stderr, "Object has wrong BTF magic: %x, expected: %x!\n", + hdr->magic, BTF_MAGIC); + return -EINVAL; + } + + if (hdr->version != BTF_VERSION) { + fprintf(stderr, "Object has wrong BTF version: %u, expected: %u!\n", + hdr->version, BTF_VERSION); + return -EINVAL; + } + + if (hdr->flags) { + fprintf(stderr, "Object has unsupported BTF flags %x!\n", + hdr->flags); + return -EINVAL; + } + + data_len = ctx->btf_data->d_size - sizeof(*hdr); + if (data_len < hdr->type_off || + data_len < hdr->str_off || + data_len < hdr->type_len + hdr->str_len || + hdr->type_off >= hdr->str_off || + hdr->type_off + hdr->type_len != hdr->str_off || + hdr->str_off + hdr->str_len != data_len || + (hdr->type_off & (sizeof(uint32_t) - 1))) { + fprintf(stderr, "Object has malformed BTF data!\n"); + return -EINVAL; + } + + ctx->btf.hdr = hdr; + ctx->btf.raw = hdr + 1; + + str_start = ctx->btf.raw + hdr->str_off; + str_end = str_start + hdr->str_len; + if (!hdr->str_len || + hdr->str_len - 1 > BTF_MAX_NAME_OFFSET || + str_start[0] || str_end[-1]) { + fprintf(stderr, "Object has malformed BTF string data!\n"); + return -EINVAL; + } + + ctx->btf.strings = str_start; + return 0; +} + +static int bpf_btf_register_type(struct bpf_elf_ctx *ctx, + const struct btf_type *type) +{ + int cur = ctx->btf.types_num, num = cur + 1; + const struct btf_type **types; + + types = realloc(ctx->btf.types, num * sizeof(type)); + if (!types) { + free(ctx->btf.types); + ctx->btf.types = NULL; + ctx->btf.types_num = 0; + return -ENOMEM; + } + + ctx->btf.types = types; + ctx->btf.types[cur] = type; + ctx->btf.types_num = num; + return 0; +} + +static struct btf_type btf_type_void; + +static int bpf_btf_prep_type_data(struct bpf_elf_ctx *ctx) +{ + const void *type_cur = ctx->btf.raw + ctx->btf.hdr->type_off; + const void *type_end = ctx->btf.raw + ctx->btf.hdr->str_off; + const struct btf_type *type; + uint16_t var_len; + int ret, kind; + + ret = bpf_btf_register_type(ctx, &btf_type_void); + if (ret < 0) + return ret; + + while (type_cur < type_end) { + type = type_cur; + type_cur += sizeof(*type); + + var_len = BTF_INFO_VLEN(type->info); + kind = BTF_INFO_KIND(type->info); + + switch (kind) { + case BTF_KIND_INT: + type_cur += sizeof(int); + break; + case BTF_KIND_ARRAY: + type_cur += sizeof(struct btf_array); + break; + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + type_cur += var_len * sizeof(struct btf_member); + break; + case BTF_KIND_ENUM: + type_cur += var_len * sizeof(struct btf_enum); + break; + case BTF_KIND_FUNC_PROTO: + type_cur += var_len * sizeof(struct btf_param); + break; + case BTF_KIND_TYPEDEF: + case BTF_KIND_PTR: + case BTF_KIND_FWD: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + case BTF_KIND_FUNC: + break; + default: + fprintf(stderr, "Object has unknown BTF type: %u!\n", kind); + return -EINVAL; + } + + ret = bpf_btf_register_type(ctx, type); + if (ret < 0) + return ret; + } + + return 0; +} + +static int bpf_btf_prep_data(struct bpf_elf_ctx *ctx) +{ + int ret = bpf_btf_check_header(ctx); + + if (!ret) + return bpf_btf_prep_type_data(ctx); + return ret; +} + +static void bpf_fetch_btf_end(struct bpf_elf_ctx *ctx) +{ + int fd = bpf_btf_attach(ctx); + + if (fd < 0) + return; + ctx->btf_fd = fd; + if (bpf_btf_prep_data(ctx) < 0) { + close(ctx->btf_fd); + ctx->btf_fd = 0; + } +} + +static bool bpf_has_map_data(const struct bpf_elf_ctx *ctx) +{ + return ctx->sym_tab && ctx->str_tab && ctx->sec_maps; +} + +static bool bpf_has_btf_data(const struct bpf_elf_ctx *ctx) +{ + return ctx->sec_btf; +} + +static bool bpf_has_call_data(const struct bpf_elf_ctx *ctx) +{ + return ctx->sec_text; +} + +static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx, bool check_text_sec) +{ + struct bpf_elf_sec_data data; + int i, ret = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + ret = bpf_fill_section_data(ctx, i, &data); + if (ret < 0) + continue; + + if (data.sec_hdr.sh_type == SHT_PROGBITS && + !strcmp(data.sec_name, ELF_SECTION_MAPS)) + ret = bpf_fetch_maps_begin(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_PROGBITS && + !strcmp(data.sec_name, ELF_SECTION_LICENSE)) + ret = bpf_fetch_license(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_PROGBITS && + (data.sec_hdr.sh_flags & SHF_EXECINSTR) && + !strcmp(data.sec_name, ".text") && + check_text_sec) + ret = bpf_fetch_text(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_SYMTAB && + !strcmp(data.sec_name, ".symtab")) + ret = bpf_fetch_symtab(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_STRTAB && + !strcmp(data.sec_name, ".strtab")) + ret = bpf_fetch_strtab(ctx, i, &data); + else if (data.sec_hdr.sh_type == SHT_PROGBITS && + !strcmp(data.sec_name, ".BTF")) + ret = bpf_fetch_btf_begin(ctx, i, &data); + if (ret < 0) { + fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n", + i); + return ret; + } + } + + if (bpf_has_btf_data(ctx)) + bpf_fetch_btf_end(ctx); + if (bpf_has_map_data(ctx)) { + ret = bpf_fetch_maps_end(ctx); + if (ret < 0) { + fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n"); + return ret; + } + + ret = bpf_maps_attach_all(ctx); + if (ret < 0) { + fprintf(stderr, "Error loading maps into kernel!\n"); + return ret; + } + } + + return ret; +} + +static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section, + bool *sseen) +{ + struct bpf_elf_sec_data data; + struct bpf_elf_prog prog; + int ret, i, fd = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + if (ctx->sec_done[i]) + continue; + + ret = bpf_fill_section_data(ctx, i, &data); + if (ret < 0 || + !(data.sec_hdr.sh_type == SHT_PROGBITS && + (data.sec_hdr.sh_flags & SHF_EXECINSTR) && + !strcmp(data.sec_name, section))) + continue; + + *sseen = true; + + memset(&prog, 0, sizeof(prog)); + prog.type = ctx->type; + prog.license = ctx->license; + prog.size = data.sec_data->d_size; + prog.insns_num = prog.size / sizeof(struct bpf_insn); + prog.insns = data.sec_data->d_buf; + + fd = bpf_prog_attach(section, &prog, ctx); + if (fd < 0) + return fd; + + ctx->sec_done[i] = true; + break; + } + + return fd; +} + +struct bpf_relo_props { + struct bpf_tail_call { + unsigned int total; + unsigned int jited; + } tc; + int main_num; +}; + +static int bpf_apply_relo_map(struct bpf_elf_ctx *ctx, struct bpf_elf_prog *prog, + GElf_Rel *relo, GElf_Sym *sym, + struct bpf_relo_props *props) +{ + unsigned int insn_off = relo->r_offset / sizeof(struct bpf_insn); + unsigned int map_idx = sym->st_value / ctx->map_len; + + if (insn_off >= prog->insns_num) + return -EINVAL; + if (prog->insns[insn_off].code != (BPF_LD | BPF_IMM | BPF_DW)) { + fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n", + insn_off); + return -EINVAL; + } + + if (map_idx >= ARRAY_SIZE(ctx->map_fds)) + return -EINVAL; + if (!ctx->map_fds[map_idx]) + return -EINVAL; + if (ctx->maps[map_idx].type == BPF_MAP_TYPE_PROG_ARRAY) { + props->tc.total++; + if (ctx->maps_ext[map_idx].owner.jited || + (ctx->maps_ext[map_idx].owner.type == 0 && + ctx->cfg.jit_enabled)) + props->tc.jited++; + } + + prog->insns[insn_off].src_reg = BPF_PSEUDO_MAP_FD; + prog->insns[insn_off].imm = ctx->map_fds[map_idx]; + return 0; +} + +static int bpf_apply_relo_call(struct bpf_elf_ctx *ctx, struct bpf_elf_prog *prog, + GElf_Rel *relo, GElf_Sym *sym, + struct bpf_relo_props *props) +{ + unsigned int insn_off = relo->r_offset / sizeof(struct bpf_insn); + struct bpf_elf_prog *prog_text = &ctx->prog_text; + + if (insn_off >= prog->insns_num) + return -EINVAL; + if (prog->insns[insn_off].code != (BPF_JMP | BPF_CALL) && + prog->insns[insn_off].src_reg != BPF_PSEUDO_CALL) { + fprintf(stderr, "ELF contains relo data for non call instruction at offset %u! Compiler bug?!\n", + insn_off); + return -EINVAL; + } + + if (!props->main_num) { + struct bpf_insn *insns = realloc(prog->insns, + prog->size + prog_text->size); + if (!insns) + return -ENOMEM; + + memcpy(insns + prog->insns_num, prog_text->insns, + prog_text->size); + props->main_num = prog->insns_num; + prog->insns = insns; + prog->insns_num += prog_text->insns_num; + prog->size += prog_text->size; + } + + prog->insns[insn_off].imm += props->main_num - insn_off; + return 0; +} + +static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx, + struct bpf_elf_sec_data *data_relo, + struct bpf_elf_prog *prog, + struct bpf_relo_props *props) +{ + GElf_Shdr *rhdr = &data_relo->sec_hdr; + int relo_ent, relo_num = rhdr->sh_size / rhdr->sh_entsize; + + for (relo_ent = 0; relo_ent < relo_num; relo_ent++) { + GElf_Rel relo; + GElf_Sym sym; + int ret = -EIO; + + if (gelf_getrel(data_relo->sec_data, relo_ent, &relo) != &relo) + return -EIO; + if (gelf_getsym(ctx->sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym) + return -EIO; + + if (sym.st_shndx == ctx->sec_maps) + ret = bpf_apply_relo_map(ctx, prog, &relo, &sym, props); + else if (sym.st_shndx == ctx->sec_text) + ret = bpf_apply_relo_call(ctx, prog, &relo, &sym, props); + else + fprintf(stderr, "ELF contains non-{map,call} related relo data in entry %u pointing to section %u! Compiler bug?!\n", + relo_ent, sym.st_shndx); + if (ret < 0) + return ret; + } + + return 0; +} + +static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section, + bool *lderr, bool *sseen, struct bpf_elf_prog *prog) +{ + struct bpf_elf_sec_data data_relo, data_insn; + int ret, idx, i, fd = -1; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + struct bpf_relo_props props = {}; + + ret = bpf_fill_section_data(ctx, i, &data_relo); + if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL) + continue; + + idx = data_relo.sec_hdr.sh_info; + + ret = bpf_fill_section_data(ctx, idx, &data_insn); + if (ret < 0 || + !(data_insn.sec_hdr.sh_type == SHT_PROGBITS && + (data_insn.sec_hdr.sh_flags & SHF_EXECINSTR) && + !strcmp(data_insn.sec_name, section))) + continue; + if (sseen) + *sseen = true; + + memset(prog, 0, sizeof(*prog)); + prog->type = ctx->type; + prog->license = ctx->license; + prog->size = data_insn.sec_data->d_size; + prog->insns_num = prog->size / sizeof(struct bpf_insn); + prog->insns = malloc(prog->size); + if (!prog->insns) { + *lderr = true; + return -ENOMEM; + } + + memcpy(prog->insns, data_insn.sec_data->d_buf, prog->size); + + ret = bpf_apply_relo_data(ctx, &data_relo, prog, &props); + if (ret < 0) { + *lderr = true; + if (ctx->sec_text != idx) + free(prog->insns); + return ret; + } + if (ctx->sec_text == idx) { + fd = 0; + goto out; + } + + fd = bpf_prog_attach(section, prog, ctx); + free(prog->insns); + if (fd < 0) { + *lderr = true; + if (props.tc.total) { + if (ctx->cfg.jit_enabled && + props.tc.total != props.tc.jited) + fprintf(stderr, "JIT enabled, but only %u/%u tail call maps in the program have JITed owner!\n", + props.tc.jited, props.tc.total); + if (!ctx->cfg.jit_enabled && + props.tc.jited) + fprintf(stderr, "JIT disabled, but %u/%u tail call maps in the program have JITed owner!\n", + props.tc.jited, props.tc.total); + } + return fd; + } +out: + ctx->sec_done[i] = true; + ctx->sec_done[idx] = true; + break; + } + + return fd; +} + +static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section) +{ + bool lderr = false, sseen = false; + struct bpf_elf_prog prog; + int ret = -1; + + if (bpf_has_call_data(ctx)) { + ret = bpf_fetch_prog_relo(ctx, ".text", &lderr, NULL, + &ctx->prog_text); + if (ret < 0) + return ret; + } + + if (bpf_has_map_data(ctx) || bpf_has_call_data(ctx)) + ret = bpf_fetch_prog_relo(ctx, section, &lderr, &sseen, &prog); + if (ret < 0 && !lderr) + ret = bpf_fetch_prog(ctx, section, &sseen); + if (ret < 0 && !sseen) + fprintf(stderr, "Program section \'%s\' not found in ELF file!\n", + section); + return ret; +} + +static int bpf_find_map_by_id(struct bpf_elf_ctx *ctx, uint32_t id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) + if (ctx->map_fds[i] && ctx->maps[i].id == id && + ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) + return i; + return -1; +} + +struct bpf_jited_aux { + int prog_fd; + int map_fd; + struct bpf_prog_data prog; + struct bpf_map_ext map; +}; + +static int bpf_derive_prog_from_fdinfo(int fd, struct bpf_prog_data *prog) +{ + char *file = NULL; + char buff[4096]; + unsigned int val; + FILE *fp; + int ret; + + ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd); + if (ret < 0) { + fprintf(stderr, "asprintf failed: %s\n", strerror(errno)); + free(file); + return ret; + } + + memset(prog, 0, sizeof(*prog)); + + fp = fopen(file, "r"); + free(file); + if (!fp) { + fprintf(stderr, "No procfs support?!\n"); + return -EIO; + } + + while (fgets(buff, sizeof(buff), fp)) { + if (sscanf(buff, "prog_type:\t%u", &val) == 1) + prog->type = val; + else if (sscanf(buff, "prog_jited:\t%u", &val) == 1) + prog->jited = val; + } + + fclose(fp); + return 0; +} + +static int bpf_tail_call_get_aux(struct bpf_jited_aux *aux) +{ + struct bpf_elf_map tmp; + int ret; + + ret = bpf_derive_elf_map_from_fdinfo(aux->map_fd, &tmp, &aux->map); + if (!ret) + ret = bpf_derive_prog_from_fdinfo(aux->prog_fd, &aux->prog); + + return ret; +} + +static int bpf_fill_prog_arrays(struct bpf_elf_ctx *ctx) +{ + struct bpf_elf_sec_data data; + uint32_t map_id, key_id; + int fd, i, ret, idx; + + for (i = 1; i < ctx->elf_hdr.e_shnum; i++) { + if (ctx->sec_done[i]) + continue; + + ret = bpf_fill_section_data(ctx, i, &data); + if (ret < 0) + continue; + + ret = sscanf(data.sec_name, "%i/%i", &map_id, &key_id); + if (ret != 2) + continue; + + idx = bpf_find_map_by_id(ctx, map_id); + if (idx < 0) + continue; + + fd = bpf_fetch_prog_sec(ctx, data.sec_name); + if (fd < 0) + return -EIO; + + ret = bpf_map_update(ctx->map_fds[idx], &key_id, + &fd, BPF_ANY); + if (ret < 0) { + struct bpf_jited_aux aux = {}; + + ret = -errno; + if (errno == E2BIG) { + fprintf(stderr, "Tail call key %u for map %u out of bounds?\n", + key_id, map_id); + return ret; + } + + aux.map_fd = ctx->map_fds[idx]; + aux.prog_fd = fd; + + if (bpf_tail_call_get_aux(&aux)) + return ret; + if (!aux.map.owner.type) + return ret; + + if (aux.prog.type != aux.map.owner.type) + fprintf(stderr, "Tail call map owned by prog type %u, but prog type is %u!\n", + aux.map.owner.type, aux.prog.type); + if (aux.prog.jited != aux.map.owner.jited) + fprintf(stderr, "Tail call map %s jited, but prog %s!\n", + aux.map.owner.jited ? "is" : "not", + aux.prog.jited ? "is" : "not"); + return ret; + } + + ctx->sec_done[i] = true; + } + + return 0; +} + +static void bpf_save_finfo(struct bpf_elf_ctx *ctx) +{ + struct stat st; + int ret; + + memset(&ctx->stat, 0, sizeof(ctx->stat)); + + ret = fstat(ctx->obj_fd, &st); + if (ret < 0) { + fprintf(stderr, "Stat of elf file failed: %s\n", + strerror(errno)); + return; + } + + ctx->stat.st_dev = st.st_dev; + ctx->stat.st_ino = st.st_ino; +} + +static int bpf_read_pin_mapping(FILE *fp, uint32_t *id, char *path) +{ + char buff[PATH_MAX]; + + while (fgets(buff, sizeof(buff), fp)) { + char *ptr = buff; + + while (*ptr == ' ' || *ptr == '\t') + ptr++; + + if (*ptr == '#' || *ptr == '\n' || *ptr == 0) + continue; + + if (sscanf(ptr, "%i %s\n", id, path) != 2 && + sscanf(ptr, "%i %s #", id, path) != 2) { + strcpy(path, ptr); + return -1; + } + + return 1; + } + + return 0; +} + +static bool bpf_pinning_reserved(uint32_t pinning) +{ + switch (pinning) { + case PIN_NONE: + case PIN_OBJECT_NS: + case PIN_GLOBAL_NS: + return true; + default: + return false; + } +} + +static void bpf_hash_init(struct bpf_elf_ctx *ctx, const char *db_file) +{ + struct bpf_hash_entry *entry; + char subpath[PATH_MAX] = {}; + uint32_t pinning; + FILE *fp; + int ret; + + fp = fopen(db_file, "r"); + if (!fp) + return; + + while ((ret = bpf_read_pin_mapping(fp, &pinning, subpath))) { + if (ret == -1) { + fprintf(stderr, "Database %s is corrupted at: %s\n", + db_file, subpath); + fclose(fp); + return; + } + + if (bpf_pinning_reserved(pinning)) { + fprintf(stderr, "Database %s, id %u is reserved - ignoring!\n", + db_file, pinning); + continue; + } + + entry = malloc(sizeof(*entry)); + if (!entry) { + fprintf(stderr, "No memory left for db entry!\n"); + continue; + } + + entry->pinning = pinning; + entry->subpath = strdup(subpath); + if (!entry->subpath) { + fprintf(stderr, "No memory left for db entry!\n"); + free(entry); + continue; + } + + entry->next = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)]; + ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)] = entry; + } + + fclose(fp); +} + +static void bpf_hash_destroy(struct bpf_elf_ctx *ctx) +{ + struct bpf_hash_entry *entry; + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->ht); i++) { + while ((entry = ctx->ht[i]) != NULL) { + ctx->ht[i] = entry->next; + free((char *)entry->subpath); + free(entry); + } + } +} + +static int bpf_elf_check_ehdr(const struct bpf_elf_ctx *ctx) +{ + if (ctx->elf_hdr.e_type != ET_REL || + (ctx->elf_hdr.e_machine != EM_NONE && + ctx->elf_hdr.e_machine != EM_BPF) || + ctx->elf_hdr.e_version != EV_CURRENT) { + fprintf(stderr, "ELF format error, ELF file not for eBPF?\n"); + return -EINVAL; + } + + switch (ctx->elf_hdr.e_ident[EI_DATA]) { + default: + fprintf(stderr, "ELF format error, wrong endianness info?\n"); + return -EINVAL; + case ELFDATA2LSB: + if (htons(1) == 1) { + fprintf(stderr, + "We are big endian, eBPF object is little endian!\n"); + return -EIO; + } + break; + case ELFDATA2MSB: + if (htons(1) != 1) { + fprintf(stderr, + "We are little endian, eBPF object is big endian!\n"); + return -EIO; + } + break; + } + + return 0; +} + +static void bpf_get_cfg(struct bpf_elf_ctx *ctx) +{ + static const char *path_jit = "/proc/sys/net/core/bpf_jit_enable"; + int fd; + + fd = open(path_jit, O_RDONLY); + if (fd > 0) { + char tmp[16] = {}; + + if (read(fd, tmp, sizeof(tmp)) > 0) + ctx->cfg.jit_enabled = atoi(tmp); + close(fd); + } +} + +static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname, + enum bpf_prog_type type, __u32 ifindex, + bool verbose) +{ + uint8_t tmp[20]; + int ret; + + if (elf_version(EV_CURRENT) == EV_NONE) + return -EINVAL; + + bpf_init_env(); + + memset(ctx, 0, sizeof(*ctx)); + bpf_get_cfg(ctx); + + ret = bpf_obj_hash(pathname, tmp, sizeof(tmp)); + if (ret) + ctx->noafalg = true; + else + hexstring_n2a(tmp, sizeof(tmp), ctx->obj_uid, + sizeof(ctx->obj_uid)); + + ctx->verbose = verbose; + ctx->type = type; + ctx->ifindex = ifindex; + + ctx->obj_fd = open(pathname, O_RDONLY); + if (ctx->obj_fd < 0) + return ctx->obj_fd; + + ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL); + if (!ctx->elf_fd) { + ret = -EINVAL; + goto out_fd; + } + + if (elf_kind(ctx->elf_fd) != ELF_K_ELF) { + ret = -EINVAL; + goto out_fd; + } + + if (gelf_getehdr(ctx->elf_fd, &ctx->elf_hdr) != + &ctx->elf_hdr) { + ret = -EIO; + goto out_elf; + } + + ret = bpf_elf_check_ehdr(ctx); + if (ret < 0) + goto out_elf; + + ctx->sec_done = calloc(ctx->elf_hdr.e_shnum, + sizeof(*(ctx->sec_done))); + if (!ctx->sec_done) { + ret = -ENOMEM; + goto out_elf; + } + + if (ctx->verbose && bpf_log_realloc(ctx)) { + ret = -ENOMEM; + goto out_free; + } + + bpf_save_finfo(ctx); + bpf_hash_init(ctx, CONFDIR "/bpf_pinning"); + + return 0; +out_free: + free(ctx->sec_done); +out_elf: + elf_end(ctx->elf_fd); +out_fd: + close(ctx->obj_fd); + return ret; +} + +static int bpf_maps_count(struct bpf_elf_ctx *ctx) +{ + int i, count = 0; + + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) { + if (!ctx->map_fds[i]) + break; + count++; + } + + return count; +} + +static void bpf_maps_teardown(struct bpf_elf_ctx *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) { + if (ctx->map_fds[i]) + close(ctx->map_fds[i]); + } + + if (ctx->btf_fd) + close(ctx->btf_fd); + free(ctx->btf.types); +} + +static void bpf_elf_ctx_destroy(struct bpf_elf_ctx *ctx, bool failure) +{ + if (failure) + bpf_maps_teardown(ctx); + + bpf_hash_destroy(ctx); + + free(ctx->prog_text.insns); + free(ctx->sec_done); + free(ctx->log); + + elf_end(ctx->elf_fd); + close(ctx->obj_fd); +} + +static struct bpf_elf_ctx __ctx; + +static int bpf_obj_open(const char *pathname, enum bpf_prog_type type, + const char *section, __u32 ifindex, bool verbose) +{ + struct bpf_elf_ctx *ctx = &__ctx; + int fd = 0, ret; + + ret = bpf_elf_ctx_init(ctx, pathname, type, ifindex, verbose); + if (ret < 0) { + fprintf(stderr, "Cannot initialize ELF context!\n"); + return ret; + } + + ret = bpf_fetch_ancillary(ctx, strcmp(section, ".text")); + if (ret < 0) { + fprintf(stderr, "Error fetching ELF ancillary data!\n"); + goto out; + } + + fd = bpf_fetch_prog_sec(ctx, section); + if (fd < 0) { + fprintf(stderr, "Error fetching program/map!\n"); + ret = fd; + goto out; + } + + ret = bpf_fill_prog_arrays(ctx); + if (ret < 0) + fprintf(stderr, "Error filling program arrays!\n"); +out: + bpf_elf_ctx_destroy(ctx, ret < 0); + if (ret < 0) { + if (fd) + close(fd); + return ret; + } + + return fd; +} + +static int +bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len, + const struct bpf_map_data *aux, unsigned int entries) +{ + struct bpf_map_set_msg msg = { + .aux.uds_ver = BPF_SCM_AUX_VER, + .aux.num_ent = entries, + }; + int *cmsg_buf, min_fd; + char *amsg_buf; + int i; + + strlcpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name)); + memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st)); + + cmsg_buf = bpf_map_set_init(&msg, addr, addr_len); + amsg_buf = (char *)msg.aux.ent; + + for (i = 0; i < entries; i += min_fd) { + int ret; + + min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i); + bpf_map_set_init_single(&msg, min_fd); + + memcpy(cmsg_buf, &aux->fds[i], sizeof(aux->fds[0]) * min_fd); + memcpy(amsg_buf, &aux->ent[i], sizeof(aux->ent[0]) * min_fd); + + ret = sendmsg(fd, &msg.hdr, 0); + if (ret <= 0) + return ret ? : -1; + } + + return 0; +} + +static int +bpf_map_set_recv(int fd, int *fds, struct bpf_map_aux *aux, + unsigned int entries) +{ + struct bpf_map_set_msg msg; + int *cmsg_buf, min_fd; + char *amsg_buf, *mmsg_buf; + unsigned int needed = 1; + int i; + + cmsg_buf = bpf_map_set_init(&msg, NULL, 0); + amsg_buf = (char *)msg.aux.ent; + mmsg_buf = (char *)&msg.aux; + + for (i = 0; i < min(entries, needed); i += min_fd) { + struct cmsghdr *cmsg; + int ret; + + min_fd = min(entries, entries - i); + bpf_map_set_init_single(&msg, min_fd); + + ret = recvmsg(fd, &msg.hdr, 0); + if (ret <= 0) + return ret ? : -1; + + cmsg = CMSG_FIRSTHDR(&msg.hdr); + if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) + return -EINVAL; + if (msg.hdr.msg_flags & MSG_CTRUNC) + return -EIO; + if (msg.aux.uds_ver != BPF_SCM_AUX_VER) + return -ENOSYS; + + min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd); + if (min_fd > entries || min_fd <= 0) + return -EINVAL; + + memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd); + memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd); + memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent)); + + needed = aux->num_ent; + } + + return 0; +} + +int bpf_send_map_fds(const char *path, const char *obj) +{ + struct bpf_elf_ctx *ctx = &__ctx; + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + struct bpf_map_data bpf_aux = { + .fds = ctx->map_fds, + .ent = ctx->maps, + .st = &ctx->stat, + .obj = obj, + }; + int fd, ret; + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "Cannot open socket: %s\n", + strerror(errno)); + return -1; + } + + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); + + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + fprintf(stderr, "Cannot connect to %s: %s\n", + path, strerror(errno)); + return -1; + } + + ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux, + bpf_maps_count(ctx)); + if (ret < 0) + fprintf(stderr, "Cannot send fds to %s: %s\n", + path, strerror(errno)); + + bpf_maps_teardown(ctx); + close(fd); + return ret; +} + +int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux, + unsigned int entries) +{ + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + int fd, ret; + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "Cannot open socket: %s\n", + strerror(errno)); + return -1; + } + + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); + + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + fprintf(stderr, "Cannot bind to socket: %s\n", + strerror(errno)); + return -1; + } + + ret = bpf_map_set_recv(fd, fds, aux, entries); + if (ret < 0) + fprintf(stderr, "Cannot recv fds from %s: %s\n", + path, strerror(errno)); + + unlink(addr.sun_path); + close(fd); + return ret; +} +#endif /* HAVE_ELF */
diff --git a/lib/color.c b/lib/color.c new file mode 100644 index 0000000..5997684 --- /dev/null +++ b/lib/color.c
@@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <linux/if.h> + +#include "color.h" +#include "utils.h" + +static void set_color_palette(void); + +enum color { + C_RED, + C_GREEN, + C_YELLOW, + C_BLUE, + C_MAGENTA, + C_CYAN, + C_WHITE, + C_BOLD_RED, + C_BOLD_GREEN, + C_BOLD_YELLOW, + C_BOLD_BLUE, + C_BOLD_MAGENTA, + C_BOLD_CYAN, + C_BOLD_WHITE, + C_CLEAR +}; + +static const char * const color_codes[] = { + "\e[31m", + "\e[32m", + "\e[33m", + "\e[34m", + "\e[35m", + "\e[36m", + "\e[37m", + "\e[1;31m", + "\e[1;32m", + "\e[1;33m", + "\e[1;34m", + "\e[1;35m", + "\e[1;36m", + "\e[1;37m", + "\e[0m", + NULL, +}; + +/* light background */ +static enum color attr_colors_light[] = { + C_CYAN, + C_YELLOW, + C_MAGENTA, + C_BLUE, + C_GREEN, + C_RED, + C_CLEAR, +}; + +/* dark background */ +static enum color attr_colors_dark[] = { + C_BOLD_CYAN, + C_BOLD_YELLOW, + C_BOLD_MAGENTA, + C_BOLD_BLUE, + C_BOLD_GREEN, + C_BOLD_RED, + C_CLEAR +}; + +static int is_dark_bg; +static int color_is_enabled; + +static void enable_color(void) +{ + color_is_enabled = 1; + set_color_palette(); +} + +bool check_enable_color(int color, int json) +{ + if (json || color == COLOR_OPT_NEVER) + return false; + + if (color == COLOR_OPT_ALWAYS || isatty(fileno(stdout))) { + enable_color(); + return true; + } + return false; +} + +bool matches_color(const char *arg, int *val) +{ + char *dup, *p; + + if (!val) + return false; + + dup = strdupa(arg); + p = strchrnul(dup, '='); + if (*p) + *(p++) = '\0'; + + if (matches(dup, "-color")) + return false; + + if (*p == '\0' || !strcmp(p, "always")) + *val = COLOR_OPT_ALWAYS; + else if (!strcmp(p, "auto")) + *val = COLOR_OPT_AUTO; + else if (!strcmp(p, "never")) + *val = COLOR_OPT_NEVER; + else + return false; + return true; +} + +static void set_color_palette(void) +{ + char *p = getenv("COLORFGBG"); + + /* + * COLORFGBG environment variable usually contains either two or three + * values separated by semicolons; we want the last value in either case. + * If this value is 0-6 or 8, background is dark. + */ + if (p && (p = strrchr(p, ';')) != NULL + && ((p[1] >= '0' && p[1] <= '6') || p[1] == '8') + && p[2] == '\0') + is_dark_bg = 1; +} + +__attribute__((format(printf, 3, 4))) +int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...) +{ + int ret = 0; + va_list args; + + va_start(args, fmt); + + if (!color_is_enabled || attr == COLOR_NONE) { + ret = vfprintf(fp, fmt, args); + goto end; + } + + ret += fprintf(fp, "%s", color_codes[is_dark_bg ? + attr_colors_dark[attr] : attr_colors_light[attr]]); + + ret += vfprintf(fp, fmt, args); + ret += fprintf(fp, "%s", color_codes[C_CLEAR]); + +end: + va_end(args); + return ret; +} + +enum color_attr ifa_family_color(__u8 ifa_family) +{ + switch (ifa_family) { + case AF_INET: + return COLOR_INET; + case AF_INET6: + return COLOR_INET6; + default: + return COLOR_NONE; + } +} + +enum color_attr oper_state_color(__u8 state) +{ + switch (state) { + case IF_OPER_UP: + return COLOR_OPERSTATE_UP; + case IF_OPER_DOWN: + return COLOR_OPERSTATE_DOWN; + default: + return COLOR_NONE; + } +}
diff --git a/lib/coverity_model.c b/lib/coverity_model.c new file mode 100644 index 0000000..1321fe8 --- /dev/null +++ b/lib/coverity_model.c
@@ -0,0 +1,17 @@ +/* + * Coverity Scan model + * + * This is a modeling file for Coverity Scan. Modeling helps to avoid false + * positives. + * + * - A model file can't import any header files. + * - Therefore only some built-in primitives like int, char and void are + * available but not wchar_t, NULL etc. + * - Modeling doesn't need full structs and typedefs. Rudimentary structs + * and similar types are sufficient. + * - An uninitialized local pointer is not an error. It signifies that the + * variable could be either NULL or have some data. + * + * Coverity Scan doesn't pick up modifications automatically. The model file + * must be uploaded by an admin. + */
diff --git a/lib/exec.c b/lib/exec.c new file mode 100644 index 0000000..9b1c8f4 --- /dev/null +++ b/lib/exec.c
@@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <sys/wait.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> + +#include "utils.h" +#include "namespace.h" + +int cmd_exec(const char *cmd, char **argv, bool do_fork, + int (*setup)(void *), void *arg) +{ + fflush(stdout); + if (do_fork) { + int status; + pid_t pid; + + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid != 0) { + /* Parent */ + if (waitpid(pid, &status, 0) < 0) { + perror("waitpid"); + exit(1); + } + + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + exit(1); + } + } + + if (setup && setup(arg)) + return -1; + + if (execvp(cmd, argv) < 0) + fprintf(stderr, "exec of \"%s\" failed: %s\n", + cmd, strerror(errno)); + _exit(1); +}
diff --git a/lib/fs.c b/lib/fs.c new file mode 100644 index 0000000..86efd4e --- /dev/null +++ b/lib/fs.c
@@ -0,0 +1,182 @@ +/* + * fs.c filesystem APIs + * + * 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 <dsa@cumulusnetworks.com> + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#include "utils.h" + +#define CGROUP2_FS_NAME "cgroup2" + +/* if not already mounted cgroup2 is mounted here for iproute2's use */ +#define MNT_CGRP2_PATH "/var/run/cgroup2" + +/* return mount path of first occurrence of given fstype */ +static char *find_fs_mount(const char *fs_to_find) +{ + char path[4096]; + char fstype[128]; /* max length of any filesystem name */ + char *mnt = NULL; + FILE *fp; + + fp = fopen("/proc/mounts", "r"); + if (!fp) { + fprintf(stderr, + "Failed to open mounts file: %s\n", strerror(errno)); + return NULL; + } + + while (fscanf(fp, "%*s %4095s %127s %*s %*d %*d\n", + path, fstype) == 2) { + if (strcmp(fstype, fs_to_find) == 0) { + mnt = strdup(path); + break; + } + } + + fclose(fp); + + return mnt; +} + +/* caller needs to free string returned */ +char *find_cgroup2_mount(void) +{ + char *mnt = find_fs_mount(CGROUP2_FS_NAME); + + if (mnt) + return mnt; + + mnt = strdup(MNT_CGRP2_PATH); + if (!mnt) { + fprintf(stderr, "Failed to allocate memory for cgroup2 path\n"); + return NULL; + + } + + if (make_path(mnt, 0755)) { + fprintf(stderr, "Failed to setup vrf cgroup2 directory\n"); + free(mnt); + return NULL; + } + + if (mount("none", mnt, CGROUP2_FS_NAME, 0, NULL)) { + /* EBUSY means already mounted */ + if (errno == EBUSY) + goto out; + + if (errno == ENODEV) { + fprintf(stderr, + "Failed to mount cgroup2. Are CGROUPS enabled in your kernel?\n"); + } else { + fprintf(stderr, + "Failed to mount cgroup2: %s\n", + strerror(errno)); + } + free(mnt); + return NULL; + } +out: + return mnt; +} + +int make_path(const char *path, mode_t mode) +{ + char *dir, *delim; + int rc = -1; + + delim = dir = strdup(path); + if (dir == NULL) { + fprintf(stderr, "strdup failed copying path"); + return -1; + } + + /* skip '/' -- it had better exist */ + if (*delim == '/') + delim++; + + while (1) { + delim = strchr(delim, '/'); + if (delim) + *delim = '\0'; + + rc = mkdir(dir, mode); + if (mkdir(dir, mode) != 0 && errno != EEXIST) { + fprintf(stderr, "mkdir failed for %s: %s\n", + dir, strerror(errno)); + goto out; + } + + if (delim == NULL) + break; + + *delim = '/'; + delim++; + if (*delim == '\0') + break; + } + rc = 0; +out: + free(dir); + + return rc; +} + +int get_command_name(const char *pid, char *comm, size_t len) +{ + char path[PATH_MAX]; + char line[128]; + FILE *fp; + + if (snprintf(path, sizeof(path), + "/proc/%s/status", pid) >= sizeof(path)) { + return -1; + } + + fp = fopen(path, "r"); + if (!fp) + return -1; + + comm[0] = '\0'; + while (fgets(line, sizeof(line), fp)) { + char *nl, *name; + + name = strstr(line, "Name:"); + if (!name) + continue; + + name += 5; + while (isspace(*name)) + name++; + + nl = strchr(name, '\n'); + if (nl) + *nl = '\0'; + + strlcpy(comm, name, len); + break; + } + + fclose(fp); + + return 0; +}
diff --git a/lib/inet_proto.c b/lib/inet_proto.c new file mode 100644 index 0000000..41e2e8b --- /dev/null +++ b/lib/inet_proto.c
@@ -0,0 +1,69 @@ +/* + * inet_proto.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> + +#include "rt_names.h" +#include "utils.h" + +const char *inet_proto_n2a(int proto, char *buf, int len) +{ + static char *ncache; + static int icache = -1; + struct protoent *pe; + + if (proto == icache) + return ncache; + + pe = getprotobynumber(proto); + if (pe && !numeric) { + if (icache != -1) + free(ncache); + icache = proto; + ncache = strdup(pe->p_name); + strlcpy(buf, pe->p_name, len); + return buf; + } + snprintf(buf, len, "ipproto-%d", proto); + return buf; +} + +int inet_proto_a2n(const char *buf) +{ + static char *ncache; + static int icache = -1; + struct protoent *pe; + __u8 ret; + + if (icache != -1 && strcmp(ncache, buf) == 0) + return icache; + + if (!get_u8(&ret, buf, 10)) + return ret; + + pe = getprotobyname(buf); + if (pe) { + if (icache != -1) + free(ncache); + icache = pe->p_proto; + ncache = strdup(pe->p_name); + return pe->p_proto; + } + return -1; +}
diff --git a/lib/json_print.c b/lib/json_print.c new file mode 100644 index 0000000..43ea69b --- /dev/null +++ b/lib/json_print.c
@@ -0,0 +1,232 @@ +/* + * json_print.c "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, <julien@cumulusnetworks.com> + */ + +#include <stdarg.h> +#include <stdio.h> + +#include "utils.h" +#include "json_print.h" + +static json_writer_t *_jw; + +#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw) +#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY)) + +void new_json_obj(int json) +{ + if (json) { + _jw = jsonw_new(stdout); + if (!_jw) { + perror("json object"); + exit(1); + } + if (pretty) + jsonw_pretty(_jw, true); + jsonw_start_array(_jw); + } +} + +void delete_json_obj(void) +{ + if (_jw) { + jsonw_end_array(_jw); + jsonw_destroy(&_jw); + } +} + +bool is_json_context(void) +{ + return _jw != NULL; +} + +json_writer_t *get_json_writer(void) +{ + return _jw; +} + +void open_json_object(const char *str) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_object(_jw); + } +} + +void close_json_object(void) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) + jsonw_end_object(_jw); +} + +/* + * Start json array or string array using + * the provided string as json key (if not null) + * or as array delimiter in non-json context. + */ +void open_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_array(_jw); + } else if (_IS_FP_CONTEXT(type)) { + printf("%s", str); + } +} + +/* + * End json array or string array + */ +void close_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) { + jsonw_end_array(_jw); + } else if (_IS_FP_CONTEXT(type)) { + printf("%s", str); + } +} + +/* + * pre-processor directive to generate similar + * functions handling different types + */ +#define _PRINT_FUNC(type_name, type) \ + __attribute__((format(printf, 4, 0))) \ + void print_color_##type_name(enum output_type t, \ + enum color_attr color, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + if (_IS_JSON_CONTEXT(t)) { \ + if (!key) \ + jsonw_##type_name(_jw, value); \ + else \ + jsonw_##type_name##_field(_jw, key, value); \ + } else if (_IS_FP_CONTEXT(t)) { \ + color_fprintf(stdout, color, fmt, value); \ + } \ + } +_PRINT_FUNC(int, int); +_PRINT_FUNC(s64, int64_t); +_PRINT_FUNC(hhu, unsigned char); +_PRINT_FUNC(hu, unsigned short); +_PRINT_FUNC(uint, unsigned int); +_PRINT_FUNC(u64, uint64_t); +_PRINT_FUNC(luint, unsigned long); +_PRINT_FUNC(lluint, unsigned long long); +_PRINT_FUNC(float, double); +#undef _PRINT_FUNC + +void print_color_string(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key && !value) + jsonw_name(_jw, key); + else if (!key && value) + jsonw_string(_jw, value); + else + jsonw_string_field(_jw, key, value); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(stdout, color, fmt, value); + } +} + +/* + * value's type is bool. When using this function in FP context you can't pass + * a value to it, you will need to use "is_json_context()" to have different + * branch for json and regular output. grep -r "print_bool" for example + */ +void print_color_bool(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + bool value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_bool_field(_jw, key, value); + else + jsonw_bool(_jw, value); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(stdout, color, fmt, value ? "true" : "false"); + } +} + +/* + * In JSON context uses hardcode %#x format: 42 -> 0x2a + */ +void print_color_0xhex(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + unsigned long long hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%#llx", hex); + print_string(PRINT_JSON, key, NULL, b1); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(stdout, color, fmt, hex); + } +} + +void print_color_hex(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + unsigned int hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%x", hex); + if (key) + jsonw_string_field(_jw, key, b1); + else + jsonw_string(_jw, b1); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(stdout, color, fmt, hex); + } +} + +/* + * In JSON context we don't use the argument "value" we simply call jsonw_null + * whereas FP context can use "value" to output anything + */ +void print_color_null(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_null_field(_jw, key); + else + jsonw_null(_jw); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(stdout, color, fmt, value); + } +} + +/* Print line separator (if not in JSON mode) */ +void print_nl(void) +{ + if (!_jw) + printf("%s", _SL_); +}
diff --git a/lib/json_writer.c b/lib/json_writer.c new file mode 100644 index 0000000..88c5eb8 --- /dev/null +++ b/lib/json_writer.c
@@ -0,0 +1,389 @@ +/* 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 <stephen@networkplumber.org> + */ + +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <assert.h> +#include <malloc.h> +#include <inttypes.h> +#include <stdint.h> + +#include "json_writer.h" + +struct json_writer { + FILE *out; /* output file */ + unsigned depth; /* nesting */ + bool pretty; /* optional whitepace */ + char sep; /* either nul or comma */ +}; + +/* indentation for pretty print */ +static void jsonw_indent(json_writer_t *self) +{ + unsigned i; + for (i = 0; i < self->depth; ++i) + fputs(" ", self->out); +} + +/* end current line and indent if pretty printing */ +static void jsonw_eol(json_writer_t *self) +{ + if (!self->pretty) + return; + + putc('\n', self->out); + jsonw_indent(self); +} + +/* If current object is not empty print a comma */ +static void jsonw_eor(json_writer_t *self) +{ + if (self->sep != '\0') + putc(self->sep, self->out); + self->sep = ','; +} + + +/* Output JSON encoded string */ +/* Handles C escapes, does not do Unicode */ +static void jsonw_puts(json_writer_t *self, const char *str) +{ + putc('"', self->out); + for (; *str; ++str) + switch (*str) { + case '\t': + fputs("\\t", self->out); + break; + case '\n': + fputs("\\n", self->out); + break; + case '\r': + fputs("\\r", self->out); + break; + case '\f': + fputs("\\f", self->out); + break; + case '\b': + fputs("\\b", self->out); + break; + case '\\': + fputs("\\\\", self->out); + break; + case '"': + fputs("\\\"", self->out); + break; + case '\'': + fputs("\\\'", self->out); + break; + default: + putc(*str, self->out); + } + putc('"', self->out); +} + +/* Create a new JSON stream */ +json_writer_t *jsonw_new(FILE *f) +{ + json_writer_t *self = malloc(sizeof(*self)); + if (self) { + self->out = f; + self->depth = 0; + self->pretty = false; + self->sep = '\0'; + } + return self; +} + +/* End output to JSON stream */ +void jsonw_destroy(json_writer_t **self_p) +{ + json_writer_t *self = *self_p; + + assert(self->depth == 0); + fputs("\n", self->out); + fflush(self->out); + free(self); + *self_p = NULL; +} + +void jsonw_pretty(json_writer_t *self, bool on) +{ + self->pretty = on; +} + +/* Basic blocks */ +static void jsonw_begin(json_writer_t *self, int c) +{ + jsonw_eor(self); + putc(c, self->out); + ++self->depth; + self->sep = '\0'; +} + +static void jsonw_end(json_writer_t *self, int c) +{ + assert(self->depth > 0); + + --self->depth; + if (self->sep != '\0') + jsonw_eol(self); + putc(c, self->out); + self->sep = ','; +} + + +/* Add a JSON property name */ +void jsonw_name(json_writer_t *self, const char *name) +{ + jsonw_eor(self); + jsonw_eol(self); + self->sep = '\0'; + jsonw_puts(self, name); + putc(':', self->out); + if (self->pretty) + putc(' ', self->out); +} + +__attribute__((format(printf, 2, 3))) +void jsonw_printf(json_writer_t *self, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + jsonw_eor(self); + vfprintf(self->out, fmt, ap); + va_end(ap); +} + +/* Collections */ +void jsonw_start_object(json_writer_t *self) +{ + jsonw_begin(self, '{'); +} + +void jsonw_end_object(json_writer_t *self) +{ + jsonw_end(self, '}'); +} + +void jsonw_start_array(json_writer_t *self) +{ + jsonw_begin(self, '['); + if (self->pretty) + putc(' ', self->out); +} + +void jsonw_end_array(json_writer_t *self) +{ + if (self->pretty && self->sep) + putc(' ', self->out); + self->sep = '\0'; + jsonw_end(self, ']'); +} + +/* JSON value types */ +void jsonw_string(json_writer_t *self, const char *value) +{ + jsonw_eor(self); + jsonw_puts(self, value); +} + +void jsonw_bool(json_writer_t *self, bool val) +{ + jsonw_printf(self, "%s", val ? "true" : "false"); +} + +void jsonw_null(json_writer_t *self) +{ + jsonw_printf(self, "null"); +} + +void jsonw_float(json_writer_t *self, double num) +{ + jsonw_printf(self, "%g", num); +} + +void jsonw_hhu(json_writer_t *self, unsigned char num) +{ + jsonw_printf(self, "%hhu", num); +} + +void jsonw_hu(json_writer_t *self, unsigned short num) +{ + jsonw_printf(self, "%hu", num); +} + +void jsonw_uint(json_writer_t *self, unsigned int num) +{ + jsonw_printf(self, "%u", num); +} + +void jsonw_u64(json_writer_t *self, uint64_t num) +{ + jsonw_printf(self, "%"PRIu64, num); +} + +void jsonw_xint(json_writer_t *self, uint64_t num) +{ + jsonw_printf(self, "%"PRIx64, num); +} + +void jsonw_luint(json_writer_t *self, unsigned long num) +{ + jsonw_printf(self, "%lu", num); +} + +void jsonw_lluint(json_writer_t *self, unsigned long long num) +{ + jsonw_printf(self, "%llu", num); +} + +void jsonw_int(json_writer_t *self, int num) +{ + jsonw_printf(self, "%d", num); +} + +void jsonw_s64(json_writer_t *self, int64_t num) +{ + jsonw_printf(self, "%"PRId64, num); +} + +/* Basic name/value objects */ +void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) +{ + jsonw_name(self, prop); + jsonw_string(self, val); +} + +void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) +{ + jsonw_name(self, prop); + jsonw_bool(self, val); +} + +void jsonw_float_field(json_writer_t *self, const char *prop, double val) +{ + jsonw_name(self, prop); + jsonw_float(self, val); +} + +void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num) +{ + jsonw_name(self, prop); + jsonw_uint(self, num); +} + +void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num) +{ + jsonw_name(self, prop); + jsonw_u64(self, num); +} + +void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num) +{ + jsonw_name(self, prop); + jsonw_xint(self, num); +} + +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num) +{ + jsonw_name(self, prop); + jsonw_hhu(self, num); +} + +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) +{ + jsonw_name(self, prop); + jsonw_hu(self, num); +} + +void jsonw_luint_field(json_writer_t *self, + const char *prop, + unsigned long num) +{ + jsonw_name(self, prop); + jsonw_luint(self, num); +} + +void jsonw_lluint_field(json_writer_t *self, + const char *prop, + unsigned long long num) +{ + jsonw_name(self, prop); + jsonw_lluint(self, num); +} + +void jsonw_int_field(json_writer_t *self, const char *prop, int num) +{ + jsonw_name(self, prop); + jsonw_int(self, num); +} + +void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num) +{ + jsonw_name(self, prop); + jsonw_s64(self, num); +} + +void jsonw_null_field(json_writer_t *self, const char *prop) +{ + jsonw_name(self, prop); + jsonw_null(self); +} + +#ifdef TEST +int main(int argc, char **argv) +{ + json_writer_t *wr = jsonw_new(stdout); + + jsonw_start_object(wr); + jsonw_pretty(wr, true); + jsonw_name(wr, "Vyatta"); + jsonw_start_object(wr); + jsonw_string_field(wr, "url", "http://vyatta.com"); + jsonw_uint_field(wr, "downloads", 2000000ul); + jsonw_float_field(wr, "stock", 8.16); + + jsonw_name(wr, "ARGV"); + jsonw_start_array(wr); + while (--argc) + jsonw_string(wr, *++argv); + jsonw_end_array(wr); + + jsonw_name(wr, "empty"); + jsonw_start_array(wr); + jsonw_end_array(wr); + + jsonw_name(wr, "NIL"); + jsonw_start_object(wr); + jsonw_end_object(wr); + + jsonw_null_field(wr, "my_null"); + + jsonw_name(wr, "special chars"); + jsonw_start_array(wr); + jsonw_string_field(wr, "slash", "/"); + jsonw_string_field(wr, "newline", "\n"); + jsonw_string_field(wr, "tab", "\t"); + jsonw_string_field(wr, "ff", "\f"); + jsonw_string_field(wr, "quote", "\""); + jsonw_string_field(wr, "tick", "\'"); + jsonw_string_field(wr, "backslash", "\\"); + jsonw_end_array(wr); + + jsonw_end_object(wr); + + jsonw_end_object(wr); + jsonw_destroy(&wr); + return 0; +} + +#endif
diff --git a/lib/libgenl.c b/lib/libgenl.c new file mode 100644 index 0000000..f2ce698 --- /dev/null +++ b/lib/libgenl.c
@@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * libgenl.c GENL library + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/genetlink.h> +#include "libgenl.h" + +static int genl_parse_getfamily(struct nlmsghdr *nlh) +{ + struct rtattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *ghdr = NLMSG_DATA(nlh); + int len = nlh->nlmsg_len; + struct rtattr *attrs; + + if (nlh->nlmsg_type != GENL_ID_CTRL) { + fprintf(stderr, "Not a controller message, nlmsg_len=%d " + "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type); + return -1; + } + + len -= NLMSG_LENGTH(GENL_HDRLEN); + + if (len < 0) { + fprintf(stderr, "wrong controller message len %d\n", len); + return -1; + } + + if (ghdr->cmd != CTRL_CMD_NEWFAMILY) { + fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd); + return -1; + } + + attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); + parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len); + + if (tb[CTRL_ATTR_FAMILY_ID] == NULL) { + fprintf(stderr, "Missing family id TLV\n"); + return -1; + } + + return rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]); +} + +int genl_resolve_family(struct rtnl_handle *grth, const char *family) +{ + GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, + NLM_F_REQUEST); + struct nlmsghdr *answer; + int fnum; + + addattr_l(&req.n, sizeof(req), CTRL_ATTR_FAMILY_NAME, + family, strlen(family) + 1); + + if (rtnl_talk(grth, &req.n, &answer) < 0) { + fprintf(stderr, "Error talking to the kernel\n"); + return -2; + } + + fnum = genl_parse_getfamily(answer); + free(answer); + + return fnum; +} + +int genl_init_handle(struct rtnl_handle *grth, const char *family, + int *genl_family) +{ + if (*genl_family >= 0) + return 0; + + if (rtnl_open_byproto(grth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + return -1; + } + + *genl_family = genl_resolve_family(grth, family); + if (*genl_family < 0) + return -1; + + return 0; +}
diff --git a/lib/libnetlink.c b/lib/libnetlink.c new file mode 100644 index 0000000..e02d629 --- /dev/null +++ b/lib/libnetlink.c
@@ -0,0 +1,1442 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/uio.h> +#include <linux/fib_rules.h> +#include <linux/if_addrlabel.h> +#include <linux/if_bridge.h> +#include <linux/nexthop.h> + +#include "libnetlink.h" + +#define __aligned(x) __attribute__((aligned(x))) + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +int rcvbuf = 1024 * 1024; + +#ifdef HAVE_LIBMNL +#include <libmnl/libmnl.h> + +static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = { + [NLMSGERR_ATTR_MSG] = MNL_TYPE_NUL_STRING, + [NLMSGERR_ATTR_OFFS] = MNL_TYPE_U32, +}; + +static int err_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + uint16_t type; + + if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) { + fprintf(stderr, "Invalid extack attribute\n"); + return MNL_CB_ERROR; + } + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, extack_policy[type]) < 0) { + fprintf(stderr, "extack attribute %d failed validation\n", + type); + return MNL_CB_ERROR; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_ext_ack_msg(bool is_err, const char *msg) +{ + fprintf(stderr, "%s: %s", is_err ? "Error" : "Warning", msg); + if (msg[strlen(msg) - 1] != '.') + fprintf(stderr, "."); + fprintf(stderr, "\n"); +} + +/* dump netlink extended ack error message */ +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) +{ + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + const struct nlmsghdr *err_nlh = NULL; + unsigned int hlen = sizeof(*err); + const char *msg = NULL; + uint32_t off = 0; + + /* no TLVs, nothing to do here */ + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return 0; + + /* if NLM_F_CAPPED is set then the inner err msg was capped */ + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + hlen += mnl_nlmsg_get_payload_len(&err->msg); + + if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK) + return 0; + + if (tb[NLMSGERR_ATTR_MSG]) + msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]); + + if (tb[NLMSGERR_ATTR_OFFS]) { + off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]); + + if (off > nlh->nlmsg_len) { + fprintf(stderr, + "Invalid offset for NLMSGERR_ATTR_OFFS\n"); + off = 0; + } else if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + err_nlh = &err->msg; + } + + if (errfn) + return errfn(msg, off, err_nlh); + + if (msg && *msg != '\0') { + bool is_err = !!err->error; + + print_ext_ack_msg(is_err, msg); + return is_err ? 1 : 0; + } + + return 0; +} + +int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error) +{ + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; + unsigned int hlen = sizeof(int); + const char *msg = NULL; + + if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK) + return 0; + + if (tb[NLMSGERR_ATTR_MSG]) + msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]); + + if (msg && *msg != '\0') { + bool is_err = !!error; + + print_ext_ack_msg(is_err, msg); + return is_err ? 1 : 0; + } + + return 0; +} +#else +#warning "libmnl required for error support" + +/* No extended error ack without libmnl */ +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) +{ + return 0; +} + +int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error) +{ + return 0; +} +#endif + +/* Older kernels may not support strict dump and filtering */ +void rtnl_set_strict_dump(struct rtnl_handle *rth) +{ + int one = 1; + + if (setsockopt(rth->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, + &one, sizeof(one)) < 0) + return; + + rth->flags |= RTNL_HANDLE_F_STRICT_CHK; +} + +int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group) +{ + return setsockopt(rth->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); +} + +void rtnl_close(struct rtnl_handle *rth) +{ + if (rth->fd >= 0) { + close(rth->fd); + rth->fd = -1; + } +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, + int protocol) +{ + socklen_t addr_len; + int sndbuf = 32768; + int one = 1; + + memset(rth, 0, sizeof(*rth)); + + rth->proto = protocol; + rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, + &sndbuf, sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + /* Older kernels may no support extended ACK reporting */ + setsockopt(rth->fd, SOL_NETLINK, NETLINK_EXT_ACK, + &one, sizeof(one)); + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr *)&rth->local, + sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr *)&rth->local, + &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", + rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct nhmsg nhm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .nlh.nlmsg_type = RTM_GETNEXTHOP, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .nhm.nh_family = family, + }; + + if (filter_fn) { + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + } + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_addrdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct ifaddrmsg ifm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .nlh.nlmsg_type = RTM_GETADDR, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifa_family = family, + }; + + if (filter_fn) { + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + } + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct ifaddrlblmsg ifal; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)), + .nlh.nlmsg_type = RTM_GETADDRLABEL, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifal.ifal_family = family, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_routedump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .nlh.nlmsg_type = RTM_GETROUTE, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .rtm.rtm_family = family, + }; + + if (filter_fn) { + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + } + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_ruledump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct fib_rule_hdr frh; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)), + .nlh.nlmsg_type = RTM_GETRULE, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .frh.family = family + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_neighdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct ndmsg ndm; + char buf[256]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)), + .nlh.nlmsg_type = RTM_GETNEIGH, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ndm.ndm_family = family, + }; + + if (filter_fn) { + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + } + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_neightbldump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct ndtmsg ndtmsg; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)), + .nlh.nlmsg_type = RTM_GETNEIGHTBL, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ndtmsg.ndtm_family = family, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_mdbdump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct br_port_msg bpm; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)), + .nlh.nlmsg_type = RTM_GETMDB, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .bpm.family = family, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_netconfdump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct netconfmsg ncm; + char buf[0] __aligned(NLMSG_ALIGNTO); + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct netconfmsg))), + .nlh.nlmsg_type = RTM_GETNETCONF, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ncm.ncm_family = family, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg rtm; + char buf[1024]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtgenmsg))), + .nlh.nlmsg_type = RTM_GETNSID, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .rtm.rtgen_family = family, + }; + int err; + + if (!filter_fn) + return -EINVAL; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + + return send(rth->fd, &req, req.nlh.nlmsg_len, 0); +} + +static int __rtnl_linkdump_req(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlh.nlmsg_type = RTM_GETLINK, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = family, + }; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_linkdump_req(struct rtnl_handle *rth, int family) +{ + if (family == AF_UNSPEC) + return rtnl_linkdump_req_filter(rth, family, RTEXT_FILTER_VF); + + return __rtnl_linkdump_req(rth, family); +} + +int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int family, + __u32 filt_mask) +{ + if (family == AF_UNSPEC || family == AF_BRIDGE) { + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + /* attribute has to be NLMSG aligned */ + struct rtattr ext_req __aligned(NLMSG_ALIGNTO); + __u32 ext_filter_mask; + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_type = RTM_GETLINK, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = family, + .ext_req.rta_type = IFLA_EXT_MASK, + .ext_req.rta_len = RTA_LENGTH(sizeof(__u32)), + .ext_filter_mask = filt_mask, + }; + + return send(rth->fd, &req, sizeof(req), 0); + } + + return __rtnl_linkdump_req(rth, family); +} + +int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + if (family == AF_UNSPEC || family == AF_PACKET) { + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + char buf[1024]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlh.nlmsg_type = RTM_GETLINK, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = family, + }; + int err; + + if (!filter_fn) + return -EINVAL; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + + return send(rth->fd, &req, req.nlh.nlmsg_len, 0); + } + + return __rtnl_linkdump_req(rth, family); +} + +int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct ifinfomsg ifm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlh.nlmsg_type = RTM_GETNEIGH, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .ifm.ifi_family = PF_BRIDGE, + }; + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask) +{ + struct { + struct nlmsghdr nlh; + struct if_stats_msg ifsm; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)); + req.nlh.nlmsg_type = RTM_GETSTATS; + req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.ifsm.family = fam; + req.ifsm.filter_mask = filt_mask; + + return send(rth->fd, &req, sizeof(req), 0); +} + +int rtnl_send(struct rtnl_handle *rth, const void *buf, int len) +{ + return send(rth->fd, buf, len, 0); +} + +int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len) +{ + struct nlmsghdr *h; + int status; + char resp[1024]; + + status = send(rth->fd, buf, len, 0); + if (status < 0) + return status; + + /* Check for immediate errors */ + status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK); + if (status < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + + for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status); + h = NLMSG_NEXT(h, status)) { + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + fprintf(stderr, "ERROR truncated\n"); + else + errno = -err->error; + return -1; + } + } + + return 0; +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh = { + .nlmsg_len = NLMSG_LENGTH(len), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlmsg_seq = rth->dump = ++rth->seq, + }; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof(nlh) }, + { .iov_base = req, .iov_len = len } + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = 2, + }; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) +{ + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov = { + .iov_base = n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + n->nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; + n->nlmsg_pid = 0; + n->nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +static int rtnl_dump_done(struct nlmsghdr *h) +{ + int len = *(int *)NLMSG_DATA(h); + + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(int))) { + fprintf(stderr, "DONE truncated\n"); + return -1; + } + + if (len < 0) { + /* check for any messages returned from kernel */ + if (nl_dump_ext_ack_done(h, len)) + return len; + + errno = -len; + switch (errno) { + case ENOENT: + case EOPNOTSUPP: + return -1; + case EMSGSIZE: + fprintf(stderr, + "Error: Buffer too small for object.\n"); + break; + default: + perror("RTNETLINK answers"); + } + return len; + } + + /* check for any messages returned from kernel */ + nl_dump_ext_ack(h, NULL); + + return 0; +} + +static void rtnl_dump_error(const struct rtnl_handle *rth, + struct nlmsghdr *h) +{ + + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + const struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + + errno = -err->error; + if (rth->proto == NETLINK_SOCK_DIAG && + (errno == ENOENT || + errno == EOPNOTSUPP)) + return; + + if (!(rth->flags & RTNL_HANDLE_F_SUPPRESS_NLERR)) + perror("RTNETLINK answers"); + } +} + +static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags) +{ + int len; + + do { + len = recvmsg(fd, msg, flags); + } while (len < 0 && (errno == EINTR || errno == EAGAIN)); + + if (len < 0) { + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + return -errno; + } + + if (len == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -ENODATA; + } + + return len; +} + +static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer) +{ + struct iovec *iov = msg->msg_iov; + char *buf; + int len; + + iov->iov_base = NULL; + iov->iov_len = 0; + + len = __rtnl_recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC); + if (len < 0) + return len; + + if (len < 32768) + len = 32768; + buf = malloc(len); + if (!buf) { + fprintf(stderr, "malloc error: not enough buffer\n"); + return -ENOMEM; + } + + iov->iov_base = buf; + iov->iov_len = len; + + len = __rtnl_recvmsg(fd, msg, 0); + if (len < 0) { + free(buf); + return len; + } + + if (answer) + *answer = buf; + else + free(buf); + + return len; +} + +static int rtnl_dump_filter_l(struct rtnl_handle *rth, + const struct rtnl_dump_filter_arg *arg) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char *buf; + int dump_intr = 0; + + while (1) { + int status; + const struct rtnl_dump_filter_arg *a; + int found_done = 0; + int msglen = 0; + + status = rtnl_recvmsg(rth->fd, &msg, &buf); + if (status < 0) + return status; + + if (rth->dump_fp) + fwrite(buf, 1, NLMSG_ALIGN(status), rth->dump_fp); + + for (a = arg; a->filter; a++) { + struct nlmsghdr *h = (struct nlmsghdr *)buf; + + msglen = status; + + while (NLMSG_OK(h, msglen)) { + int err = 0; + + h->nlmsg_flags &= ~a->nc_flags; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) + goto skip_it; + + if (h->nlmsg_flags & NLM_F_DUMP_INTR) + dump_intr = 1; + + if (h->nlmsg_type == NLMSG_DONE) { + err = rtnl_dump_done(h); + if (err < 0) { + free(buf); + return -1; + } + + found_done = 1; + break; /* process next filter */ + } + + if (h->nlmsg_type == NLMSG_ERROR) { + rtnl_dump_error(rth, h); + free(buf); + return -1; + } + + if (!rth->dump_fp) { + err = a->filter(h, a->arg1); + if (err < 0) { + free(buf); + return err; + } + } + +skip_it: + h = NLMSG_NEXT(h, msglen); + } + } + free(buf); + + if (found_done) { + if (dump_intr) + fprintf(stderr, + "Dump was interrupted and may be inconsistent.\n"); + return 0; + } + + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (msglen) { + fprintf(stderr, "!!!Remnant of size %d\n", msglen); + exit(1); + } + } +} + +int rtnl_dump_filter_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, __u16 nc_flags) +{ + const struct rtnl_dump_filter_arg a[2] = { + { .filter = filter, .arg1 = arg1, .nc_flags = nc_flags, }, + { .filter = NULL, .arg1 = NULL, .nc_flags = 0, }, + }; + + return rtnl_dump_filter_l(rth, a); +} + +static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err, + nl_ext_ack_fn_t errfn) +{ + if (nl_dump_ext_ack(h, errfn)) + return; + + fprintf(stderr, "RTNETLINK answers: %s\n", + strerror(-err->error)); +} + + +static int __rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iov, + size_t iovlen, struct nlmsghdr **answer, + bool show_rtnl_err, nl_ext_ack_fn_t errfn) +{ + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec riov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = iovlen, + }; + unsigned int seq = 0; + struct nlmsghdr *h; + int i, status; + char *buf; + + for (i = 0; i < iovlen; i++) { + h = iov[i].iov_base; + h->nlmsg_seq = seq = ++rtnl->seq; + if (answer == NULL) + h->nlmsg_flags |= NLM_F_ACK; + } + + status = sendmsg(rtnl->fd, &msg, 0); + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + /* change msg to use the response iov */ + msg.msg_iov = &riov; + msg.msg_iovlen = 1; + i = 0; + while (1) { +next: + status = rtnl_recvmsg(rtnl->fd, &msg, &buf); + ++i; + + if (status < 0) + return status; + + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, + "sender address length == %d\n", + msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len > status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + free(buf); + return -1; + } + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); + exit(1); + } + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq > seq || h->nlmsg_seq < seq - iovlen) { + /* Don't forget to skip that message. */ + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + int error = err->error; + + if (l < sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + free(buf); + return -1; + } + + if (!error) { + /* check messages from kernel */ + nl_dump_ext_ack(h, errfn); + } else { + errno = -error; + + if (rtnl->proto != NETLINK_SOCK_DIAG && + show_rtnl_err) + rtnl_talk_error(h, err, errfn); + } + + if (answer) + *answer = (struct nlmsghdr *)buf; + else + free(buf); + + if (i < iovlen) + goto next; + return error ? -i : 0; + } + + if (answer) { + *answer = (struct nlmsghdr *)buf; + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + } + free(buf); + + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, + struct nlmsghdr **answer, + bool show_rtnl_err, nl_ext_ack_fn_t errfn) +{ + struct iovec iov = { + .iov_base = n, + .iov_len = n->nlmsg_len + }; + + return __rtnl_talk_iov(rtnl, &iov, 1, answer, show_rtnl_err, errfn); +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, + struct nlmsghdr **answer) +{ + return __rtnl_talk(rtnl, n, answer, true, NULL); +} + +int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen, + struct nlmsghdr **answer) +{ + return __rtnl_talk_iov(rtnl, iovec, iovlen, answer, true, NULL); +} + +int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n, + struct nlmsghdr **answer) +{ + return __rtnl_talk(rtnl, n, answer, false, NULL); +} + +int rtnl_listen_all_nsid(struct rtnl_handle *rth) +{ + unsigned int on = 1; + + if (setsockopt(rth->fd, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, + sizeof(on)) < 0) { + perror("NETLINK_LISTEN_ALL_NSID"); + return -1; + } + rth->flags |= RTNL_HANDLE_F_LISTEN_ALL_NSID; + return 0; +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_listen_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + char cmsgbuf[BUFSIZ]; + + if (rtnl->flags & RTNL_HANDLE_F_LISTEN_ALL_NSID) { + msg.msg_control = &cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + iov.iov_base = buf; + while (1) { + struct rtnl_ctrl_data ctrl; + struct cmsghdr *cmsg; + + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + fprintf(stderr, "netlink receive error %s (%d)\n", + strerror(errno), errno); + if (errno == ENOBUFS) + continue; + return -1; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, + "Sender address length == %d\n", + msg.msg_namelen); + exit(1); + } + + if (rtnl->flags & RTNL_HANDLE_F_LISTEN_ALL_NSID) { + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.nsid = -1; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) + if (cmsg->cmsg_level == SOL_NETLINK && + cmsg->cmsg_type == NETLINK_LISTEN_ALL_NSID && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + int *data = (int *)CMSG_DATA(cmsg); + + ctrl.nsid = *data; + } + } + + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len > status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); + exit(1); + } + + err = handler(&ctrl, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler, + void *jarg) +{ + size_t status; + char buf[16384]; + struct nlmsghdr *h = (struct nlmsghdr *)buf; + + while (1) { + int err, len; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status == 0 && feof(rtnl)) + return 0; + if (status != sizeof(*h)) { + if (ferror(rtnl)) + perror("rtnl_from_file: fread"); + if (feof(rtnl)) + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + len = h->nlmsg_len; + l = len - sizeof(*h); + + if (l < 0 || len > sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status != NLMSG_ALIGN(l)) { + if (ferror(rtnl)) + perror("rtnl_from_file: fread"); + if (feof(rtnl)) + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(NULL, h, jarg); + if (err < 0) + return err; + } +} + +int addattr(struct nlmsghdr *n, int maxlen, int type) +{ + return addattr_l(n, maxlen, type, NULL, 0); +} + +int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u8)); +} + +int addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u16)); +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u32)); +} + +int addattr64(struct nlmsghdr *n, int maxlen, int type, __u64 data) +{ + return addattr_l(n, maxlen, type, &data, sizeof(__u64)); +} + +int addattrstrz(struct nlmsghdr *n, int maxlen, int type, const char *str) +{ + return addattr_l(n, maxlen, type, str, strlen(str)+1); +} + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, + "addattr_l ERROR: message exceeded bound of %d\n", + maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return 0; +} + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { + fprintf(stderr, + "addraw_l ERROR: message exceeded bound of %d\n", + maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; + return n->nlmsg_len; +} + +struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, + const void *data, int len) +{ + struct rtattr *start = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, data, len); + addattr_nest(n, maxlen, type); + return start; +} + +int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start) +{ + struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len); + + start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start; + addattr_nest_end(n, nest); + return n->nlmsg_len; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr, + "rta_addattr32: Error! max allowed bound %d exceeded\n", + maxlen); + return -1; + } + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { + fprintf(stderr, + "rta_addattr_l: Error! max allowed bound %d exceeded\n", + maxlen); + return -1; + } + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + if (alen) + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); + return 0; +} + +int rta_addattr8(struct rtattr *rta, int maxlen, int type, __u8 data) +{ + return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u8)); +} + +int rta_addattr16(struct rtattr *rta, int maxlen, int type, __u16 data) +{ + return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u16)); +} + +int rta_addattr64(struct rtattr *rta, int maxlen, int type, __u64 data) +{ + return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u64)); +} + +struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) +{ + struct rtattr *nest = RTA_TAIL(rta); + + rta_addattr_l(rta, maxlen, type, NULL, 0); + nest->rta_type |= NLA_F_NESTED; + + return nest; +} + +int rta_nest_end(struct rtattr *rta, struct rtattr *nest) +{ + nest->rta_len = (void *)RTA_TAIL(rta) - (void *)nest; + + return rta->rta_len; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + return parse_rtattr_flags(tb, max, rta, len, 0); +} + +int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if ((type <= max) && (!tb[type])) + tb[type] = rta; + rta = RTA_NEXT(rta, len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); + return 0; +} + +struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if (rta->rta_type == type) + return rta; + rta = RTA_NEXT(rta, len); + } + + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); + return NULL; +} + +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, + struct rtattr *rta, + int len) +{ + if (RTA_PAYLOAD(rta) < len) + return -1; + if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) { + rta = RTA_DATA(rta) + RTA_ALIGN(len); + return parse_rtattr_nested(tb, max, rta); + } + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + return 0; +}
diff --git a/lib/ll_addr.c b/lib/ll_addr.c new file mode 100644 index 0000000..00b562a --- /dev/null +++ b/lib/ll_addr.c
@@ -0,0 +1,89 @@ +/* + * ll_addr.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + +const char *ll_addr_n2a(const unsigned char *addr, int alen, int type, + char *buf, int blen) +{ + int i; + int l; + + if (alen == 4 && + (type == ARPHRD_TUNNEL || type == ARPHRD_SIT + || type == ARPHRD_IPGRE)) + return inet_ntop(AF_INET, addr, buf, blen); + + if (alen == 16 && (type == ARPHRD_TUNNEL6 || type == ARPHRD_IP6GRE)) + return inet_ntop(AF_INET6, addr, buf, blen); + + snprintf(buf, blen, "%02x", addr[0]); + for (i = 1, l = 2; i < alen && l < blen; i++, l += 3) + snprintf(buf + l, blen - l, ":%02x", addr[i]); + return buf; +} + +/*NB: lladdr is char * (rather than u8 *) because sa_data is char * (1003.1g) */ +int ll_addr_a2n(char *lladdr, int len, const char *arg) +{ + if (strchr(arg, '.')) { + inet_prefix pfx; + if (get_addr_1(&pfx, arg, AF_INET)) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + if (len < 4) + return -1; + memcpy(lladdr, pfx.data, 4); + return 4; + } else { + int i; + + for (i = 0; i < len; i++) { + int temp; + char *cp = strchr(arg, ':'); + if (cp) { + *cp = 0; + cp++; + } + if (sscanf(arg, "%x", &temp) != 1) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", + arg); + return -1; + } + if (temp < 0 || temp > 255) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", + arg); + return -1; + } + lladdr[i] = temp; + if (!cp) + break; + arg = cp; + } + return i + 1; + } +}
diff --git a/lib/ll_map.c b/lib/ll_map.c new file mode 100644 index 0000000..e0ed54b --- /dev/null +++ b/lib/ll_map.c
@@ -0,0 +1,295 @@ +/* + * ll_map.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <net/if.h> + +#include "libnetlink.h" +#include "ll_map.h" +#include "list.h" + +struct ll_cache { + struct hlist_node idx_hash; + struct hlist_node name_hash; + unsigned flags; + unsigned index; + unsigned short type; + char name[]; +}; + +#define IDXMAP_SIZE 1024 +static struct hlist_head idx_head[IDXMAP_SIZE]; +static struct hlist_head name_head[IDXMAP_SIZE]; + +static struct ll_cache *ll_get_by_index(unsigned index) +{ + struct hlist_node *n; + unsigned h = index & (IDXMAP_SIZE - 1); + + hlist_for_each(n, &idx_head[h]) { + struct ll_cache *im + = container_of(n, struct ll_cache, idx_hash); + if (im->index == index) + return im; + } + + return NULL; +} + +unsigned namehash(const char *str) +{ + unsigned hash = 5381; + + while (*str) + hash = ((hash << 5) + hash) + *str++; /* hash * 33 + c */ + + return hash; +} + +static struct ll_cache *ll_get_by_name(const char *name) +{ + struct hlist_node *n; + unsigned h = namehash(name) & (IDXMAP_SIZE - 1); + + hlist_for_each(n, &name_head[h]) { + struct ll_cache *im + = container_of(n, struct ll_cache, name_hash); + + if (strncmp(im->name, name, IFNAMSIZ) == 0) + return im; + } + + return NULL; +} + +int ll_remember_index(struct nlmsghdr *n, void *arg) +{ + unsigned int h; + const char *ifname; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct ll_cache *im; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi))) + return -1; + + im = ll_get_by_index(ifi->ifi_index); + if (n->nlmsg_type == RTM_DELLINK) { + if (im) { + hlist_del(&im->name_hash); + hlist_del(&im->idx_hash); + free(im); + } + return 0; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + ifname = rta_getattr_str(tb[IFLA_IFNAME]); + if (ifname == NULL) + return 0; + + if (im) { + /* change to existing entry */ + if (strcmp(im->name, ifname) != 0) { + hlist_del(&im->name_hash); + h = namehash(ifname) & (IDXMAP_SIZE - 1); + hlist_add_head(&im->name_hash, &name_head[h]); + } + + im->flags = ifi->ifi_flags; + return 0; + } + + im = malloc(sizeof(*im) + strlen(ifname) + 1); + if (im == NULL) + return 0; + im->index = ifi->ifi_index; + strcpy(im->name, ifname); + im->type = ifi->ifi_type; + im->flags = ifi->ifi_flags; + + h = ifi->ifi_index & (IDXMAP_SIZE - 1); + hlist_add_head(&im->idx_hash, &idx_head[h]); + + h = namehash(ifname) & (IDXMAP_SIZE - 1); + hlist_add_head(&im->name_hash, &name_head[h]); + + return 0; +} + +const char *ll_idx_n2a(unsigned int idx) +{ + static char buf[IFNAMSIZ]; + + snprintf(buf, sizeof(buf), "if%u", idx); + return buf; +} + +static unsigned int ll_idx_a2n(const char *name) +{ + unsigned int idx; + + if (sscanf(name, "if%u", &idx) != 1) + return 0; + return idx; +} + +static int ll_link_get(const char *name, int index) +{ + 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 = RTM_GETLINK, + .ifm.ifi_index = index, + }; + __u32 filt_mask = RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS; + struct rtnl_handle rth = {}; + struct nlmsghdr *answer; + int rc = 0; + + if (rtnl_open(&rth, 0) < 0) + return 0; + + addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); + if (name) + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, + strlen(name) + 1); + + if (rtnl_talk_suppress_rtnl_errmsg(&rth, &req.n, &answer) < 0) + goto out; + + /* add entry to cache */ + rc = ll_remember_index(answer, NULL); + if (!rc) { + struct ifinfomsg *ifm = NLMSG_DATA(answer); + + rc = ifm->ifi_index; + } + + free(answer); +out: + rtnl_close(&rth); + return rc; +} + +const char *ll_index_to_name(unsigned int idx) +{ + static char buf[IFNAMSIZ]; + const struct ll_cache *im; + + if (idx == 0) + return "*"; + + im = ll_get_by_index(idx); + if (im) + return im->name; + + if (ll_link_get(NULL, idx) == idx) { + im = ll_get_by_index(idx); + if (im) + return im->name; + } + + if (if_indextoname(idx, buf) == NULL) + snprintf(buf, IFNAMSIZ, "if%u", idx); + + return buf; +} + +int ll_index_to_type(unsigned idx) +{ + const struct ll_cache *im; + + if (idx == 0) + return -1; + + im = ll_get_by_index(idx); + return im ? im->type : -1; +} + +int ll_index_to_flags(unsigned idx) +{ + const struct ll_cache *im; + + if (idx == 0) + return 0; + + im = ll_get_by_index(idx); + return im ? im->flags : -1; +} + +unsigned ll_name_to_index(const char *name) +{ + const struct ll_cache *im; + unsigned idx; + + if (name == NULL) + return 0; + + im = ll_get_by_name(name); + if (im) + return im->index; + + idx = ll_link_get(name, 0); + if (idx == 0) + idx = if_nametoindex(name); + if (idx == 0) + idx = ll_idx_a2n(name); + return idx; +} + +void ll_drop_by_index(unsigned index) +{ + struct ll_cache *im; + + im = ll_get_by_index(index); + if (!im) + return; + + hlist_del(&im->idx_hash); + hlist_del(&im->name_hash); + + free(im); +} + +void ll_init_map(struct rtnl_handle *rth) +{ + static int initialized; + + if (initialized) + return; + + if (rtnl_linkdump_req(rth, AF_UNSPEC) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(rth, ll_remember_index, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + initialized = 1; +}
diff --git a/lib/ll_proto.c b/lib/ll_proto.c new file mode 100644 index 0000000..2a0c1cb --- /dev/null +++ b/lib/ll_proto.c
@@ -0,0 +1,117 @@ +/* + * ll_proto.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "utils.h" +#include "rt_names.h" + + +#define __PF(f,n) { ETH_P_##f, #n }, +static const struct { + int id; + const char *name; +} llproto_names[] = { +__PF(LOOP,loop) +__PF(PUP,pup) +__PF(PUPAT,pupat) +__PF(IP,ip) +__PF(X25,x25) +__PF(ARP,arp) +__PF(BPQ,bpq) +__PF(IEEEPUP,ieeepup) +__PF(IEEEPUPAT,ieeepupat) +__PF(DEC,dec) +__PF(DNA_DL,dna_dl) +__PF(DNA_RC,dna_rc) +__PF(DNA_RT,dna_rt) +__PF(LAT,lat) +__PF(DIAG,diag) +__PF(CUST,cust) +__PF(SCA,sca) +__PF(RARP,rarp) +__PF(ATALK,atalk) +__PF(AARP,aarp) +__PF(IPX,ipx) +__PF(IPV6,ipv6) +__PF(PPP_DISC,ppp_disc) +__PF(PPP_SES,ppp_ses) +__PF(ATMMPOA,atmmpoa) +__PF(ATMFATE,atmfate) +__PF(802_3,802_3) +__PF(AX25,ax25) +__PF(ALL,all) +__PF(802_2,802_2) +__PF(SNAP,snap) +__PF(DDCMP,ddcmp) +__PF(WAN_PPP,wan_ppp) +__PF(PPP_MP,ppp_mp) +__PF(LOCALTALK,localtalk) +__PF(CAN,can) +__PF(PPPTALK,ppptalk) +__PF(TR_802_2,tr_802_2) +__PF(MOBITEX,mobitex) +__PF(CONTROL,control) +__PF(IRDA,irda) +__PF(ECONET,econet) +__PF(TIPC,tipc) +__PF(AOE,aoe) +__PF(8021Q,802.1Q) +__PF(8021AD,802.1ad) +__PF(MPLS_UC,mpls_uc) +__PF(MPLS_MC,mpls_mc) + +{ 0x8100, "802.1Q" }, +{ 0x88cc, "LLDP" }, +{ ETH_P_IP, "ipv4" }, +}; +#undef __PF + + +const char * ll_proto_n2a(unsigned short id, char *buf, int len) +{ + int i; + + id = ntohs(id); + + for (i=0; !numeric && i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (llproto_names[i].id == id) + return llproto_names[i].name; + } + snprintf(buf, len, "[%d]", id); + return buf; +} + +int ll_proto_a2n(unsigned short *id, const char *buf) +{ + int i; + for (i=0; i < sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (strcasecmp(llproto_names[i].name, buf) == 0) { + *id = htons(llproto_names[i].id); + return 0; + } + } + if (get_be16(id, buf, 0)) + return -1; + return 0; +}
diff --git a/lib/ll_types.c b/lib/ll_types.c new file mode 100644 index 0000000..49da15d --- /dev/null +++ b/lib/ll_types.c
@@ -0,0 +1,122 @@ +/* + * ll_types.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + +const char * ll_type_n2a(int type, char *buf, int len) +{ +#define __PF(f,n) { ARPHRD_##f, #n }, +static const struct { + int type; + const char *name; +} arphrd_names[] = { +__PF(NETROM,netrom) +__PF(ETHER,ether) +__PF(EETHER,eether) +__PF(AX25,ax25) +__PF(PRONET,pronet) +__PF(CHAOS,chaos) +__PF(IEEE802,ieee802) +__PF(ARCNET,arcnet) +__PF(APPLETLK,atalk) +__PF(DLCI,dlci) +__PF(ATM,atm) +__PF(METRICOM,metricom) +__PF(IEEE1394,ieee1394) +__PF(INFINIBAND,infiniband) +__PF(SLIP,slip) +__PF(CSLIP,cslip) +__PF(SLIP6,slip6) +__PF(CSLIP6,cslip6) +__PF(RSRVD,rsrvd) +__PF(ADAPT,adapt) +__PF(ROSE,rose) +__PF(X25,x25) +__PF(HWX25,hwx25) +__PF(CAN,can) +__PF(PPP,ppp) +__PF(HDLC,hdlc) +__PF(LAPB,lapb) +__PF(DDCMP,ddcmp) +__PF(RAWHDLC,rawhdlc) +__PF(TUNNEL,ipip) +__PF(TUNNEL6,tunnel6) +__PF(FRAD,frad) +__PF(SKIP,skip) +__PF(LOOPBACK,loopback) +__PF(LOCALTLK,ltalk) +__PF(FDDI,fddi) +__PF(BIF,bif) +__PF(SIT,sit) +__PF(IPDDP,ip/ddp) +__PF(IPGRE,gre) +__PF(PIMREG,pimreg) +__PF(HIPPI,hippi) +__PF(ASH,ash) +__PF(ECONET,econet) +__PF(IRDA,irda) +__PF(FCPP,fcpp) +__PF(FCAL,fcal) +__PF(FCPL,fcpl) +__PF(FCFABRIC,fcfb0) +__PF(FCFABRIC+1,fcfb1) +__PF(FCFABRIC+2,fcfb2) +__PF(FCFABRIC+3,fcfb3) +__PF(FCFABRIC+4,fcfb4) +__PF(FCFABRIC+5,fcfb5) +__PF(FCFABRIC+6,fcfb6) +__PF(FCFABRIC+7,fcfb7) +__PF(FCFABRIC+8,fcfb8) +__PF(FCFABRIC+9,fcfb9) +__PF(FCFABRIC+10,fcfb10) +__PF(FCFABRIC+11,fcfb11) +__PF(FCFABRIC+12,fcfb12) +__PF(IEEE802_TR,tr) +__PF(IEEE80211,ieee802.11) +__PF(IEEE80211_PRISM,ieee802.11/prism) +__PF(IEEE80211_RADIOTAP,ieee802.11/radiotap) +__PF(IEEE802154, ieee802.15.4) +__PF(IEEE802154_MONITOR, ieee802.15.4/monitor) +__PF(PHONET, phonet) +__PF(PHONET_PIPE, phonet_pipe) +__PF(CAIF, caif) +__PF(IP6GRE, gre6) +__PF(NETLINK, netlink) +__PF(6LOWPAN, 6lowpan) + +__PF(NONE, none) +__PF(VOID,void) +}; +#undef __PF + + int i; + for (i=0; !numeric && i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) { + if (arphrd_names[i].type == type) + return arphrd_names[i].name; + } + snprintf(buf, len, "[%d]", type); + return buf; +}
diff --git a/lib/mpls_ntop.c b/lib/mpls_ntop.c new file mode 100644 index 0000000..f8d89f4 --- /dev/null +++ b/lib/mpls_ntop.c
@@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <linux/mpls.h> + +#include "utils.h" + +static const char *mpls_ntop1(const struct mpls_label *addr, char *buf, size_t buflen) +{ + size_t destlen = buflen; + char *dest = buf; + int count = 0; + + while (1) { + uint32_t entry = ntohl(addr[count++].entry); + uint32_t label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + int len = snprintf(dest, destlen, "%u", label); + + if (len >= destlen) + break; + + /* Is this the end? */ + if (entry & MPLS_LS_S_MASK) + return buf; + + dest += len; + destlen -= len; + if (destlen) { + *dest = '/'; + dest++; + destlen--; + } + } + errno = -E2BIG; + return NULL; +} + +const char *mpls_ntop(int af, const void *addr, char *buf, size_t buflen) +{ + switch(af) { + case AF_MPLS: + errno = 0; + return mpls_ntop1((struct mpls_label *)addr, buf, buflen); + default: + errno = EAFNOSUPPORT; + } + + return NULL; +}
diff --git a/lib/mpls_pton.c b/lib/mpls_pton.c new file mode 100644 index 0000000..065374e --- /dev/null +++ b/lib/mpls_pton.c
@@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <linux/mpls.h> + +#include "utils.h" + + +static int mpls_pton1(const char *name, struct mpls_label *addr, + unsigned int maxlabels) +{ + char *endp; + unsigned count; + + for (count = 0; count < maxlabels; count++) { + unsigned long label; + + label = strtoul(name, &endp, 0); + /* Fail when the label value is out or range */ + if (label >= (1 << 20)) + return 0; + + if (endp == name) /* no digits */ + return 0; + + addr->entry = htonl(label << MPLS_LS_LABEL_SHIFT); + if (*endp == '\0') { + addr->entry |= htonl(1 << MPLS_LS_S_SHIFT); + return 1; + } + + /* Bad character in the address */ + if (*endp != '/') + return 0; + + name = endp + 1; + addr += 1; + } + /* The address was too long */ + fprintf(stderr, "Error: too many labels.\n"); + return 0; +} + +int mpls_pton(int af, const char *src, void *addr, size_t alen) +{ + unsigned int maxlabels = alen / sizeof(struct mpls_label); + int err; + + switch(af) { + case AF_MPLS: + errno = 0; + err = mpls_pton1(src, (struct mpls_label *)addr, maxlabels); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +}
diff --git a/lib/names.c b/lib/names.c new file mode 100644 index 0000000..b46ea79 --- /dev/null +++ b/lib/names.c
@@ -0,0 +1,152 @@ +/* + * names.c db names + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "names.h" +#include "utils.h" + +#define MAX_ENTRIES 256 +#define NAME_MAX_LEN 512 + +static int read_id_name(FILE *fp, int *id, char *name) +{ + char buf[NAME_MAX_LEN]; + int min, maj; + + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p == '#' || *p == '\n' || *p == 0) + continue; + + if (sscanf(p, "%x:%x %s\n", &maj, &min, name) == 3) { + *id = (maj << 16) | min; + } else if (sscanf(p, "%x:%x %s #", &maj, &min, name) == 3) { + *id = (maj << 16) | min; + } else if (sscanf(p, "0x%x %s\n", id, name) != 2 && + sscanf(p, "0x%x %s #", id, name) != 2 && + sscanf(p, "%d %s\n", id, name) != 2 && + sscanf(p, "%d %s #", id, name) != 2) { + strcpy(name, p); + return -1; + } + return 1; + } + + return 0; +} + +struct db_names *db_names_alloc(void) +{ + struct db_names *db; + + db = calloc(1, sizeof(*db)); + if (!db) + return NULL; + + db->size = MAX_ENTRIES; + db->hash = calloc(db->size, sizeof(struct db_entry *)); + + return db; +} + +int db_names_load(struct db_names *db, const char *path) +{ + struct db_entry *entry; + FILE *fp; + int id; + char namebuf[NAME_MAX_LEN] = {0}; + int ret = -1; + + fp = fopen(path, "r"); + if (!fp) + return -ENOENT; + + while ((ret = read_id_name(fp, &id, &namebuf[0]))) { + if (ret == -1) { + fprintf(stderr, "Database %s is corrupted at %s\n", + path, namebuf); + goto Exit; + } + ret = -1; + + if (id < 0) + continue; + + entry = malloc(sizeof(*entry)); + if (!entry) + goto Exit; + + entry->name = strdup(namebuf); + if (!entry->name) { + free(entry); + goto Exit; + } + + entry->id = id; + entry->next = db->hash[id & (db->size - 1)]; + db->hash[id & (db->size - 1)] = entry; + } + ret = 0; + +Exit: + fclose(fp); + return ret; +} + +void db_names_free(struct db_names *db) +{ + int i; + + if (!db) + return; + + for (i = 0; i < db->size; i++) { + struct db_entry *entry = db->hash[i]; + + while (entry) { + struct db_entry *next = entry->next; + + free(entry->name); + free(entry); + entry = next; + } + } + + free(db->hash); + free(db); +} + +char *id_to_name(struct db_names *db, int id, char *name) +{ + struct db_entry *entry; + + if (!db) + return NULL; + + entry = db->hash[id & (db->size - 1)]; + while (entry && entry->id != id) + entry = entry->next; + + if (entry) { + strncpy(name, entry->name, IDNAME_MAX); + return name; + } + + snprintf(name, IDNAME_MAX, "%d", id); + return NULL; +}
diff --git a/lib/namespace.c b/lib/namespace.c new file mode 100644 index 0000000..06ae0a4 --- /dev/null +++ b/lib/namespace.c
@@ -0,0 +1,139 @@ +/* + * namespace.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <sys/statvfs.h> +#include <fcntl.h> +#include <dirent.h> +#include <limits.h> + +#include "utils.h" +#include "namespace.h" + +static void bind_etc(const char *name) +{ + char etc_netns_path[sizeof(NETNS_ETC_DIR) + NAME_MAX]; + char netns_name[PATH_MAX]; + char etc_name[PATH_MAX]; + struct dirent *entry; + DIR *dir; + + if (strlen(name) >= NAME_MAX) + return; + + snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name); + dir = opendir(etc_netns_path); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name); + snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name); + if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) { + fprintf(stderr, "Bind %s -> %s failed: %s\n", + netns_name, etc_name, strerror(errno)); + } + } + closedir(dir); +} + +int netns_switch(char *name) +{ + char net_path[PATH_MAX]; + int netns; + unsigned long mountflags = 0; + struct statvfs fsstat; + + snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); + netns = open(net_path, O_RDONLY | O_CLOEXEC); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace \"%s\": %s\n", + name, strerror(errno)); + return -1; + } + + if (setns(netns, CLONE_NEWNET) < 0) { + fprintf(stderr, "setting the network namespace \"%s\" failed: %s\n", + name, strerror(errno)); + close(netns); + return -1; + } + close(netns); + + if (unshare(CLONE_NEWNS) < 0) { + fprintf(stderr, "unshare failed: %s\n", strerror(errno)); + return -1; + } + /* Don't let any mounts propagate back to the parent */ + if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL)) { + fprintf(stderr, "\"mount --make-rslave /\" failed: %s\n", + strerror(errno)); + return -1; + } + + /* Mount a version of /sys that describes the network namespace */ + + if (umount2("/sys", MNT_DETACH) < 0) { + /* If this fails, perhaps there wasn't a sysfs instance mounted. Good. */ + if (statvfs("/sys", &fsstat) == 0) { + /* We couldn't umount the sysfs, we'll attempt to overlay it. + * A read-only instance can't be shadowed with a read-write one. */ + if (fsstat.f_flag & ST_RDONLY) + mountflags = MS_RDONLY; + } + } + if (mount(name, "/sys", "sysfs", mountflags, NULL) < 0) { + fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno)); + return -1; + } + + /* Setup bind mounts for config files in /etc */ + bind_etc(name); + return 0; +} + +int netns_get_fd(const char *name) +{ + char pathbuf[PATH_MAX]; + const char *path, *ptr; + + path = name; + ptr = strchr(name, '/'); + if (!ptr) { + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + NETNS_RUN_DIR, name ); + path = pathbuf; + } + return open(path, O_RDONLY); +} + +int netns_foreach(int (*func)(char *nsname, void *arg), void *arg) +{ + DIR *dir; + struct dirent *entry; + + dir = opendir(NETNS_RUN_DIR); + if (!dir) + return -1; + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0) + continue; + if (strcmp(entry->d_name, "..") == 0) + continue; + if (func(entry->d_name, arg)) + break; + } + + closedir(dir); + return 0; +}
diff --git a/lib/rt_names.c b/lib/rt_names.c new file mode 100644 index 0000000..41cccfb --- /dev/null +++ b/lib/rt_names.c
@@ -0,0 +1,683 @@ +/* + * rt_names.c rtnetlink names DB. + * + * 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, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <dirent.h> +#include <limits.h> + +#include <asm/types.h> +#include <linux/rtnetlink.h> + +#include "rt_names.h" +#include "utils.h" + +#define NAME_MAX_LEN 512 + +int numeric; + +struct rtnl_hash_entry { + struct rtnl_hash_entry *next; + const char *name; + unsigned int id; +}; + +static int fread_id_name(FILE *fp, int *id, char *namebuf) +{ + char buf[NAME_MAX_LEN]; + + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p == '#' || *p == '\n' || *p == 0) + continue; + + if (sscanf(p, "0x%x %s\n", id, namebuf) != 2 && + sscanf(p, "0x%x %s #", id, namebuf) != 2 && + sscanf(p, "%d %s\n", id, namebuf) != 2 && + sscanf(p, "%d %s #", id, namebuf) != 2) { + strcpy(namebuf, p); + return -1; + } + return 1; + } + return 0; +} + +static void +rtnl_hash_initialize(const char *file, struct rtnl_hash_entry **hash, int size) +{ + struct rtnl_hash_entry *entry; + FILE *fp; + int id; + char namebuf[NAME_MAX_LEN] = {0}; + int ret; + + fp = fopen(file, "r"); + if (!fp) + return; + + while ((ret = fread_id_name(fp, &id, &namebuf[0]))) { + if (ret == -1) { + fprintf(stderr, "Database %s is corrupted at %s\n", + file, namebuf); + fclose(fp); + return; + } + + if (id < 0) + continue; + + entry = malloc(sizeof(*entry)); + entry->id = id; + entry->name = strdup(namebuf); + entry->next = hash[id & (size - 1)]; + hash[id & (size - 1)] = entry; + } + fclose(fp); +} + +static void rtnl_tab_initialize(const char *file, char **tab, int size) +{ + FILE *fp; + int id; + char namebuf[NAME_MAX_LEN] = {0}; + int ret; + + fp = fopen(file, "r"); + if (!fp) + return; + + while ((ret = fread_id_name(fp, &id, &namebuf[0]))) { + if (ret == -1) { + fprintf(stderr, "Database %s is corrupted at %s\n", + file, namebuf); + fclose(fp); + return; + } + if (id < 0 || id > size) + continue; + + tab[id] = strdup(namebuf); + } + fclose(fp); +} + +static char *rtnl_rtprot_tab[256] = { + [RTPROT_UNSPEC] = "unspec", + [RTPROT_REDIRECT] = "redirect", + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", + + [RTPROT_GATED] = "gated", + [RTPROT_RA] = "ra", + [RTPROT_MRT] = "mrt", + [RTPROT_ZEBRA] = "zebra", + [RTPROT_BIRD] = "bird", + [RTPROT_BABEL] = "babel", + [RTPROT_DNROUTED] = "dnrouted", + [RTPROT_XORP] = "xorp", + [RTPROT_NTK] = "ntk", + [RTPROT_DHCP] = "dhcp", + [RTPROT_BGP] = "bgp", + [RTPROT_ISIS] = "isis", + [RTPROT_OSPF] = "ospf", + [RTPROT_RIP] = "rip", + [RTPROT_EIGRP] = "eigrp", +}; + + +static int rtnl_rtprot_init; + +static void rtnl_rtprot_initialize(void) +{ + struct dirent *de; + DIR *d; + + rtnl_rtprot_init = 1; + rtnl_tab_initialize(CONFDIR "/rt_protos", + rtnl_rtprot_tab, 256); + + d = opendir(CONFDIR "/rt_protos.d"); + if (!d) + return; + + while ((de = readdir(d)) != NULL) { + char path[PATH_MAX]; + size_t len; + + if (*de->d_name == '.') + continue; + + /* only consider filenames ending in '.conf' */ + len = strlen(de->d_name); + if (len <= 5) + continue; + if (strcmp(de->d_name + len - 5, ".conf")) + continue; + + snprintf(path, sizeof(path), CONFDIR "/rt_protos.d/%s", + de->d_name); + rtnl_tab_initialize(path, rtnl_rtprot_tab, 256); + } + closedir(d); +} + +const char *rtnl_rtprot_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256 || numeric) { + snprintf(buf, len, "%u", id); + return buf; + } + if (!rtnl_rtprot_tab[id]) { + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + } + if (rtnl_rtprot_tab[id]) + return rtnl_rtprot_tab[id]; + snprintf(buf, len, "%u", id); + return buf; +} + +int rtnl_rtprot_a2n(__u32 *id, const char *arg) +{ + static char *cache; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + + for (i = 0; i < 256; i++) { + if (rtnl_rtprot_tab[i] && + strcmp(rtnl_rtprot_tab[i], arg) == 0) { + cache = rtnl_rtprot_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + +static char *rtnl_rtscope_tab[256] = { + [RT_SCOPE_UNIVERSE] = "global", + [RT_SCOPE_NOWHERE] = "nowhere", + [RT_SCOPE_HOST] = "host", + [RT_SCOPE_LINK] = "link", + [RT_SCOPE_SITE] = "site", +}; + +static int rtnl_rtscope_init; + +static void rtnl_rtscope_initialize(void) +{ + rtnl_rtscope_init = 1; + rtnl_tab_initialize(CONFDIR "/rt_scopes", + rtnl_rtscope_tab, 256); +} + +const char *rtnl_rtscope_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256 || numeric) { + snprintf(buf, len, "%d", id); + return buf; + } + + if (!rtnl_rtscope_tab[id]) { + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + } + + if (rtnl_rtscope_tab[id]) + return rtnl_rtscope_tab[id]; + + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtscope_a2n(__u32 *id, const char *arg) +{ + static const char *cache; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + + for (i = 0; i < 256; i++) { + if (rtnl_rtscope_tab[i] && + strcmp(rtnl_rtscope_tab[i], arg) == 0) { + cache = rtnl_rtscope_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + +static char *rtnl_rtrealm_tab[256] = { + "unknown", +}; + +static int rtnl_rtrealm_init; + +static void rtnl_rtrealm_initialize(void) +{ + rtnl_rtrealm_init = 1; + rtnl_tab_initialize(CONFDIR "/rt_realms", + rtnl_rtrealm_tab, 256); +} + +const char *rtnl_rtrealm_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256 || numeric) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtrealm_tab[id]) { + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + } + if (rtnl_rtrealm_tab[id]) + return rtnl_rtrealm_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + + +int rtnl_rtrealm_a2n(__u32 *id, const char *arg) +{ + static char *cache; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + + for (i = 0; i < 256; i++) { + if (rtnl_rtrealm_tab[i] && + strcmp(rtnl_rtrealm_tab[i], arg) == 0) { + cache = rtnl_rtrealm_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + +static struct rtnl_hash_entry dflt_table_entry = { .name = "default" }; +static struct rtnl_hash_entry main_table_entry = { .name = "main" }; +static struct rtnl_hash_entry local_table_entry = { .name = "local" }; + +static struct rtnl_hash_entry *rtnl_rttable_hash[256] = { + [RT_TABLE_DEFAULT] = &dflt_table_entry, + [RT_TABLE_MAIN] = &main_table_entry, + [RT_TABLE_LOCAL] = &local_table_entry, +}; + +static int rtnl_rttable_init; + +static void rtnl_rttable_initialize(void) +{ + struct dirent *de; + DIR *d; + int i; + + rtnl_rttable_init = 1; + for (i = 0; i < 256; i++) { + if (rtnl_rttable_hash[i]) + rtnl_rttable_hash[i]->id = i; + } + rtnl_hash_initialize(CONFDIR "/rt_tables", + rtnl_rttable_hash, 256); + + d = opendir(CONFDIR "/rt_tables.d"); + if (!d) + return; + + while ((de = readdir(d)) != NULL) { + char path[PATH_MAX]; + size_t len; + + if (*de->d_name == '.') + continue; + + /* only consider filenames ending in '.conf' */ + len = strlen(de->d_name); + if (len <= 5) + continue; + if (strcmp(de->d_name + len - 5, ".conf")) + continue; + + snprintf(path, sizeof(path), + CONFDIR "/rt_tables.d/%s", de->d_name); + rtnl_hash_initialize(path, rtnl_rttable_hash, 256); + } + closedir(d); +} + +const char *rtnl_rttable_n2a(__u32 id, char *buf, int len) +{ + struct rtnl_hash_entry *entry; + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + entry = rtnl_rttable_hash[id & 255]; + while (entry && entry->id != id) + entry = entry->next; + if (!numeric && entry) + return entry->name; + snprintf(buf, len, "%u", id); + return buf; +} + +int rtnl_rttable_a2n(__u32 *id, const char *arg) +{ + static const char *cache; + static unsigned long res; + struct rtnl_hash_entry *entry; + char *end; + unsigned long i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i = 0; i < 256; i++) { + entry = rtnl_rttable_hash[i]; + while (entry && strcmp(entry->name, arg)) + entry = entry->next; + if (entry) { + cache = entry->name; + res = entry->id; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > RT_TABLE_MAX) + return -1; + *id = i; + return 0; +} + + +static char *rtnl_rtdsfield_tab[256] = { + "0", +}; + +static int rtnl_rtdsfield_init; + +static void rtnl_rtdsfield_initialize(void) +{ + rtnl_rtdsfield_init = 1; + rtnl_tab_initialize(CONFDIR "/rt_dsfield", + rtnl_rtdsfield_tab, 256); +} + +const char *rtnl_dsfield_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtdsfield_tab[id]) { + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + } + if (!numeric && rtnl_rtdsfield_tab[id]) + return rtnl_rtdsfield_tab[id]; + snprintf(buf, len, "0x%02x", id); + return buf; +} + + +int rtnl_dsfield_a2n(__u32 *id, const char *arg) +{ + static char *cache; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + + for (i = 0; i < 256; i++) { + if (rtnl_rtdsfield_tab[i] && + strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { + cache = rtnl_rtdsfield_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 16); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + +static struct rtnl_hash_entry dflt_group_entry = { + .id = 0, .name = "default" +}; + +static struct rtnl_hash_entry *rtnl_group_hash[256] = { + [0] = &dflt_group_entry, +}; + +static int rtnl_group_init; + +static void rtnl_group_initialize(void) +{ + rtnl_group_init = 1; + rtnl_hash_initialize(CONFDIR "/group", + rtnl_group_hash, 256); +} + +int rtnl_group_a2n(int *id, const char *arg) +{ + static const char *cache; + static unsigned long res; + struct rtnl_hash_entry *entry; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_group_init) + rtnl_group_initialize(); + + for (i = 0; i < 256; i++) { + entry = rtnl_group_hash[i]; + while (entry && strcmp(entry->name, arg)) + entry = entry->next; + if (entry) { + cache = entry->name; + res = entry->id; + *id = res; + return 0; + } + } + + i = strtol(arg, &end, 0); + if (!end || end == arg || *end || i < 0) + return -1; + *id = i; + return 0; +} + +const char *rtnl_group_n2a(int id, char *buf, int len) +{ + struct rtnl_hash_entry *entry; + int i; + + if (!rtnl_group_init) + rtnl_group_initialize(); + + for (i = 0; !numeric && i < 256; i++) { + entry = rtnl_group_hash[i]; + + while (entry) { + if (entry->id == id) + return entry->name; + entry = entry->next; + } + } + + snprintf(buf, len, "%d", id); + return buf; +} + +static char *nl_proto_tab[256] = { + [NETLINK_ROUTE] = "rtnl", + [NETLINK_UNUSED] = "unused", + [NETLINK_USERSOCK] = "usersock", + [NETLINK_FIREWALL] = "fw", + [NETLINK_SOCK_DIAG] = "tcpdiag", + [NETLINK_NFLOG] = "nflog", + [NETLINK_XFRM] = "xfrm", + [NETLINK_SELINUX] = "selinux", + [NETLINK_ISCSI] = "iscsi", + [NETLINK_AUDIT] = "audit", + [NETLINK_FIB_LOOKUP] = "fiblookup", + [NETLINK_CONNECTOR] = "connector", + [NETLINK_NETFILTER] = "nft", + [NETLINK_IP6_FW] = "ip6fw", + [NETLINK_DNRTMSG] = "dec-rt", + [NETLINK_KOBJECT_UEVENT] = "uevent", + [NETLINK_GENERIC] = "genl", + [NETLINK_SCSITRANSPORT] = "scsi-trans", + [NETLINK_ECRYPTFS] = "ecryptfs", + [NETLINK_RDMA] = "rdma", + [NETLINK_CRYPTO] = "crypto", +}; + +static int nl_proto_init; + +static void nl_proto_initialize(void) +{ + nl_proto_init = 1; + rtnl_tab_initialize(CONFDIR "/nl_protos", + nl_proto_tab, 256); +} + +const char *nl_proto_n2a(int id, char *buf, int len) +{ + if (id < 0 || id >= 256 || numeric) { + snprintf(buf, len, "%d", id); + return buf; + } + + if (!nl_proto_init) + nl_proto_initialize(); + + if (nl_proto_tab[id]) + return nl_proto_tab[id]; + + snprintf(buf, len, "%u", id); + return buf; +} + +int nl_proto_a2n(__u32 *id, const char *arg) +{ + static char *cache; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!nl_proto_init) + nl_proto_initialize(); + + for (i = 0; i < 256; i++) { + if (nl_proto_tab[i] && + strcmp(nl_proto_tab[i], arg) == 0) { + cache = nl_proto_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +}
diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..95d46ff --- /dev/null +++ b/lib/utils.c
@@ -0,0 +1,1688 @@ +/* + * utils.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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <ctype.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <asm/types.h> +#include <linux/pkt_sched.h> +#include <linux/param.h> +#include <linux/if_arp.h> +#include <linux/mpls.h> +#include <linux/snmp.h> +#include <time.h> +#include <sys/time.h> +#include <errno.h> +#ifdef HAVE_LIBCAP +#include <sys/capability.h> +#endif + +#include "rt_names.h" +#include "utils.h" +#include "ll_map.h" +#include "namespace.h" + +int resolve_hosts; +int timestamp_short; +int pretty; +const char *_SL_ = "\n"; + +static int af_byte_len(int af); +static void print_time(char *buf, int len, __u32 time); +static void print_time64(char *buf, int len, __s64 time); + +int read_prop(const char *dev, char *prop, long *value) +{ + char fname[128], buf[80], *endp, *nl; + FILE *fp; + long result; + int ret; + + ret = snprintf(fname, sizeof(fname), "/sys/class/net/%s/%s", + dev, prop); + + if (ret <= 0 || ret >= sizeof(fname)) { + fprintf(stderr, "could not build pathname for property\n"); + return -1; + } + + fp = fopen(fname, "r"); + if (fp == NULL) { + fprintf(stderr, "fopen %s: %s\n", fname, strerror(errno)); + return -1; + } + + if (!fgets(buf, sizeof(buf), fp)) { + fprintf(stderr, "property \"%s\" in file %s is currently unknown\n", prop, fname); + fclose(fp); + goto out; + } + + nl = strchr(buf, '\n'); + if (nl) + *nl = '\0'; + + fclose(fp); + result = strtol(buf, &endp, 0); + + if (*endp || buf == endp) { + fprintf(stderr, "value \"%s\" in file %s is not a number\n", + buf, fname); + goto out; + } + + if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) { + fprintf(stderr, "strtol %s: %s", fname, strerror(errno)); + goto out; + } + + *value = result; + return 0; +out: + fprintf(stderr, "Failed to parse %s\n", fname); + return -1; +} + +int get_hex(char c) +{ + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + + return -1; +} + +int get_integer(int *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtol(arg, &ptr, base); + + /* If there were no digits at all, strtol() stores + * the original value of nptr in *endptr (and returns 0). + * In particular, if *nptr is not '\0' but **endptr is '\0' on return, + * the entire string is valid. + */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* If an underflow occurs, strtol() returns LONG_MIN. + * If an overflow occurs, strtol() returns LONG_MAX. + * In both cases, errno is set to ERANGE. + */ + if ((res == LONG_MAX || res == LONG_MIN) && errno == ERANGE) + return -1; + + /* Outside range of int */ + if (res < INT_MIN || res > INT_MAX) + return -1; + + *val = res; + return 0; +} + +int mask2bits(__u32 netmask) +{ + unsigned int bits = 0; + __u32 mask = ntohl(netmask); + __u32 host = ~mask; + + /* a valid netmask must be 2^n - 1 */ + if ((host & (host + 1)) != 0) + return -1; + + for (; mask; mask <<= 1) + ++bits; + return bits; +} + +static int get_netmask(unsigned int *val, const char *arg, int base) +{ + inet_prefix addr; + + if (!get_unsigned(val, arg, base)) + return 0; + + /* try converting dotted quad to CIDR */ + if (!get_addr_1(&addr, arg, AF_INET) && addr.family == AF_INET) { + int b = mask2bits(addr.data[0]); + + if (b >= 0) { + *val = b; + return 0; + } + } + + return -1; +} + +int get_unsigned(unsigned int *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtoul(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + /* out side range of unsigned */ + if (res > UINT_MAX) + return -1; + + *val = res; + return 0; +} + +/* + * get_time_rtt is "translated" from a similar routine "get_time" in + * tc_util.c. We don't use the exact same routine because tc passes + * microseconds to the kernel and the callers of get_time_rtt want to + * pass milliseconds (standard unit for rtt values since 2.6.27), and + * have a different assumption for the units of a "raw" number. + */ +int get_time_rtt(unsigned int *val, const char *arg, int *raw) +{ + double t; + unsigned long res; + char *p; + + if (strchr(arg, '.') != NULL) { + t = strtod(arg, &p); + if (t < 0.0) + return -1; + + /* no digits? */ + if (!p || p == arg) + return -1; + + /* over/underflow */ + if ((t == HUGE_VALF || t == HUGE_VALL) && errno == ERANGE) + return -1; + } else { + res = strtoul(arg, &p, 0); + + /* empty string? */ + if (!p || p == arg) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + t = (double)res; + } + + if (p == arg) + return -1; + *raw = 1; + + if (*p) { + *raw = 0; + if (strcasecmp(p, "s") == 0 || + strcasecmp(p, "sec") == 0 || + strcasecmp(p, "secs") == 0) + t *= 1000; + else if (strcasecmp(p, "ms") == 0 || + strcasecmp(p, "msec") == 0 || + strcasecmp(p, "msecs") == 0) + t *= 1.0; /* allow suffix, do nothing */ + else + return -1; + } + + /* emulate ceil() without having to bring-in -lm and always be >= 1 */ + *val = t; + if (*val < t) + *val += 1; + + return 0; + +} + +int get_u64(__u64 *val, const char *arg, int base) +{ + unsigned long long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtoull(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULLONG_MAX && errno == ERANGE) + return -1; + + /* in case ULL is 128 bits */ + if (res > 0xFFFFFFFFFFFFFFFFULL) + return -1; + + *val = res; + return 0; +} + +int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + /* in case UL > 32 bits */ + if (res > 0xFFFFFFFFUL) + return -1; + + *val = res; + return 0; +} + +int get_u16(__u16 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + if (res > 0xFFFFUL) + return -1; + + *val = res; + return 0; +} + +int get_u8(__u8 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + + res = strtoul(arg, &ptr, base); + /* empty string or trailing non-digits */ + if (!ptr || ptr == arg || *ptr) + return -1; + + /* overflow */ + if (res == ULONG_MAX && errno == ERANGE) + return -1; + + if (res > 0xFFUL) + return -1; + + *val = res; + return 0; +} + +int get_s64(__s64 *val, const char *arg, int base) +{ + long long res; + char *ptr; + + errno = 0; + + if (!arg || !*arg) + return -1; + res = strtoll(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr) + return -1; + if ((res == LLONG_MIN || res == LLONG_MAX) && errno == ERANGE) + return -1; + if (res > INT64_MAX || res < INT64_MIN) + return -1; + + *val = res; + return 0; +} + +int get_s32(__s32 *val, const char *arg, int base) +{ + long res; + char *ptr; + + errno = 0; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr) + return -1; + if ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE) + return -1; + if (res > INT32_MAX || res < INT32_MIN) + return -1; + + *val = res; + return 0; +} + +int get_be64(__be64 *val, const char *arg, int base) +{ + __u64 v; + int ret = get_u64(&v, arg, base); + + if (!ret) + *val = htonll(v); + + return ret; +} + +int get_be32(__be32 *val, const char *arg, int base) +{ + __u32 v; + int ret = get_u32(&v, arg, base); + + if (!ret) + *val = htonl(v); + + return ret; +} + +int get_be16(__be16 *val, const char *arg, int base) +{ + __u16 v; + int ret = get_u16(&v, arg, base); + + if (!ret) + *val = htons(v); + + return ret; +} + +/* This uses a non-standard parsing (ie not inet_aton, or inet_pton) + * because of legacy choice to parse 10.8 as 10.8.0.0 not 10.0.0.8 + */ +static int get_addr_ipv4(__u8 *ap, const char *cp) +{ + int i; + + for (i = 0; i < 4; i++) { + unsigned long n; + char *endp; + + n = strtoul(cp, &endp, 0); + if (n > 255) + return -1; /* bogus network value */ + + if (endp == cp) /* no digits */ + return -1; + + ap[i] = n; + + if (*endp == '\0') + break; + + if (i == 3 || *endp != '.') + return -1; /* extra characters */ + cp = endp + 1; + } + + return 1;